Getting notified by the Microsoft Graph with Webhooks Elio Struyf Trainer @ U2U MVP September 9th, 2017
What are WebHooks?
What are WebHooks? Event driven notifications AKA callbacks from the web Universal model used by many services: GitHub, Slack, MailChimp, Pushing mechanism à asynchronous! Implemented in various Office 365 Services Microsoft Graph Connectors SharePoint
Where can you use them? SharePoint List Libraries Microsoft Graph Messages Events Contacts Conversations in groups OneDrive Microsoft Teams Connectors (incoming webhooks)
The good and the bad WebHooks are asynchronous mechanism Mechanism to respond on things that happen-ed Retry mechanism Tries to send the event a number of times within 4 hours window Requires some setup, but once done, it is easy to maintain
How to start using WebHooks?
By subscribing to a WebHook! 1. Create a subscription 4. MS Graph responds with subscription info Notification service
Important things about subscribing Subscribing: POST https://graph.microsoft.com/v1.0/subscriptions { } "changetype": "created,updated,deleted", "notificationurl": "Your notification service URL HTTPS is required", "expirationdatetime": "The subscription expiration date", "resource": "The resource to monitor" "clientstate": "String value for validation in your service"
Expiration times vary per resource type Mail, Calendar, Contacts, Group conversations 4230 minutes à 2,9375 days OneDrive 86400 minutes à 60 days
What resource? The resource is the endpoint you are interested in. Example: me/messages me/contacts me/events Users/user@tenant.onmicrosoft.com/mailFolders('Inbox')/messages
Important things about subscribing The Microsoft Graph calls your notification service: POST - https://notification-service?validationtoken={random-guid} Respond with: Status: 200 Body: validationtoken Important: notification service needs to respond within 10 seconds
Important things about subscribing When validation is done, Microsoft Graph returns the subscription information { } "@odata.context":"https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity", "id":"6d4f0dbd-7824-41ab-b9ad-16ba6e7341e2", "resource":"users/admin@estruyfdev2.onmicrosoft.com/mailfolders('inbox')/messages", "changetype":"created,updated,deleted", "clientstate":"demowebhooksubscription", "notificationurl":"https://90a26680.ngrok.io/listen", "expirationdatetime":"2017-09-21t13:14:01.68z
Local development and testing: ngrok - Secure tunnels to localhost https://ngrok.com/
Demo Subscribing to a WebHook
More about the notification service
Notification service details You choose the technology: Web API, Node.js, Respond in < 30-60 seconds and with status: 202 - Accepted Retry mechanism à multiple retries over timespan of 4 hours Not for the validation process Receives minimal information You have to define the logic what you want to do per type of change
What can I expect from the notification?
This is what you will receive { } "value": [{ "subscriptionid":"64a28ad9-08c9-4e6c-b317-9820f1bbc408", "subscriptionexpirationdatetime":"2017-09-21t13:05:39.552+00:00", "changetype":"created", "resource":"users/4b37b409-5518-476e-91db-4baf0b39baaa/messages/message-id" "resourcedata":{ "@odata.type":"#microsoft.graph.message", "@odata.id":"users/4b37b409-5518-476e-91db-4baf0b39baaa/messages/message-id", "@odata.etag":"w/\"cqaaabyaaadytakpqfg/q6i7mg8jzqy0aaayjst2\"", "id": message-id }, "clientstate":"demowebhooksubscription }]
How do I know what happened? { } "value": [{ "subscriptionid":"64a28ad9-08c9-4e6c-b317-9820f1bbc408", "subscriptionexpirationdatetime":"2017-09-21t13:05:39.552+00:00", "changetype":"created", "resource":"users/4b37b409-5518-476e-91db-4baf0b39baaa/messages/message-id" "resourcedata":{ "@odata.type":"#microsoft.graph.message", "@odata.id":"users/4b37b409-5518-476e-91db-4baf0b39baaa/messages/message-id", "@odata.etag":"w/\"cqaaabyaaadytakpqfg/q6i7mg8jzqy0aaayjst2\"", "id": message-id }, "clientstate":"demowebhooksubscription }]
How do I know what happened? { } "value": [{ "subscriptionid":"64a28ad9-08c9-4e6c-b317-9820f1bbc408", "subscriptionexpirationdatetime":"2017-09-21t13:05:39.552+00:00", "changetype":"created", "resource":"users/4b37b409-5518-476e-91db-4baf0b39baaa/messages/message-id" "resourcedata":{ "@odata.type":"#microsoft.graph.message", "@odata.id":"users/4b37b409-5518-476e-91db-4baf0b39baaa/messages/message-id", "@odata.etag":"w/\"cqaaabyaaadytakpqfg/q6i7mg8jzqy0aaayjst2\"", "id": message-id }, "clientstate":"demowebhooksubscription }]
How do I know what happened? { } "value": [{ "subscriptionid":"64a28ad9-08c9-4e6c-b317-9820f1bbc408", "subscriptionexpirationdatetime":"2017-09-21t13:05:39.552+00:00", "changetype":"created", "resource":"users/4b37b409-5518-476e-91db-4baf0b39baaa/messages/message-id" "resourcedata":{ "@odata.type":"#microsoft.graph.message", "@odata.id":"users/4b37b409-5518-476e-91db-4baf0b39baaa/messages/message-id", "@odata.etag":"w/\"cqaaabyaaadytakpqfg/q6i7mg8jzqy0aaayjst2\"", "id": message-id }, "clientstate":"demowebhooksubscription }]
Retrieve the message information GET - https://graph.microsoft.com/v1.0/{resource value} Example: https://graph.microsoft.com/v1.0/users/4b37b409-5518-476e-91db- 4baf0b39bbbb/Messages/AAMkADM5N2VjMjU3LWQyM2YtNGQ4MS05NzZkLTZhN WFmMTcwNGFjYQBGAAAAAAA88VAhp3C7SbzAOB6K6turBwDyTAkPqFG- Q6I7Mg8jZqy0AAAAAAEMAADyTAkPqFG-Q6I7Mg8jZqy0AAAYIipBAAA=
You still need permissions in order to get started
Permissions per resource type / item Resouce type / item Contacts Conversations Events Messages Drive (User s OneDrive) Drives (SharePoint shared content and drives) Permission Contacts.Read Group.Read.All Calendars.Read Mail.Read Files.ReadWrite Files.ReadWrite.All
Creating your notification service
Get an access token via client ID and secret public getapponlyaccesstoken(): Promise<any> { return new Promise<any>((resolve, reject) => { const context = new AuthenticationContext(authority); context.acquiretokenwithclientcredentials(graphurl, clientid, secret, (err, tokenres) => { if (err) { reject(err); return; } }); }); resolve(tokenres.accesstoken);
A bit more secure, working with certificates Azure AD app registration Generate a certificate Acquiring a token via a certificate sets the appidacr value to 2 Application Authentication Context Class Reference 0 = Public client 1 = Identified by a client id and secret 2 = Identified by a certificate
Azure AD application manifest Store this in a separate file (privatekey.pem) Fingerprint is also require to get the access token
Get an access token via a certificate and private key with ADAL for JS public getapponlyaccesstoken(config: IConfig): Promise<any> { return new Promise((resolve, reject) => { const certificate = fs.readfilesync(path.join( dirname, 'privatekey.pem'), { encoding : 'utf8'}); const authcontext = new adal.authenticationcontext(authority); authcontext.acquiretokenwithclientcertificate(resource, clientid, certificate, fingerprint, (err, tokenres) => { if (err) { reject(err); return; } } }); }); resolve(tokenres.accesstoken);
The subscription APIs Retrieve subscription information GET - https://graph.microsoft.com/v1.0/subscriptions/{id} Update a subscription PATCH - https://graph.microsoft.com/v1.0/subscriptions/{id} Body: { "expirationdatetime": "New expiration date" } Delete a subscription DELETE - https://graph.microsoft.com/v1.0/subscriptions/{id}
When do I use the token? request.get("https://graph.microsoft.com/v1.0/subscriptions/{id}", { headers: { "content-type": "application/json", "Accept": "application/json", "Authorization": "Bearer " + token } }, (error, response, body) => { if (error) { console.log('error: There was an error creating the subscription.'); console.log(json.stringify(body)); return null; } return body; });
Demo Sample application
Think about renewing your subscriptions
Renewing subscriptions Check expiration date per notification your service receives Be careful: could be that you do not receive notifications for a certain period of time Timer triggered job which renews each known subscription Better option
Real life scenario
A real life setup and configuration 1. Subscribe and validate 3. Notify service 5. Respond with 200 HTTP triggered function 4. Put notification message on a queue 10. Update sub. 2. Store the subscription ID Timer triggered function 6. Trigger another Azure function that will do change handling Queue triggered function 9. Your business logic starts here
Demo Azure Functions + Webhooks
Brand new from MS Ignite Azure Functions & Microsoft Graph integration
Microsoft Graph integration in preview
Microsoft Graph integration in preview
Recap Azure AD Application Permission configuration and token retrieval Notification service has to respond in < 30-60 seconds Retry mechanism 4 hour timespan with multiple retries Subscription expiration date varies per resource! Think about your renewal process
Questions?
Elio Struyf Lead trainer and architect @eliostruyf www.eliostruyf.com info@estruyf.be Office Servers & Services MVP Azure / Office 365 / SharePoint
Resources Webhook documentation - http://elst.es/2xleaoo Certificate creation process with makecert - http://elst.es/2rhvezc Keycred GitHub - http://elst.es/2pw77vm Node.js mail tracker Azure Function - http://elst.es/2lgmwdi Microsoft Graph Webhooks Sample for Node.js - http://elst.es/2w3zexc Microsoft Graph ASP.NET Webhooks - http://elst.es/2hcuef6