Create calendar event using Microsoft Graph: ErrorAccessDenied - oauth-2.0

I've been using curl and the Outlook REST API v1.0 (with basic authentication) to create calendar events in Outlook calendars. Basic authentication is soon being deprecated so I am now trying to migrate to Microsoft Graph v1.0 with OAuth 2. I have got some way down the road but am stuck at "ErrorAccessDenied" when running the POST to create the event. This is what I've done so far:
Registered my app (curl) here: https://apps.dev.microsoft.com/#/appList, setting Application Permissions to Calendars.ReadWrite (and, for good measure, Delegated Permissions to Calendars.ReadWrite as well.)
Using the client/application ID and client secret/password from step 1 above, I can get an access token using this command:
curl -X POST -k -d "grant_type=client_credentials&client_id=[ApplicationID]&client_secret=[Password]&resource=https%3A%2F%2Fgraph.microsoft.com%2F" https://login.microsoftonline.com/[TenantID]/oauth2/token
I then try to create an event using the following command but get the ErrorAccessDenied error below:
curl.exe -k -o "C:\Temp\output.txt" -X POST -H "Content-Type:application/json" -H "Authorization: Bearer [TOKEN]" -d #"C:\Temp\eventDetails.json" https://graph.microsoft.com/v1.0/users/[UserPrincipalName]/calendars/[CalendarID]/events?$select=Id,lastModifiedDateTime
{
"error": {
"code": "ErrorAccessDenied",
"message": "Access is denied. Check credentials and try again.",
"innerError": {
"request-id": "38b802b9-08b9-4e0b-8f91-66ef56c459f8",
"date": "2018-10-17T01:08:22"
}
}
}
Beyond setting the registered app's Application Permissions to Calendars.ReadWrite (step 1 above) I'm at a loss on how to allow access. Any help would be greatly appreciated.
Thank you.

