I am having trouble starting a Microsoft Graph webhook subscription (for a mailbox in particular). When initiating the subscription it appears as though Microsoft accepts all of the parameters I am sending to configure the subscription but fails the total subscription because it is receiving a non 2xx response from the endpoint I have configured.
The reason my endpoint is sending a 401 back to Microsoft is because their POST that includes the subscription validation token is missing the clientState.
I am using the clientState key-value pair to authenticate all the communication between Microsoft and my endpoint. If my endpoint does not see the correct clientState it will return a 401.
Any ideas on what I might be missing or if I should go about this in a different way? In my opinion allowing my endpoint to accept unauthenticated GET/POST's is not an option.
Example request body using POST method including the API key in the header:
{
"changeType": "created",
"clientState": "testClientState",
"resource": "users/<UserName>/messages",
"expirationDateTime": "2017-08-10T10:24:57.0000000Z",
"notificationUrl": "<EndpointURL>"
}
Error Returned from Microsoft:
"error": {
"code": "InvalidRequest",
"message": "Subscription validation request failed. Must respond with 200 OK to this request.",
"innerError": {
"request-id": "adf7fc7b-6b14-4422-8526-c1391be8dd27",
"date": "2017-08-07T16:24:59"
}
}
I understand everything to work as intended until my endpoint is sent the validation token because I receive the validation token but my endpoint rejects it because it is missing the client state.
Endpoint Log Snippet:
queryStringParameters": {
"validationToken": "<ValidationToken sent by Microsoft>"
}
I am basing my API endpoint logic off of some of Microsoft's developer guides. For the subscription creation in particular I am using this guide.
It appears this question was also asked but not answered on GitHub.
I represent Microsoft Graph Web hooks team ...
We verified your request in our MS Graph Service logs and confirmed that it was failed at Subscription validation phase because of HTTP status code='Unauthorized' from your endpoint ... Up to this everything is correct per your observation ...
By design, MS Graph Web hooks do not send the clientState header as part of the Subscription validation request. Please do not expect for this header during the subscription validation.
More information
You would have expected to receive the clientState as part of the validation request header because Office365 graph sends it https://msdn.microsoft.com/en-us/office/office365/api/notify-rest-operations. Office 365 Graph different from MS Graph ...
There are some document improvements observed with this question at https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/subscription_post_subscriptions ... Example Publisher Notification Payload, which is after successful subscription is mentioned together with the Subscription validation … We fixed those.
Related
Problem Description
I was trying to call this GET via Graph Explorer
https://graph.microsoft.com/v1.0/reports/microsoft.graph.getM365AppUserCounts(period='D7')
But it fails the following error:
The response content is not available in Graph Explorer due to CORS policy. You can execute this request in an API client, like Postman. Read more about CORS and understand how it works here.
What I've Tried so Far
So I went ahead and tried to set up a new application registration in Azure Portal, with a client secret. I've tried both Id Tokens and Access Tokens but I get the same results.
As far as API permissions, I'm not sure what I'm supposed to add to access the reports about M365 application usage. In my case I want to find out the forms usage.
I then forked the MS Graph collection into my local postman workspace and have the following values in the Authorization tab for the collection :
Access Token URL: https://login.microsoft.com/<tenantID>/oauth2/token
Client ID: (new app registration id)
Client Secret: (new secret)
It seems to generate a token no problem. I created a custom query under the MS graph collection and the authorization is set to inherit from parent. And when I check the headers tab, I can see it's using an Authorization header with the token.
But I consistently get the following error message in postman:
{
"error": {
"code": "InvalidAuthenticationToken",
"message": "Access token validation failure. Invalid audience.",
"innerError": {
"date": "2022-10-26T19:52:48",
"request-id": "ggg-3d7b-ggg-a748-ggg",
"client-request-id": "sdfg-3d7b-47d9-a748-sdfg"
}
}
}
Specific questions
What API permissions are needed to run those reports?
any idea on why I'm getting the invalid token error? Is it because I am missing scope?
If i need scope what value do i supply?
Any help would be appreciated.
EDIT 1
I've updated the scope value in Postman to use:
https://graph.microsoft.com/.default
I've updated my Azure Application Registration to move all API permissions except "Reports.Read.All - Application"
In the specific Postman request, under the authorization tab, the value is set to "inherit from parent". Under "Headers" tab, I can see it adds the "Authorization" header with the value of my token. When I click on the "Go to authorization" hyperlink from the headers page, it takes me to the correct Authorization configuration section in the top level folder of the mS Graph collection.
After making these changes, I'm still getting the same error message "message": "Access token validation failure. Invalid audience.","
My app uses delegated user access tokens to interact with the MSFT graph api. They have the Calendars.ReadWrite.Shared permission. When making requests to https://graph.microsoft.com/v1.0/subscriptions in production with the following request body:
{
"changeType": "created,updated,deleted",
"notificationUrl": <https_app_notification_url>,
"resource": "/me/calendars/<calendar_id>/events",
"expirationDateTime": (datetime.now(timezone.utc) + timedelta(minutes=4200)).isoformat(),
"clientState": <a_crypto_random_string>
}
almost every time the response is an error with the following in the response body:
[Status Code: Forbidden; Reason: Access is denied. Check credentials and try again.]
The most recent occurrence of this error had the following innerError in the response:
{'date': '2020-05-13T20:58:09', 'request-id': '1448e490-9e45-4a08-9aab-dd9c996c18db'}
This happens even when trying to subscribe to the user's own default calendar.
The strange thing is that the exact same code, when run on my local machine and tunneled to the web via ngrok to expose my app's notificationUrl endpoint, is able to consistently get a 201 from the MSFT's subscriptions endpoint.
I've checked the system time on the server with the date command and there's less than 1 second difference between my local machine and the server. So I don't think the expirationDateTime field is the issue; it's got 30 minutes of buffer built in as I believe the max according to the docs is 4230.
Should I be requesting additional permissions in the oauth scopes for the user access tokens? The docs say that Calendars.Read is enough so I would've thought Calendars.ReadWrite.Shared would do it. Or am I missing something else?
You need Calendars.Read as outlined in the documentation to subscribe to the current user's calendar.
I'm trying to use the Microsoft Bookings API in Microsoft Graph. I've gone through the OAuth stage where I grant access and retrieve an authorization code from the token api. This works well. My issue now is that I get this response:
{
"error": {
"code": "ErrorInvalidOperation",
"message": "Mailbox does not exist.",
"innerError": {
"request-id": "e40bf9b9-8f2b-48e9-bb37-7a9ce1794ae7",
"date": "2020-01-27T21:28:46"
}
}
}
When I run the request recommended here: https://learn.microsoft.com/en-us/graph/api/resources/booking-api-overview?view=graph-rest-beta
POST https://graph.microsoft.com/beta/bookingBusinesses
Authorization: Bearer {access token}
Content-Type: application/json
{
"displayName":"Contoso"
}
(With another displayName)
I don't get what's wrong, do I need to set something up for my account or for the application in the Azure dashboard?
Any help would be greatly appreciated!
Found the answer!
The license for the company I was working for was not sufficient. Had to upgrade license to "P1" license.
The company I'm working for has also outsourced the management of their Microsoft environment, which means I couldn't change that license myself.
Changing the license fixed the problem though!
I have experienced the same problem with our organisation using a service account, I am doing exactly the same request and receiving exactly the same result from Graph API, However, our service account has an MS A3 license with MS booking permission. We found out the reason for it, is because the service account was not granted an Exchange mailbox. And it works after we granted an exchange mailbox to the account.
I'm getting an HTTP 400 on a request for https://graph.microsoft.com/v1.0/sites?search=* but expected response is an HTTP 200 w/ data.
It works successfully in multiple client environments (i.e. 100s of environments), but it doesn't work for one of our clients.
The application does successfully return calls for /groups, /domains, and other endpoints prior to hitting this HTTP 400.
The error is:
Error authenticating with resource
Response from remote side is:
{
"code": "AuthenticationError",
"message": "Error authenticating with resource",
"innerError": {
"request-id": "1d68c066-dba8-487e-b3a8-bd77f517d394",
"date": "2019-06-25T18:50:31"
}
}
I'm not sure the next steps from here. HTTP 400 suggests a malformed request, but I'm guessing this should actually be an HTTP 403 or 401 response instead of a 400?
Not sure how to resolve, please let me know, thanks!
edit: request-id added back in
If you're able to call other resources the same way (i.e. /groups), it would suggest a lack of permissions. Be sure the app or user authenticating has the required permissions:
Delegated (work or school account): Sites.Read.All, Sites.ReadWrite.All
Delegated (personal Microsoft account): Not supported.
Application: Sites.Read.All, Sites.ReadWrite.All
Documentation here
I am trying to use the create subscription API to open a subscription channel on a calendar's events such that we can receive updates when interviewers respond to the interview.
I have tried many variations to get this request to work:
Permission Scopes
I have gone through the oauth process to retrieve graph access tokens for a user with the following permission Scopes:
Mail.Send
Calendars.ReadWrite.Shared
User.ReadBasic.All
Successful Calendar Event Creation
Using the access tokens with the above scopes, before attempting to create the subscription, I add an event with {eventId} to a shared calendar, let's say with the user relative id of {sharedCalendarId}.
Subscription Creation Attempts
Using the same access tokens to create the same Authorization header with permission scopes listed above, I consistently receive the same ExtensionError response. Below I will list the various permutations of request I have made to the graph API.
Urls Attempted
I have sent each permutation of the subscription POST requests to the following URLs:
https://graph.microsoft.com/v1.0/subscriptions
https://graph.microsoft.com/beta/subscriptions
Request Headers
Content-Type: application/json
Authorization: Bearer {authToken}
Request Bodies Attempted
The changeType and notificationUrl, and expirationDateTime are the same for all requests.
I have tried 3 different variations on the resource string, and have tried the request with and without the clientState.
First I tried the fully identified resource and added clientState:
{
"changeType": "created,updated",
"notificationUrl": "https://{domain-with-valid-https-certs}/calendar/microsoft/notifications",
"resource": "users/{userPrincipalName}/calendars/{sharedCalendarId}/events",
"clientState": "{thisIsLimitedTo128Characters}",
"expirationDateTime": "2018-05-23T21:02:45.487Z"
}
Next, I abandoned clientState for the sake of narrowing down the problem and I used me instead of {userPrincipalName}:
{
...
"resource": "me/calendars/{sharedCalendarId}/events"
}
Then I abandoned the shared calendar to see if that was the problem and used a resource that's an example in the docs.
{
...
"resource": "me/events"
}
Error Response Received
While I seem to receive the validation request to the notificationUrl provided (and the server responds appropriately) the request to create the subscription then responds with this kind of error:
{
code: "ExtensionError",
message: "Operation: Create; Exception: [Status Code: Forbidden; Reason: Forbidden]",
innerError: {
"request-id": "0d86a983-ea50-4226-905c-38f0e8f12308",
date: "2018-05-23T03:55:38"
}
}
What's confusing is that I am attempting to create this subscription on a calendar I have just successfully created or changed and event, so the 403 seems like it should not be related to access to the resource.
Are there specific permissions or scopes that need to be granted for us to successfully create subscriptions to a calendar's events?
Is there a different API we should be using?
In order to subscribe to a resource, you minimally need read access to the resource. So for events, you would need Calendars.Read. Since you have more than that, you should be fine. (Reference)
I believe your problem is that you are not including expirationDateTime in the payload. This is a required property (documented here, arguably not the best place for this to be listed). Set it to a value equal to or less than the maximum for calendar, 4230 minutes (Reference).
Self-answer here for anyone having a similar issue who doesn't want to dig through comments.
After some debugging help from Marc LaFleur and Jason Johnston (thanks for the responses!), the answer to my specific question is "sort of".
No, there are no specific Subscriptions.* scopes, and they are not needed to create subscription channels.
But, quixotically, the POST /subscriptions creation endpoint requires more specific permission scopes than the resource itself.
In my example, the Calendars.ReadWrite.Shared permission does indeed allow my app to read and write events to the user's calendars and shared calendars, but I was unable to create a subscription channel for the same calendar.
Requesting the less permissive and more specific scope of Calendars.ReadWrite in addition to Calendars.ReadWrite.Shared solved my problem.