Access is denied. Check credentials and try again - node.js

I am trying to access calendar events using the Microsoft Graph API (https://graph.microsoft.com/v1.0/me/calendarView) on node.js following this permissions guide but I receive the error response :
{
"code": "ErrorAccessDenied",
"message": "Access is denied. Check credentials and try again.",
"innerError": {
"request-id": "7c2...",
"date": "2016-07-13T21:19:11"
}
}
The call was made with using :
request({url : 'https://graph.microsoft.com/v1.0/me/calendarview', qs : queryParams, 'auth': {'bearer': token}}, function (error, response, body) {
...
});
The request has a valid token and the call to .../me/ via
request({url : 'https://graph.microsoft.com/v1.0/me/', 'auth': {'bearer': token}}, function (error, response, body) {
...
});
returns :
{"#odata.context":"https://graph.microsoft.com/v1.0/$metadata#users/$entity",
"id":"<valid_id>",
"businessPhones":[],
"displayName":"<valid_name>",
"givenName":"<valid_name>",
"jobTitle":"<valid_title>",
"mail":"<valid_email>",
"mobilePhone":"<valid_cell>",
"officeLocation":null,
"preferredLanguage":"en-US",
"surname":"<valid_name>",
"userPrincipalName":"<valid_email>"}
So I am assuming this is an issue with permissions set on https://manage.windowsazure.com/ where I created two applications, one for the node server and one for the web client application. I am using passport for authentication and the client id and secret for the web client application.
var AzureOAuthStrategy = require('passport-azure-oauth').Strategy;
passport.use(new AzureOAuthStrategy({
clientId: config.live.clientID,
clientSecret: config.live.clientSecret,
tenantId: config.live.tenant,
resource: 'https://graph.microsoft.com/',
redirectURL: config.live.callbackURL
},
function(accessToken, refreshToken, profile, done) {
Here is what I set for "permissions to other applications" on the node application:
Windows Azure Active Directory :
Delegated Permissions
Read all users' full profiles
Sign in and read user profile
Microsoft graph :
Delegated Permissions
Have full access to user calendars
Read user calendars
(see below)
Delegated Permissions
Access
Here is what I set for "permissions to other application" on the web client application:
Microsoft Graph :
Application Permissions
Read and write calendars in all mailboxes
Read calendars in all mailboxes
Delegated Permissions
Sign users in
Read user contacts
Have full access to user calendars
Read user calendars
Sign in and read user profile
Office Exchange 360 Online :
Application Permissions
Read and write calendars in all mailboxes
Read calendars in all mailboxes
Delegated Permissions
Read user and share calendars
Read and write user and shared calendars
Read all users' basic profiles
Read user profiles
Read user contacts
Read user calendars
Windows Azure Active Directory
Application Permissions : none
Delegated Permissions
Sign in and read user profile
I'm not 100% on what the relationship between the permissions set within the azure management portal and specific end point access. I have read the API scope article but discussion in that article is a bit too conceptual for my needs.
Ultimately I am trying to access and write events to all reservable resources within a tenant id.

I was able to resolve the issue by deleting then regenerating the application through https://manage.windowsazure.com/ then updating the client id and secret. I was, after the fact, able to remove the native client (node) app from the application list and still make the call work.
And while I did not use the reference https://jwt.io/ provided by Fei Xue, I assume it will be invaluable for debugging azure to API permissions in the future.

Related

Microsoft Graph to send mail with Client Credential Flow (application permission) and personal account

I am learning Microsoft Graph and for this I use Graph Explorer and Postman.
With Graph Explorer : I am Signed In with my personal user account (hotmail). As soon as I am connected, I can see the token. Strangely when I copy/paste this token in jwt.io it cannot be decoded. Yet I can perform query like https://graph.microsoft.com/v1.0/me which returns me some infos of myself as a user (with http 200). Or this query https://graph.microsoft.com/v1.0/me/sendMail which allow me to send a test and receive a test mail (with http 202). All of these requests was "delegated permission". So I don't have any problem using Graph Explorer with my personnal account (hotmail).
With Postman : this time I will perform some tests with "application permission". I followed the steps below:
On the Azure Portal
Step 1: App registrations / New registration / I give a name / Choose the 3rd account type (Accounts in any organizational directory and personal Microsoft accounts) / Click on Register button
Step 2: Api permissions / Add permission / Microsoft Graph / Application permissions / Mail.Send (send mail as any user)
Step 3: Grand admin consent for... button to activate the permission
Step 4: Certificate & Secrets / New client secret / Enter a description / Click Add button
Step 5: Obtain a token in Postman
POST
https://login.microsoftonline.com/{my-tenant-id-here}/oauth2/v2.0/token
HEADERS
Content-Type: application/x-www-form-urlencoded
BODY
client_id: {my-client-id-here}
client_secret: {my-client-secret-here}
grant_type: client_credentials
scope: https://graph.microsoft.com/.default
OK I got a token
When copy/paste this token in jwt.io I see this:
Step 6: Query for listing all users
GET
https://graph.microsoft.com/v1.0/users
AUTHORIZATION
Bearer token: {bearer-token-received-previously}
HEADERS
Content-Type: application/json
OK I got infos for all users (as json)
Step 7: Query for sending a mail
POST
https://graph.microsoft.com/v1.0/users/{user-principal-name}/sendMail
AUTHORIZATION
Bearer token: {bearer-token-received-previously}
HEADERS
Content-Type: application/json
BODY (JSON)
{
"message": {
"subject": "This is my subject",
"body": {
"contentType": "Text",
"content": "This is my content"
},
"toRecipients": [
{
"emailAddress": {
"address": "thierry.langie#skynet.be"
}
}
],
"ccRecipients": [
]
},
"saveToSentItems": "false"
}
NOT OK Error: MailboxNotEnabledForRESTAPI - REST API is not yet supported for this mailbox
I would like to know why I got this error ? I can send email with Graph Explorer (when using delegated permission) and not with Postman (when using application permission).
As you can see below, I grant admin consent in Enterprise applications on the Azure Portal.
I read somewhere that the error means the user doesn't have the mailbox in EXO cloud. EXO is O365 Exchange Online Cloud. So if you don't have mailbox in the cloud O365 Exchange REST APIs will not work for these users. If that is the case, what would you do ?
I have a Web application which should send mails from a shared mailbox. No matter which user is connected, this is always the same mailbox which is used to send mails. That's why I go with "application permission" and "Client credential flow".
As explained above, I use my personal account (hotmail) for testing purpose but in production I'll use a work account (not accessible from my dev environment).
As a side note, I know there are libraries to facilitate the process and avoid using REST API calls (urls) but I don't think that can explain the problem I am facing.
"MailboxNotEnabledForRESTAPI - REST API is not yet supported for this mailbox" This error message means that the email account you are using to send email doesn't have an Exchange Online license.
For a personal account, you should use Delegated permission, which you have tried in Microsoft Graph Explorer. See Permissions here.
.
If we add the personal account to your tenant as a guest user, although we can Assign a license to a guest user (I assume we can assign EXO license to the guest user), the mailbox hosted in EXO is different from the mailbox of the personal account. They are totally 2 separated mailboxes. And in fact I failed to assign EXO license to the guest.
So in this case Client Credential Flow applies to the AAD users, not the personal account.
UPDATE:
For personal account which uses Delegated permission (you have tried in Microsoft Graph Explorer), the authority endpoint is https://login.microsoftonline.com/commonm/oauth2/v2.0/authorize or https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize.
But when you use client credential flow with Application permission, you have to specify the tenant id in the request https://login.microsoftonline.com/{tenant id}/oauth2/v2.0/authorize.
Although your personal account is added into the tenant as a guest user, it doesn't have EXO license (based on test we are unable to assign EXO license to it), so it won't be hosted in O365.
That is why we can't use client credential flow with personal account.

How to grant access to read/write files on a sharepoint site via OAuth2

I am having trouble figuring out how to give an application read/write access to a sharepoint site.
Here's what I've done:
I made a sharepoint site
I created a microsoft azure application and authenticated users using OAuth2
I set delegated permissions on the app control panel to include Files.ReadWrite.All
I created a user who has read/write permissions to the sharepoint site and authenticated him with the app.
I have a program (in PHP) which has valid access_token and refresh_token, but when I try to upload files, I get a 403 error.
Client error: `PUT https://graph.microsoft.com/v1.0/drives/b!XxxxxxxxxxxxxxxxB/items/root:/StationMD%20Addl%20Enrollees%2020200508.xlsx:/content` resulted in a `403 Forbidden` response:
{
"error": {
"code": "accessDenied",
"message": "Access denied",
"innerError": {
Oddly enough, I was able to do this as the user who created the site, but not as a dummy user (who also has permission to read and write files)
So...
The problem seems to be app permissions. Is there a way to set application permissions for just that sharepoint site, or is Files.ReadWrite.All my only option? Can I use delegated permissions for a user who is not currently logged in, but did authenticate via OAuth2?
Some more information 5/16/20
I have two users. One is my regular account which I used to create the Sharepoint Site. The other is a "service account" used to upload files. Both users are able to edit/upload/delete files using the GUI. However, when using the API, the service user can't upload files, while the regular user can. I made sure that both users were members of the site. Using the Graph Explorer, I am able to see the drive.
https://graph.microsoft.com/v1.0/drives/b!XXXXxxxxx
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#drives/$entity",
"createdDateTime": "2020-04-26T11:19:17Z",
"description": "",
"id": "b!XXXXXxxxxx",
"lastModifiedDateTime": "2020-05-16T11:56:23Z",
"name": "Documents",
"webUrl": "https://xxxxxxx.sharepoint.com/sites/sftp/Shared%20Documents",
"driveType": "documentLibrary",
"createdBy": {
"user": {
"displayName": "System Account"
}
},
"owner": {
"group": {
"email": "xxxx#xxxxxx.onmicrosoft.com",
"id": "xxxxx-xx-xxxx-xxxx-xxxxxxxx",
"displayName": "sftp Owners"
}
},
"quota": {
"deleted": 2812850,
"remaining": 27487773843447,
"state": "normal",
"total": 27487790694400,
"used": 8848121
}
}
It's been quite a while since I worked with SharePoint, but I'll try to answer for the Azure AD related questions.
Oddly enough, I was able to do this as the user who created the site, but not as a dummy user (who also has permission to read and write files)
This is pretty odd. I would expect the call to work with the permissions the app has :\
Is there a way to set application permissions for just that sharepoint site, or is Files.ReadWrite.All my only option?
This one I am not sure about.
You can either give the app the delegated permission Files.ReadWrite.All which allows you to access/modify any file the user can, or you can give the application permission Files.ReadWrite.All which allows the app to access/modify any file in any site collection.
Can I use delegated permissions for a user who is not currently logged in, but did authenticate via OAuth2?
Short answer, yes.
If they authenticated at least once to your app, you got a refresh token for that user when they did.
Store that refresh token securely, and use it to get a new access token when needed.
Do note a couple things:
The refresh token itself will expire after some time (maybe 2 weeks?)
When you get a new token with that refresh token, you also get a new refresh token
Token refresh can fail due to some factors, your app should be ready for this
The user will have to authenticate again to provide a new refresh token
Based on my knowledge:
Can't limit to specific site in app permission level(limit in logic if need).
Use Application permission if want to skip user to log in.
Use permission in Microsoft Graph to access data in SharePoint if want to use graph api.

Microsoft OAuth 2.0 Client Credentials - Unable to retrieve permission to scope even with Granted Admin Consent on Azure

I am working on daemon service that retrieves the list of contacts from Microsoft Graph, that does not prompt Microsoft Authentication to the user. However, I'm not able to retrieve the permissions I set in Microsoft Azure even with granted admin consent.
I use Postman to generate a token with the following information.
https://login.microsoftonline.com/0835055b-8a00-4130-b07a-037430dd000d/oauth2/v2.0/token
The following json is the result I get
{
"token_type": "Bearer",
"scope": "profile openid email https://graph.microsoft.com/User.Read https://graph.microsoft.com/.default",
"expires_in": 3600,
"ext_expires_in": 3600,
"access_token": "{MY_TOKEN_HERE}"
}
Token is generated successfully but the token returned does not include the permission with granted admin consent I set in MS Azure at portal.azure.com > Registered App > API Permission.
I have Microsoft Graph - Users.Read and Contacts Read with green ticks on admin consent required. See image below:
Microsoft Azure - API Permission
And without the scope, I'm not able to retrieve the list of contacts with given token.
{
"error": {
"code": "ErrorAccessDenied",
"message": "Access is denied. Check credentials and try again.",
"innerError": {
"request-id": "63a24a22-34f7-42a1-a9d5-d7aaf598f4d6",
"date": "2019-07-01T02:59:15"
}
}
}
Is there anything I missed? I assumed when the admin has granted consents, I should be able to include the API in scopes to generate tokens.
I saw there is a similar issue here but there is no solutions that work for me:
Microsoft graph API: Unable to fetch users with the generated access token
Based on your screenshot, the contacts.read you granted is an application permission. And, you have only one delegated permission: user.read .
However, if you want to call the graph api : graph.microsoft.com/v1.0/me/contacts , you need to use delegated permission. It will represent the user. So you can get the information for "me". And based on the official documentation , you need to grant the Contacts.Read (Contacts.ReadWrite for writing) delegated permission.
If you want to use application permission, then you need to get access token with client credentials flow. Token got in this way can be used for application permission. And you should call the api as following : graph.microsoft.com/v1.0/users/{id | userPrincipalName}/contacts

Missing application permission scopes in Azure AD JWT token

Our application is managing Office 365 calendars without requiring explicit consent from users using the Office 365 Exchange Online API. This works as expected for existing installations at customers, but for new customer all requests to the Exchange Online API return a 401 Unauthorized response. We've narrowed this down to a roles claim missing in the JWT token.
Our question is if this roles claim missing in the JWT is a bug, or if this is by design. Perhaps someone from the Azure AD team can share their thoughts.
How to reproduce
In Azure AD, we've created an app registration. A public key has been uploaded in order to authenticate via ADAL4j. Next to this, some application permissions have been granted to Exchange Online:
We can successfully request a access token via ADAL4j, using https://outlook.office365.com/ as the Resource Id. The JWT looks something like this (removed some irrelevant information):
{
typ: "JWT",
alg: "RS256",
},
{
aud: "https://outlook.office365.com/",
iss: "https://sts.windows.net/yyy/",
app_displayname: "Test",
appid: "app-id",
ver: "1.0"
}
As can be seen, the property roles is missing in the JWT token.
When calling the Exchange Online API (e.g. https://outlook.office365.com/api/v2.0/users/user#tenant.onmicrosoft.com/calendars), sending the JWT as a Bearer token, a 401 Unauthorized is returned. The x-ms-diagnostics header mentions:
2000008;reason="The token contains no permissions, or permissions can not be understood.";error_category="invalid_grant"
Expected behaviour
When using an old application registration (created using the Azure Classic Portal, if I recall correctly), the JWT does contain a Roles property with the role we've requested:
{
typ: "JWT",
alg: "RS256",
},
{
aud: "https://outlook.office365.com/",
iss: "https://sts.windows.net/yyy/",
app_displayname: "Test",
appid: "app-id",
roles: [
"Calendars.ReadWrite.All"
],
ver: "1.0"
}
Using this JWT as a Bearer token when calling the Exchange Online API works as expected.
Workaround
We've worked around the issue by using the Grant Permissions button for the new app registration:
Now, the Calendars.ReadWrite.All role is present in the JWT, so everything is working as expected.
Question
In the past we've never had to execute the Grant Permissions action. Also, this page mentions (emphasis added):
As an administrator, you can also consent to an application's
delegated permissions on behalf of all the users in your tenant.
Administrative consent prevents the consent dialog from appearing for
every user in the tenant, and can be done in the Azure portal by users
with the administrator role. From the Settings page for your
application, click Required Permissions and click on the Grant
Permissions button
However, the "Read and write calendars in all mailboxes" permission is an application permission, and not a delegated permission, as mentioned at this page.
Is the workaround the correct solution to our missing Roles claim issue, or is something else wrong on the Azure AD side?
The workaround is the correct solution. When your application needs application permissions, an admin must consent by either clicking in the "grant permissions" button (as you did) or by passing admin_consent to the login URL. This applies to the AAD v1 application model. For the AAD v2 application model, there is a different way to get admin consent. More information here.
In the past (Azure Classic Portal), when you added application permissions to an application, the consent was granted automatically. This is not the case in the new Azure Portal.

How to authorize service to use Microsoft Graph user account without user interaction?

I want my server application to interact with it's own Excel files using Microsoft Graph. That is, the files belong to the application, not a particular user of the application.
I have registered an application with Azure ID and granted "Have full access to all files user can access" permission for Microsoft Graph.
I am trying to use OAuth Resource Owner Password Credentials Grant.
I can get an authorization token like this:
POST https://login.microsoftonline.com/common/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=password
&resource=https://graph.microsoft.com
&client_id=<ID of application registered with Azure AD>
&username=<Microsoft username>
&password=<password>&scope=Files.ReadWrite.All
But the response only indicates scope User.Read:
{
"token_type": "Bearer",
"scope": "User.Read",
"expires_in": "3600",
"ext_expires_in": "0",
"expires_on": "1494467388",
"not_before": "1494463488",
"resource": "https://graph.microsoft.com",
"access_token": "eyJ0e...",
"refresh_token": "AQAB..."
}
And when I try to list files in the account's One Drive, I do not get an error, but the response contains no items:
Request:
GET https://graph.microsoft.com/v1.0/me/drive/root/children
Authorization: bearer eyJ0e...
Response:
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('<account ID>')/drive/root/children",
"value": []
}
When I make the same request in Graph Explorer when logged in with same account the response includes all the items in that account's one drive root.
I understand that Microsoft Graph does not currently support application-only file access, when authorized via OAuth Client Credentials Grant (as per instructions for calling Microsoft Graph in a service), but since I am getting authorization for a particular user account (not just application) I would expect to get access to that users files.
Am I doing something wrong, or is file access not supported using Resource Owner Password Credentials Grant either?
If the latter, how can I achieve allowing my application to use user credentials to manipulate Excel files via Microsoft Graph without user interaction?
UPDATE:
I have had administrator permissions assigned to the account I am using, and re-set the application permissions for Microsoft Graph in the Azure Portal, but it still is not working for me.
Here are details of the account I am using:
Please try to click Grant Permissions(better using admin account) in "Required permissions" blade after granted "Have full access to all files user can access" permission for Microsoft Graph:
After that acquire token using Resource Owner Password flow , you will find Files.ReadWrite.All in scp claims . Then you could call microsoft graph api to list files .
Update
Here is the steps how i make the resource owner flow work :
register a native app , Add the "Have full access to all files user can access" delegate permission for Microsoft Graph(don't click grant permissions button as above picture shown) . using Resource Owner Password Credentials Grant and get the access token ,only find User.Read in scp claim :
POST https://login.microsoftonline.com/common/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=password&client_id=XXXXXXXXXX&resource=https://graph.microsoft.com/&username=XXXXXX&password=XXXXXXX
click grant permissions button as above picture shown , using Resource Owner Password Credentials Grant and get the access token ,you could find Files.ReadWrite.All User.Read in scp claim :
The issue with this is due to permissions on the Graph API. The reason is since you are logged in under a specific user for the Microsoft Graph Explorer - you are able to see everything ... due to the fact you have authenticated as a single person ... the reason you see nothing is because the app-only permissions does not work.

Resources