How to set up WhatAapp cloud webhook on Laravel PHP framework - php

I have been trying to set up WhatsApp cloud API on my Laravel MVC project. I'm stuck trying to set up webhook to receive WhatsApp notifications when someone sends a message. The below is my code and it is not simply working giving server error or 405 method not allowed error, in the WhatsApp cloud api side it does not pass the validation point.
API
Route::GET('/webhook' , 'admin\InventoryInvoiceController#webhook')->name('webhook');
Controller
public function webhook() {
if($_SERVER['REQUEST_METHOD']=="GET"){
echo $_GET['hub_challenge']; //respond back hub_callenge key
http_response_code(200);
}else{
$data = json_decode(file_get_contents('php://input'), true);
error_log(json_encode($data)); //print inbound message
}
}
I have added this route to the exception so it's run with the need for CSRF validation. Error received on the Whatsapp API Cloud side:
The callback URL or verify token couldn't be validated. Please verify
the provided information or try again later.
What can I try next?

The problem was that I had not added my route to the CSRF exception which would invalidate the request send by Whatsapp cloud api. Make sure to do that before testing! Below is the code:
public function webhook(Request $request) {
$mode = $request->hub_mode;
$challenge = $request->hub_challenge;
$token = $request->hub_verify_token;
echo $challenge;
}
public function __construct() {
$this->middleware('auth:admin',
['except' => ['webhook', 'webhookpost']]
);

Related

Laravel handle Telegram webhooks

I'm fairly new to Laravel, and recently I tried to create a Telegram bot that can manage incoming messages and reply accordingly. Initially I plan to ask a user's name and use his name the next time the bot replies.
So how can I manage my bot's webhook which I already managed to set up.
Route::any('/setWebhook', function () {
$response = Telegram::setWebhook([
'url' => 'https://57f7-2806-104e-c-5c3b-3dc7-3284-7383-e130.ngrok.io/NdqvlJstHyIUhmNyTZhCYTnoYxdGzoPcLCzDiMiH/webhook'
]);
dd($response);
});
How can I manage the bot's incoming updates? I'm currently using irazasyed sdk.
You need to use a DB, which requires Model & migration
Telegram Bot API will send POST requests to your webhook
You should add ->middleware('api'), because you'll receive data
Use file_get_contents('php://input') to get the update, Or use a library.
To me, I use SimpleBotAPI library
Simple code in SimpleBotAPI:
// Handle updates here
class BotHandler extends UpdatesHandler
{
public function MessageHandler($message) : bool
{
// Do whatever:
$this->Bot->SendMessage(['chat_id' => $message->chat->id, 'text' => 'new Message!']);
return true;
}
}
Route::post('/bot_webhook', function () {
$Bot = new TelegramBot(env('BOT_TOKEN'), new BotHandler());
$Bot->OnWebhookUpdate();
})->middleware('api');

How to send parameter with API in get method

I have to run an API on postmen, the project is based upon Laravel. The endpoint is mention below
`Route::get('order/detail/{order}', 'OrdersController#show');`
here is my order controller function:
public function show(Order $order)
{
dd("ok");
}
I am trying to run API using postmen and receiving 404 error
but when I removed {order} from endpoints, it worked. so my question is how can I run this API on postmen. Any help would be highly appreciable.
With Route::get('order/detail/{order}', 'OrdersController#show');
You must use the endpoint is http://localhost:8082/v3/order/detail/45487.
Not ...order/detail?order=45487
then
public function show($order) //$order is 45487
{
dd("ok");
}

Migrate an existing project to Firebase : Authentication handling & Preserve existing JWT

I've an existing project that is built on :
AngularJS
Google App Engine backend, PHP, with an authentication based on JWT
I'm in process of rewriting the frontend to move to Angular 8 and I want to leverage the firebase features.
I'm currently working on integrating the authentication feature (username/password, google, twitter, facebook etc...)
And I'm thinking about my next step :
Once my use is authenticated with firebase, how can my GAE PHP backend check that the user is authenticated ?
In my JWT, I've set some basic user information, that are essential to my backend function.
uid, first name, last name, entityId, entityName, roleId, environmentId
I was imagining something like :
once authenticated with firebase, call my GAE Backend with the OAuth2 token
call some magic function that will validate the OAuth2 token and associate the firebase user, with my internal user table
reply with a JWT
Include the JWT and the OAuth2 token in every call
Would this work ? any suggestions ?
So here is how I did it :
On the client side (Angular Application), I use ngx-auth-firebaseui, to display the login form.
On the form, I set the call back that handle an authentication success:
login.component.html
<ngx-auth-firebaseui (onSuccess)="successfulLogin($event)"
(onError)="printError($event)">
</ngx-auth-firebaseui>
The code of the callback is here.
From the Firebase User object, I call the method getIdTokenResult() to get the firebase JWT.
And I then call my php backend via the authenticationService
login.component.ts
successfulLogin(user:User) {
console.log(user);
user.getIdTokenResult().then((idTokenResult:IdTokenResult)=> {
console.log(idTokenResult.token);
let token : string = idTokenResult.token;
let rcqJWTToken = this.authenticationService.authenticate( { token } as FirebaseJWT);
rcqJWTToken.subscribe((rcqToken:string)=> console.log("RCQ JWT Token : '"+rcqToken+"'"));
this.router.navigate['/welcome'];
});
}
Here I transmit the Firebase JWT to my php backend
authentication.service.ts
authenticate(firebaseJWTToken:FirebaseJWT):Observable<String>{
return this.http.post<String>(this.authenticationURL, firebaseJWTToken, httpOptions)
.pipe(
tap(_ => console.log('fetched RCQ JWT')),
catchError(this.handleError<String>('authenticate', ""))
);
}
On the server side :
I set the GOOGLE_APPLICATION_CREDENTIALS as an env var, like it is when deployed on Google App Engine
putenv("GOOGLE_APPLICATION_CREDENTIALS=/Users/myuser/.cred/GCP-project-ID.json");
I use Slimframework, so I instanciate the Firebase object in my dependencies.php file.
With the env var, Firebase do not need anything else.
check here : https://firebase-php.readthedocs.io/en/4.32.0/setup.html
use Kreait\Firebase;
use Kreait\Firebase\Factory;
/**
* Used to authenticate a firebase user, from it's Firebase JWT
* #property Firebase $firebase
* #param \Slim\Container $c
* #return Firebase
*/
$container['firebase'] = function (\Slim\Container $c)
{
$firebase = (new Factory)->create();
return $firebase;
};
and here comes the route where the authentication is done :
$app->post(getPrefix().'/firebase-authenticate', function($request, $response, $args) use ($app)
{
$token = $this->clientInputValidator->validateString("token" , $request->getParsedBodyParam("token" ), 1500 , true );
$username = "";
Logger::dataForLogging(new LoggingEntity(null, ["username"=>$username]));
try
{
$verifiedIdToken = $this->firebase->getAuth()->verifyIdToken($token);
}
catch (InvalidToken $e)
{
$response401 = $response->withStatus(401);
$response401->getBody()->write(json_encode(["error" =>"Authentication error"]));
$this->logger->error("Firebase authentication error", array('username' => $username, 'token' => $token));
return $response401;
}
$uid = $verifiedIdToken->getClaim('sub');
$user = $this->firebase->getAuth()->getUser($uid);
$this->logger->debug("Firebase JWT checked successfully", array('uid' => $uid,'user' => $user));
});
The main thing is here :
$verifiedIdToken = $this->firebase->getAuth()->verifyIdToken($token);
And the user details are retrieved here:
$user = $this->firebase->getAuth()->getUser($uid);
I can get the uid, email, and all the info in the Firebase JWT.
the token itself has a TTL of 1 hour, so I'll probably have to refresh the token and revalidate it against my backend.

Notification endpoint validation not working with Laravel Endpoint

I am using the Microsoft Graph and I need to set up a webhook to receive changes to email and calendar events. I was able to get it working with my PHP Laravel application, but now that I am trying to subscribe to notifications, I am running into issues with validating the notificationUrl, which is pointing to a public server of mine.
The script for creating the webhook is returning the following error:
Client error: POST https://graph.microsoft.com/v1.0/subscriptions resulted in a 400 Bad Request response:
{
"error": {
"code": "InvalidRequest",
"message": "Subscription validation request failed. Response must ex (truncated...)
The truncated part I believe is
Subscription validation request failed. Must respond with 200 OK to this request.
Here is my code for creating the subscription:
$data = [
"changeType" => "created",
"notificationUrl" => "https://anatbanielmethod.successengine.net/office365/webhooks/events",
"resource" => "me/events",
"expirationDateTime" => "2018-12-20T18:23:45.9356913Z",
"clientState" => "secret",
];
$result = $graph->createRequest('POST', '/subscriptions')
->attachBody($data)
->execute();
and here is my method for my notificationUrl:
public function events()
{
//if validationToken exists return that to validate notificationUrl
if(isset($_REQUEST['validationToken'])){
return response($_REQUEST['validationToken'], 200)
->header('Content-Type', 'text/plain');
}
//process event normally for those that have already been validated
}
Once again this URL is public and live and I have tested it by using Postman to send it test posts and it is working fine. Also, I added this route to my VerifyCsrfToken middleware to allow a third party post to hit this URL.
Originally I set up a simple single page PHP script to test validating the notificationUrl and that simple script worked fine. It successfully validates Webhooks created that point to it. Here is that one page script code:
<?php
if(isset($_REQUEST['validationToken'])){
echo $_REQUEST['validationToken']; // needed only once when subscribing
} else {
//process like normal not a validation Token request...
}
}
So I would expect that the Laravel endpoint would work like the simple one page PHP script, and it is when I test both URLs in Postman, but the Laravel endpoint is not validating when Office365 attempts to validate it when creating a new webhook.
I have searched all over for help on this and read through all of the Microsoft developer documentation I can find on webhooks and these are some of the more helpful parts of the documentation but I am still not finding an answer to this issue:
https://learn.microsoft.com/en-us/graph/api/subscription-post-subscriptions?view=graph-rest-1.0
https://learn.microsoft.com/en-us/graph/webhooks#notification-endpoint-validation
Any ideas of this?
Thanks Marc! You were correct about the linefeed being the issue, I am still not sure where the line feed is coming from, some how Laravel appears to be adding it. Needless to say I found a solution by adding an "ob_clean();" right before returning the response. Below is my updated notificationUrl method:
public function events()
{
//if validationToken exists return that to validate notificationUrl
if(isset($_REQUEST['validationToken'])){
ob_clean();//this line is cleaning out that previously added linefeed
return response($_REQUEST['validationToken'], 200)
->header('Content-Type', 'text/plain');
}
//process event normally for those that have already been validated
}
It's odd that JakeD's answer requires the use of ob_clean(). here is my webhook controller method in my Laravel 5.7.x app:
use Illuminate\Http\Request;
public function webhook (Request $request) {
if (filled($request->input('validationToken'))) {
return response($request->input('validationToken'))
->header('Content-Type', 'text/plain');
}
// code to process the webhook after validation is complete
}
I don't see an extra linefeed character and the Microsoft Graph API subscription is validated and created.

Twitter API responds with "Your credentials do not allow access to this resource" while calling statuses/update.json

I'm using Hybridauth 3 in my PHP app to make some periodical tweets on behalf of my account.
The app has all possible permissions. I'm giving it all permissions when it asks for them on the first auth step.
After that Twitter redirects me to the specified callback URL and there I'm getting a pair of access_token and access_token_secret.
But when I'm trying to make a tweet using these tokens - it gives me:
{"errors":[{"code":220,"message":"Your credentials do not allow access to this resource."}]}
Here's how I'm trying to make a tweet:
$config = [
'authentication_parameters' => [
//Location where to redirect users once they authenticate
'callback' => 'https://mysite/twittercallback/',
//Twitter application credentials
'keys' => [
'key' => 'xxx',
'secret' => 'yyy'
],
'authorize' => true
]
];
$adapter = new Hybridauth\Provider\Twitter($config['authentication_parameters']);
//Attempt to authenticate the user
$adapter->setAccessToken(/*tokens I've got from getAccessToken() on /twittercallback/*/);
if(! $adapter->isConnected()) {
// never goes here, so adapter is connected
return null;
}
try{
$response = $adapter->setUserStatus('Hello world!');
}
catch (\Exception $e) {
// here I've got the error
echo $e->getMessage();
return;
}
Tried to recreate tokens and key\secret pairs and passed auth process for the app many times, including entering password for my Twitter account (as suggested in some posts on stackoverflow) but still have this error.
P.S. According to this, Hybridauth has fixed the issue in the recent release.
It looks like you are using application authentication as opposed to user authentication. In order to post a tweet, you must authenticate as a user. Also, make sure your Twitter app has read/write privileges.
After comparing headers of outgoing requests from my server with the ones required by Twitter, I've noticed that Hybris doesn't add very important part of the header: oauth_token. At least it's not doing this in the code for Twitter adapter and for the scenario when you apply access token with setAccessToken(). It's just storing tokens in the inner storage but not initializing corresponding class member called consumerToken in OAuth1 class.
So to initialize the consumer token properly I've overridden the apiRequest method for Twitter class (before it used the defalut parent implementation) and added a small condition, so when consumer token is empty before the request - we need to try to init it.
public function apiRequest($url, $method = 'GET', $parameters = [], $headers = [])
{
if(empty($this->consumerToken)) {
$this->initialize();
}
return parent::apiRequest($url, $method, $parameters, $headers);
}
I'm not sure that I've fixed it the best way, but as long as it's working - that's fine.
For your info setAccessToken was fixed in v3.0.0-beta.2 (see PR https://github.com/hybridauth/hybridauth/pull/880)
I faced the same error when implementing a sample app in clojure and the following resource was a huge help to sort out my confusion about application-only auth vs user authentication: https://developer.twitter.com/en/docs/basics/authentication/overview/oauth

Categories