What helped me greatly is inspecting the token with a JWT inspecting website, like JWT.ms. This website will explain all the details to you about the token (it is build by Microsoft, the token only gets parsed by javascript and isn't transmitted). This website also has a Claims tab, where they explain all the claims in the token.
You're talking about application permissions, that requires an extra step. It also has to be consented by an Azure AD Administrator for the directory you want to access.
You can build this into the sign-in flow, but the easiest way is through the Azure Portal.
Go to https://portal.azure.com -> Azure Active Directory -> [AD Name] - App registrations
Find the app you want to grant access to.
Click Settings
Click Required Permissions
Click Grant Permissions
What this does is creating an application account in your tenant, with the permissions you requested.
After these steps you should create a new token and compare it to the token you got the first time, and you should be able to see the changes.

Related

Microsoft Graph Mail Query - Getting "ErrorAccessDenied"

My company is using Microsoft 365 Business Standard licenses. We are using email through these accounts. We also have a few shared mailboxes. We are trying to create an app that uses the microsoft graph application permissions (rather than the delegated permissions) so the application can access one of the shared mailboxes without needing to be authenticated under the current user.
This is the steps we have taken so far:
Within Microsoft Azure, we have an application in which we have granted application api permissions for Mail.Read, and we have accepted Admin consent.
We authorized as an app, not as a user, in the application using this endpoint https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize, pointing out the required parameters for sending a request. Then, MS API builds this link:
https://login.microsoftonline.com/{some_string}/oauth2/v2.0/authorize?state={some_string}&scope=offline_access%20https%3A%2F%2Fgraph.microsoft.com%2F.default&response_type=code&approval_prompt=auto&redirect_uri=http%3A%2F%2Flocalhost&client_id={some_string}
When we follow the link, we get to the standard authorization form on the site. After we log in, a link is created, where we take the code and create the token: http://localhost/?code={some_string}&state={some_string}&session_state={some_string}
When we try to hit this endpoint: https://graph.microsoft.com/v1.0/users/sharedmailbox#domain.com/messages, we get this response:
{
"error": {
"code": "ErrorAccessDenied",
"message": "Access is denied. Check credentials and try again.",
"innerError": {
"date": "2020-09-14T11:22:30",
"request-id": "{some_string}",
"client-request-id": "{some_string}"
}
}
}
I am thinking that hitting this endpoint https://graph.microsoft.com/v1.0/users/sharedmailbox#domain.com/messages requires us to pass the token previously generated and/or specify which application is making the query?
Any help or direction on what needs to be done to make this query work would be greatly appreciated. Thank you!
I am thinking that hitting this endpoint https://graph.microsoft.com/v1.0/users/sharedmailbox#domain.com/messages requires us to pass the token previously generated and/or specify which application is making the query?
Yes you would need to send the AccessToken in the Authorization header, you should also include the x-anchormailbox header which helps route the request to correct mailbox eg
GET https://graph.microsoft.com/v1.0/users/sharedmailbox#domain.com/messages HTTP/1.1
Host: graph.microsoft.com
Authorization: Bearer EwAoA8l6BAAU ... 7PqHGsykYj7A0XqHCjbKKgWSkcAg==
X-AnchorMailbox: sharedmailbox#domain.com
The other thing you might want to check is to ensure you have the correct scopes in your token you can use https://jwt.io/ for that
In order to use application permissions you will need to use the client credentials auth flow (not the authorization code auth flow which uses delegated permissions). To get a token make a request against "/oauth2/v2.0/token" and specify "grant_type=client_credentials" in the request. See examples of client credentials auth flow here for more details: https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow

Can't set hideFromAddressLists in Logic Apps call to Graph API

I have a Logic App that is calling the Graph API to create lots of O365 Groups. For the creation, I am using Application permissions with a registered Azure app which works great.
However, I am now trying to hide O365 groups from the GAL.
I need to set these parameters:
{
"hideFromAddressLists": true,
"hideFromOutlookClients": true
}
I am having the same issue described here. But I can't figure out how to call the Graph API on behalf of a user, with Delegated permissions. I've tried setting up an Azure Managed Identity and setting it's permissions as per these instructions, but I am getting error:
"code": "ErrorGroupsAccessDenied"
"message": "User does not have permissions to execute this action.",
Can anyone help?
These are the App permissions I have set, but I am still getting "ErrorGroupsAccessDenied" "User does not have permissions to execute this action."
As your mentioned it requires to be called with delegated permission, so you can't get the access token just by MSI. According to the page you provided about MSI, it seems just use the service principal to verify the permissions. It still use application permission but not delegated permission. So please refer to the steps below to get the access token and then request the graph api.
1. Create an "HTTP" action to get the access token(we need to use username/password grant flow in this http request).
2. Use "Parse JSON" action to parse the response data from the HTTP action above.
3. Request the graph api to update the group(with the access token from "Parse JSON" action).
Please notice there is a space between "Bearer" and "access_token".

Onedrive API rejects access token (CompactToken parsing failed with error code: 8004920A)

Hopefully I'm missing something very simple. According to this documentation to get an access token I need to hit the following url:
https://login.live.com/oauth20_authorize.srf?client_id={client_id}&scope={scope}
&response_type=token&redirect_uri={redirect_uri}
So far this appears to be working as the returned url I get contains
/#access_token=EwAYA61DBAAUcSSzo.......
According to the token flow documentation above,
You can use the value of access_token to make requests to the OneDrive API.
According to this page,
Your app provides the access token in each request, through an HTTP header:
Authorization: bearer {token}
When running curl I give it the exact token I was given before,
curl -X GET \
https://graph.microsoft.com/v1.0/drive \
-H 'Authorization: Bearer EwAYA61DBAAUcSSzoTJJs.....
{
"error": {
"code": "InvalidAuthenticationToken",
"message": "CompactToken parsing failed with error code: 8004920A",
"innerError": {
"request-id": "8780c600-2b7f-45a0-b484-7eca9dfd2697",
"date": "2019-01-25T19:33:22"
}
}
}
Why is the token they provided not working?
What I've tried so far:
changing the case of bearer in the header
wrapping said token in {}
generating new tokens
URL decoding said token
One thing to note: the tokens I am receiving are not JWT tokens. Googling the error code pulls up several Stack Overflow questions that seem to imply the graph api is expecting a JWT. If this is the case, am I missing a step to obtaining it?
You can't call Microsoft Graph API after completing the authentication flow for Microsoft accounts (OneDrive personal).
In addition, the authorization process with Microsoft accounts is no longer recommended according the docs and new applications should be developed using Microsoft Graph:
This topic contains information about authorizing an application using Microsoft accounts for OneDrive personal. However, this approach is no longer recommended. New applications should be developed using Microsoft Graph and follow the authorization process in Authorization and sign-in for OneDrive in Microsoft Graph.

Either scp or roles claim need to be present in the token

I'm trying to build an application that uses Microsoft Graph to automatically create and read pages in OneNotes stored in SharePoint 365.
I can successfully do this using Graph Explorer as long as I am logged in, but can't get it to work using a bearer token in Postman
The error I am getting is:
Either scp or roles claim need to be present in the token
I successfully get an access token using this:
https://login.microsoftonline.com/common/oauth2/v2.0/token
And passing in the grant_type, client_id, client_secret, code, redirect_uri and scope
Then I perform the following call, with the bearer token included in the header:
https://graph.microsoft.com/v1.0/sites
With the following getting returned:
{
"error": {
"code": "AccessDenied",
"message": "Either scp or roles claim need to be present in the token.",
"innerError": {
"request-id": "fa442c72-4ffe-493b-a33a-8e9e78c94f09",
"date": "2018-01-19T09:56:34"
}
}
}
I have set up graph permissions as per below. I have also tried enabling ALL Notes permissions configured with the same result though
Graph Permissions Image
One post I found said to check http://jwt.calebb.net/ what is returned in the token, and I found that it doesn't contain any roles, so I wonder if this is the problem.
You may need to state your tenant in the url when getting the token :
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Thanks to Tsuyoshi Matsuzaki as seen here : How to use Application Permission with Azure AD v2
Your application can get access token using the following HTTP request
(OAuth). Note that you cannot use
https://login.microsoftonline.com/common/oauth2/v2.0/token (which is
commonly used) for getting the token. Instead, you must use
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token, which
identifies the specific tenant.
The call that you mentioned "https://graph.microsoft.com/v1.0/sites" is not valid.
Also request to ~/sites/... routes may need other permissions in addition to Notes.ReadWrite.
Recommend to follow steps mentioned in the following references:
https://developer.microsoft.com/en-us/graph/docs/concepts/permissions_reference
https://developer.microsoft.com/en-us/graph/docs/concepts/auth_v2_user
https://developer.microsoft.com/en-us/graph/docs/concepts/auth_v2_service

