I have Office365 developer account with a couple of users. I am attempting to access their mail via a daemon app (i.e. headless) using the Microsoft Graph API.
I have verified that mailboxes exist for these users, but I'm getting an error:
ErrorNonExistentMailbox
No mailbox was found that includes the specified identity: `xx#yy.onmicrosoft.com`
I have previously obtained a token as a registered app (via apps.dev.microsoft.com) and included it with the following request URL:
https://graph.microsoft.com/v1.0/users/xx#yy.onmicrosoft.com/messages
Any guidance?
The token comes to me in the first operation like this:
{
"token_type": "Bearer",
"expires_in": 3599,
"ext_expires_in": 0,
"access_token": "eyJ0eXAiOiJKV1QiLCJub25jZSI6IkFRQUJBQUFBQUFEWDhHQ2k2SnM2U0s4MlRzRDJQYjdyblpfdTg3QjlwS2hFeG5ncFRpS1gwczVwY2k1YnpobHZDdTFlVi1uRlFkRk0yMHlJZ1MxdnlBbC1UUnBYdGNoakxIYkhMb21hT28wb3UxaTkxYnJKRENBQSIsImFsZyI6IlJTMjU2IiwieDV0IjoiaUJqTDFSY3F6aGl5NGZweEl4ZFpxb2hNMllrIiwia2lkIjoiaUJqTDFSY3F6aGl5NGZweEl4ZFpxb2hNMllrIn0.eyJhdWQiOiJodHRwczovL2dyYXBoLm1pY3Jvc29mdC5jb20iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83MmY5ODhiZi04NmYxLTQxYWYtOTFhYi0yZDdjZDAxMWRiNDcvIiwiaWF0IjoxNTI3NTM1NTU5LCJuYmYiOjE1Mjc1MzU1NTksImV4cCI6MTUyNzUzOTQ1OSwiYWlvIjoiWTJkZ1lHaTl6Zi9XNWVTTXlPZ0ppc3NxMThUSkFRQT0iLCJhcHBfZGlzcGxheW5hbWUiOiJJbmZvcm1hdGljYSBDQUkgaXoiLCJhcHBpZCI6IjQ0YTA4YWU3LTZiYzItNGEzMy04MmNiLTAzNzZhMzkxYjdhZCIsImFwcGlkYWNyIjoiMSIsImlkcCI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzcyZjk4OGJmLTg2ZjEtNDFhZi05MWFiLTJkN2NkMDExZGI0Ny8iLCJ0aWQiOiI3MmY5ODhiZi04NmYxLTQxYWYtOTFhYi0yZDdjZDAxMWRiNDciLCJ1dGkiOiI4RnlhZUZ0ZHVFdWRjcFBNYmswYkFBIiwidmVyIjoiMS4wIn0.DMpgiNfazeDd2KsEjHAggKAg3STMeK59ls9-whHaf-UEON_fvP45ay6PrqfAnwdqz-QKmX-8ikyiM8zuE5r8IGL1d7zYzJCnIECeQwtg8OKzYJPyDW7V0RAF0yePT2fg22luGhFz5yqzjSGlxhWauZmkmXq7JrGrO7fhzAUwoJh7XJrIOlfj098LPoTrmfAaOn36hBmmcQyuFNDhW5E6oXqZsyssJ5SKvaXN_w62IrXv2-nmIkZwyqcrVlCnX_Q1ytrFuc_xItL0FFe_5MbRiF-JIxdbeFNE8LR06mFWfEGHyJu01SJvgxE9500nVzok94qCX-r5lxN0WqVLOuTqEg"
}
I found what I was doing wrong. When requesting the token initially, I was using "common" as the tenant portion of the URL. Apparently, although legal, this does not give the appropriate permissions. The token I got back, when decoded, does not list any permissions, and this would have been a clue if only I had been a little more familiar with the JWT token content and format.
Once I changed the tenant portion of the URL to either the tenant GUID (retrieved from AD), or the fully qualified domain name (something like yy.onmicrosoft.com in my case), I got back a different token, one that includes a list of the Microsoft Graph permissions I had granted in the app registration portal.
Using this new token, I was able to retrieve the mail from the mailbox of any user in my domain. Thanks to Marc for pointing me in the right direction.
Just to clarify Aldu's answer, I did also have the same issue.
Obtaining the admin consent, it is fine to use /common as in step 2 of the instructions .
However, then requesting an access token from the microsoft auth server via client_credentials at https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token, I found I needed to replace tenant with a tenant ID. That changed my access token and allowed access to the resources.
Related
I'm trying to figure out how to use Microsoft Graph API in order to get information on an Azure AD B2C tenant's Policy Keys. It seems like it should be possible given the following documentation, but I keep getting an InvalidAuthenticationToken error.
Documentation link: https://learn.microsoft.com/en-us/azure/active-directory-b2c/microsoft-graph-get-started?tabs=app-reg-ga
What exactly do I need to do to call the endpoint shown in here (the /trustFramework/keySets endpoint)? https://learn.microsoft.com/en-us/graph/api/trustframework-list-keysets?view=graph-rest-beta&tabs=http
In postman, I'm simply firing off the get request with Basic auth using my Portal credentials. Am I going about this the right way?
I tried to reproduce the same in my environment. and received similar error *InvalidAuthenticationToken*
Then , I checked that I missed to give authorization header with bearer token ,
Which Is required parameter to query that as per : List keySets - Microsoft Graph beta | Microsoft Learn
Below are the scopes , I have given admin consent to. delegated and application permissions like openid offline_access profile User.ReadWrite.All TrustFrameworkKeySet.Read.All Policy.ReadWrite.TrustFramework AuditLog.Read.All
Do a get request at https://login.microsoftonline.com/xxxxxx063/oauth2/v2.0/token to receive a token for the application endpoint in postman with the scope for graph i.e; https://graph.microsoft.com/.default , for that particular tenant (here used tenantId of azure ad b2c).
Give required values like client_id, client_secret for client_credential flow
Then try to query graph using that token :
GET https://graph.microsoft.com/beta/trustFramework/keySets ,
Following above steps with correct values and granting admin consent , I could successfully query the keySets
We recently migrated from on premise exchange to Microsoft 365 and I'm wanting to turn on 2FA for all users (Enable security defaults). However this disables SMTP authentication which we have been using for sending mail from a distribution group address. (Not achievable via EWS as it doesn't have a physical mailbox)
From what I can see, the only method would be to set up a SMTP relay or send via Microsoft Graph.
I've tried going down the Microsoft Graph route, and here's what I've got so far.
Create application in Azure Active Directory > App Registrations
Add Mail.Send and User.Read.All (Application, not delegated) API Permissions and have granted Admin Consent.
Request token using the following
Generate auth code via https://login.microsoftonline.com/{AzureApi.TenantId}/oauth2/v2.0/authorize?response_type=code&client_id={AzureApi.ClientId}&redirect_uri={WebUtility.UrlEncode(RedirectUrl)}&scope=offline_access%20Mail.Send%20User.Read.All [using admin credentials]
Post request to https://login.microsoftonline.com/{AzureApi.TenantId}/oauth2/v2.0/token with the following request body.
{ "grant_type": "authorization_code", "client_id": "AzureApi.ClientId", "client_secret": "AzureApi.ClientSecret", "code": "insert auth code", "redirect_uri": "insert redirect URL" } to get the bearer token
Once I have the token, Now I perform a request to send some mail
https://graph.microsoft.com/v1.0/users/{fromAddress}/sendMail
This works when fromAddress is the email address of the user that requested the token, however when I try to send from a different address it gives this error {"error":{"code":"ErrorAccessDenied","message":"Access is denied. Check credentials and try again."}}
I even tried adding the SendAs permission to the token user for the mailbox I was trying to send as, but this didn't make any difference. Regardless - I want to be able to send as any user without delegating permissions to each mailbox.
Glad of any help. Thanks!!
The behavior you are getting is expected because you are using delegated permissions - authorization code flow. This flow will not allow your app to send email as any user except the user that is signed in/"requested the token".
In your case you can add the permissions as application permissions and then use Client Credentials flow with either secret or certificate(more secure). Your token request will look like the Get Access Token Section. Note that this means two things:
Your app will need to be secured on server side to protect the credentials.
An admin will need to consent to the permissions on Azure AD
Your app will be able to send emails as any user in your tenant so it is very sensitive.
With application permissions you only need Mail.Send
I have a JavaScript app that requires a user to login and hit a button then it runs a report, posts data to Excel, and sends an email. I want to automate this so a user does not have to log in and push a button.
I started with this project code: https://github.com/microsoftgraph/nodejs-apponlytoken-rest-sample
I followed the instructions and am able to get an access token but then my api call fails with 401 unauthorized. As a test, I am trying to send an email as myself and I have the Application type Mail.Send permission granted by the company admin.
I have spent many hours reading docs and blogs but have not found a solution. This document summarizes best what I am troubleshooting: https://blogs.technet.microsoft.com/sharepointdevelopersupport/2018/03/15/troubleshooting-assistance-with-microsoft-graph-api-development/.
The token I get back does not have any Roles in it as seen when I decode it with JWT. This is the only discrepancy I have found so far.
Any advise would be greatly appreciated. How can I ensure that my token has Roles defined or what else can I try? How is it that I can get a token successfully but can't use it for anything?
Thank you!
I am not a Node expert, so helping you out with a few pointers that might help.
Microsoft Graph has two types of permissions, Delegated and Application. So some things to know of and check..
Delegated permissions require a user to be present, they would show up in the scp claim in the access token. These are obtained by web applications using the implicit_grant flow, Authorization code grant or on-behalf-of flow (usually).
Application Permissions, require an admin to consent and will be provided to you in the roles claim in the access token. This requires, the app to obtain an access token via the client credentials grant. Note that, these are also present when the user is assigned a role as explained in this sample, but this scenario might not be applicable for in your case.
Does you app has the grant provided as you expect? You can check these via the Graph Explorer using the following two rest calls. There would be a OAuth2PermissionGrant entry with the expected role in it.
https://graph.microsoft.com/beta/OAuth2PermissionGrants
https://graph.microsoft.com/beta/kalyankrishna.net/OAuth2PermissionGrants
It'd help more if you can explain the flow that you have been using to obtain the access token. For example, the implicit_grant_flow does not work with application permissions.
In my case, the problem had to do with the endpoint and my tenant. I had been using the common tenant make api calls for tokens because that is what was listed in AzureAD for my app. I found that for the Application client permissions I must use my specific tenant id like 'https://login.microsoftonline.com/tenant_id/oauth2/token' when getting tokens with the proper roles.
I'm currently trying to authenticate an angular 7 app with ADFS 2016 (using angular-oauth2-oidc). So far, it works pretty well. When I access the application, I'm redirected to ADFS login page where I enter my credentials and gets tokens.
Now, when the app calls the a web API, it send the access token in the request headers. The access token returned by ADFS looks like this:
{
"aud": "microsoft:identityserver:xxx",
"iss": "http://xxx/adfs/services/trust",
"iat": 1554561406,
"nbf": 1554561406,
"exp": 1554565006,
"apptype": "Public",
"appid": "xxx",
"authmethod": "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport",
"auth_time": "2019-04-06T12:21:39.222Z",
"ver": "1.0",
"scp": "profile openid"
}
The issue is that the web API must know the ID of the user making the call (as there are some permissions defined at application level). I saw here that I can add claims to the access token, which I did. However, when reading articles on Internet, I read that apparently the access token is not supposed to be used to identify the user, so I don't know if it's correct to add a claim used by the web API to identify the user.
However, I read here that
The only user information the Access Token possesses is the user ID,
located in the sub claim.
But by default, ADFS does not provide a "sub" token. Is it correct if I add a "sub" claim by myself that contains the email address of the user or do I have to proceed in a different way?
Yes, access token is not supposed to be used to identify the user. But if you can get a user identifier into the access token and it's all you need to know about a user, then it's probably the easiest way. But it's implementation-specific for your auth server.
The standard way is to ask for the profile scope (which you did) and get information from the userinfo endpoint (see OpenID Connect RFC). If you use this way, you may want to cache the response, so you don't need to make this HTTP call on every client request.
If your backend API is used just by the Angular application and is not supposed to be called by third parties, you could also consider using the backend as the OAuth2 client which receives an authorization code and exchanges it for an ID token. Then, it can save the user's identity (read from the ID token) in a session and issue a cookie identifying the session. So there would be no need for the frontend to send a token on each request. This would make the backend stateful, but it might be easier to implement.
Using Postman, I am trying to retrieve the last emails received in my Outlook mailbox.
To achieve this, I have declared my app in the App Portal. Then, I can do a GET request to get a token from the endpoint:
https://login.microsoftonline.com/[tenantId]/oauth2/token
Next, I try to use the token I received to perform a request at
https://graph.microsoft.com/v1.0/me/mailfolders/inbox/messages
The problem is that the API returns:
{
"code": "NoPermissionsInAccessToken",
"message": "The token contains no permissions, or permissions can not be understood.",
}
In the permissions of my app, I have authorized every action related to reading emails. Am I missing something?
I was actually missing admin approval for the scopes (read.mail in my case). In a App-Only usage, you need to get approval from admin. To do so, admin must use this url:
https://login.microsoftonline.com/common/adminconsent?client_id=[your_client_id]&state=[random_string]&redirect_uri=http://localhost/
Admin will be prompt to approve permissions.
This sounds like you forgot to "Grant permission" (it happens to the best of us :P).
Grant the permission for your tenant. The easiest way is through https://portal.azure.com -> Azure AD -> App Registrations -> Your App -> Settings -> Required permissions -> Button Grant Access.)
Related to this answer
It also helps to take the token, and paste in into https://jwt.ms which will show you all the data in the token (and should also show the claims about the granted permissions).
The exception is the API to find meeting times or send mail, which applies to only Office 365 mailboxes (on Azure AD) and not to Microsoft accounts.
For simplicity of reference, the rest of this article uses Outlook.com to include these Microsoft account domains.
https://learn.microsoft.com/en-us/previous-versions/office/office-365-api/api/version-2.0/calendar-rest-operations