0
votes

I am having some trouble working with the MicrosoftGraph Webhook. I think that I am not properly returning HTTP response, because when I put a binding.pry into my controller, after making the request it hits the controller.

However, the request seems to timeout before hitting my controller (see stack trace below). Here is my controller code and subscription request code, and stack trace. I really appreciate the help.

def self.create_subscription(user)

  callback = Proc.new do |r| 
    r.headers['Authorization'] = "Bearer #{user.outlook_token.access_token}"
    r.headers['Content-Type'] = 'application/json'
    r.headers['X-AnchorMailbox'] = user.email
  end

  path = 'subscriptions'

  data = {
    changeType: "created, updated",
    notificationUrl: "https://my_url/api/watch/outlookNotification",
    resource: "me/mailFolders('Inbox')/messages",
    expirationDateTime:"2017-08-19T06:23:45.9356913Z",
    clientState: "subscription-identifier"
  }

  graph = MicrosoftGraph.new(base_url: 'https://graph.microsoft.com/v1.0',
                             cached_metadata_file: File.join(MicrosoftGraph::CACHED_METADATA_DIRECTORY, 'metadata_v1.0.xml'),
                             &callback)

  response = graph.service.post(path, data.to_json)
end

def outlook_webhook  
   @token = params[:validationToken]
   head 200, content_type: "text/plain", content_length: 7
   response.body = @token
end

Completed 500 Internal Server Error in 14613ms (ActiveRecord: 478.4ms)

OData::ClientError (400 InvalidRequest: "Subscription validation request timed out." from "https://graph.microsoft.com/v1.0/subscriptions"):
app/models/outlook_wrapper.rb:44:in create_subscription'
app/controllers/business/messages_controller.rb:18:in
index'

Rendered /Users/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/actionpack-4.2.4/lib/action_dispatch/middleware/templates/rescues/_source.erb (7.9ms) Rendered /Users/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/actionpack-4.2.4/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb (1.9ms) Rendered /Users/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/actionpack-4.2.4/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb (3.8ms) Rendered /Users/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/actionpack-4.2.4/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb (176.2ms)

Started POST "/api/watch/outlookNotification?validationToken=YTJhMTk5MDQtYjdmOC00ZjYxLWIzOGEtMDczM2FjMTAxZTBj" for 52.161.110.176 at 2017-08-23 12:29:05 -0400 Processing by InboundEmailsController#outlook_webhook as HTML Parameters: {"validationToken"=>"YTJhMTk5MDQtYjdmOC00ZjYxLWIzOGEtMDczM2FjMTAxZTBj"} Completed 200 OK in 35ms (ActiveRecord: 0.0ms)

2

2 Answers

0
votes

A Content-Length of 7 is not large enough to accommodate the validation token.

As far as the request timing out, when testing locally, is notificationUrl a publicly accessible URL? If you're using something like http://localhost/api/watch/outlookNotification, then Microsoft will not know how to access your webhook since localhost is not a publicly accessible host.

Even if you do provide a publicly accessible host, you'll still need to poke a hole in your firewall. My suggestion is to use ngrok when testing locally and use an environment variable to set the host.

Run ngrok http 3000 in a console and it will give you a URL that looks something like https://d83375e5.ngrok.io. While ngrok is running, it will forward HTTP and HTTPS requests from d83375e5.ngrok.io to your computer on port 3000.

If you use a notificationUrl like https://${ENV['WEBHOOK_HOST']}/api/watch/outlookNotification, then in development, set the environment variable WEBHOOK_HOST to the host provided by ngrok. In production, you don't need to run ngrok; just set WEBHOOK_HOST to the publicly accessible host for your server.

0
votes

I may be a little late to the party, but I ran into this same issue. The development environment in rails blocks multiple requests which is why you are getting a timeout. Since you are waiting for a response from the post while Graph is blasting a request back, the request will be blocked until you receive a response from your first post.

As it seems you have discovered, when running in prod this is not an issue as the production environment settings do not block multiple requests.