JakeD
JakeD

Reputation: 59

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:

Any ideas of this?

Upvotes: 6

Views: 3391

Answers (2)

Jeremy Braband
Jeremy Braband

Reputation: 19

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.

Upvotes: 0

JakeD
JakeD

Reputation: 59

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
}

Upvotes: -1

Related Questions