How to access another user's data via the Graph API?

Using the Graph API and related authentication/authorization flows, how can I access data of all users in an organization? Also, multiple organizations/tenants need to be able to use the app.
As an example: I have an app which needs to read events from all calendars of all users under contoso.onmicrosoft.com. I would like that jack#contoso.onmicrosoft.com, the administrator, authorizes the app which will then be able to read the mentioned the data. Using the Managed API this can be easily done via impersonation.
However, I am trying to do the same with Graph API and OAuth, but I can't find a straight forward solution, or I must be missing something very obvious. I have created an app through manage.windowsazure.com (multi-tenant), and configured it so that it requires Microsoft Graph API (all application and delegated permissions).
I did the following:
1) Point jack#contoso.onmicrosoft.com towards https://login.microsoftonline.com/common/oauth2/authorize?response_type=code&redirect_uri=<my redirect url>&client_id=<my client id>
2) Jack authorizes the app
3) I get back: <my redirect url>/?code=<my authorization code>&session_state=<blah>
4) I send a POST request like below:
POST https://login.microsoftonline.com/common/oauth2/token
Headers: content-type: application/x-www-form-urlencoded
Body:
grant_type=authorization_code&code=<my auth code from step above>
&redirect_uri=<my redirect url>
&client_id=<my client id>
&client_secret=<my client secret>
&resource=https%3A%2F%2Fgraph.microsoft.com%2F
5) When I do the following:
GET https://graph.microsoft.com/v1.0/users/jack#contoso.onmicrosoft.com/messages
Headers: Authorization: Bearer <auth token from step #4>
I get a 200 OK response with the messages.
When I do the following:
GET https://graph.microsoft.com/v1.0/users/anyotheruser#contoso.onmicrosoft.com/messages
Headers: Authorization: Bearer <auth token from step #4>
I get a 403 Forbidden response with:
{
"error": {
"code": "ErrorAccessDenied",
"innerError": {
"date": "2016-06-07T08:47:27",
"request-id": "5b629e30-e6bd-474d-b3dd-8ce25c5ad1c4"
},
"message": "Access is denied. Check credentials and try again."
}
}
The flow/URLs you've referenced are for the authorization code flow which leverages delegated scopes.
A) If you want the app to only work (i.e. access all calendars) for admins then you are using the right flow.
B) If you want the app to work for all users after the admin consents to it, you'll need to use the app (client credentials) flow and use application scopes. This means that you'll need to separate out consent from the regular auth flow.
For consent you'll need to to point the admin to the following url:
GET https://login.microsoftonline.com/common/oauth2/authorize?resource=https://graph.microsoft.com/&client_id=<YourClientId>&client_secret=<YourClientSecret>&response_type=code&redirectUri=<YourRedirectUri>&prompt=admin_consent
For auth flow you'll need a single call from your web server:
POST https://login.microsoftonline.com/common/oauth2/token
body resource=https://graph.microsoft.com/&client_id=<YourClientId>&client_secret=<YourClientSecret>&response_type=code
Or better yet, just use ADAL's AquireToken(resource, clientCredentials) overload.
Once that's done, your app should be good to go to make requests to Graph.
Regardless if you want to stick to A) or switch over to B), to double check that things are set up correctly you can:
Check the token that you get back from Graph (the one you attach to the request along with Bearer) and confirm that it has a roles entry with the roles you need i.e. Calendars.Read
NOTE: The following steps 2.a & 2.b require you to have admin to a test tenant where you'd be consenting to the application.
2.a Use GraphExplorer (https://graphexplorer2.azurewebsites.net/) and confirm that consent has been properly set up by querying
beta/servicePrincipals?$filter=displayName eq '[YourApplicationName]'
If nothing shows up, then the no one has consented to the application.
2.b (only applicable for auth code flow with delegated scopes) Use GraphExplorer and confirm that either delegation has been authorized correctly by querying
beta/oauth2permissiongrants?$filter=clientId eq '[IdFrom ServicePrincipal in 2.a]'
And ensuring you get either a result for the specific user in question or for "AllPrincipals".
More info on app vs delegated scopes here: http://graph.microsoft.io/en-us/docs/authorization/permission_scopes
More info on app flow here: https://graph.microsoft.io/en-us/docs/authorization/app_only
Please use the app-only auth flow (see https://graph.microsoft.io/en-us/docs/authorization/app_only) to use the application permissions - for the token request (step 4) you need to pass grant_type=client_credentials instead of grant_type=authorization_code.

Resources