How to pass through MFA auth for on-behalf-of flow in Outlook-addin - azure

We are implementing SSO in outlook adding. Let's presume, there is two tenants for simplicity.
Home Tenant
Customer Tenant
We have added Customer Tenant's user as guest in Home Tenant. Hence, we can manage guest user with certain accessibility.
We have created multi-tenant app registration in Home Tenant to get user consent and authentication from Customer Tenant. And we have put application (client) id and application id uri in Outlook-addin xml as show in below.
</OfficeApp>
...
...
<WebApplicationInfo>
<Id>928cd908-multi-tenant-application-id</Id>
<Resource>api://company-domain.com/928cd908-multi-tenant-application-id</Resource>
<Scopes>
<Scope>Files.Read.All</Scope>
<Scope>offline_access</Scope>
<Scope>openid</Scope>
<Scope>profile</Scope>
</Scopes>
</WebApplicationInfo>
</VersionOverrides>
</VersionOverrides>
</OfficeApp>
Let's assume consent has been done on before,
In order to do SSO, I'm following steps,
Executing OfficeJs.auth.getAccessToken and get access token which is issued by Customer Tenant.
Passing Customer Tenant token to application server through api call.
Exchanging Customer into Home Tenant AD token by using on-behalf-of (OBO) request.
POST /2e627697-home-tenant-ad-id/oauth2/v2.0/token HTTP/1.1
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
&client_id=928cd908-multi-tenant-application-id
&client_secret=.m_7Qxxx
&assertion=eyJ0eXAiOiJKV1Q-customer-tenant-ad-token
&requested_token_use=on_behalf_of
&scope=api://company-domain.com/928cd908-multi-tenant-application-id/access_as_user openid
Received accesstoken from Home Tenant AD, since Customer Tenant's user exist in Home Tenant AD as guest.
Allowing user to access server resource.
Send response to Outlook-addin from server.
So far all good, The problem is,
If I enable Per-User MFA for the guest user, then I started to get below error when I do on-behalf-of request from server.
{
"error": "invalid_grant",
"error_description": "AADSTS50076: Due to a configuration change made by your administrator, or because you moved to a new location, you must use multi-factor authentication to access '928cd908-multi-tenant-application-id'.\r\nTrace ID: 706875e8-bfe7-44b8-a9f9-402b1f4a2201\r\nCorrelation ID: 8a4bde4c-9222-4aa0-a4d6-cd0748ff3816\r\nTimestamp: 2022-06-29 18:00:31Z",
"error_codes": [
50076
],
"timestamp": "2022-06-29 18:00:31Z",
"trace_id": "706875e8-bfe7-44b8-a9f9-402b1f4a2201",
"correlation_id": "8a4bde4c-9222-4aa0-a4d6-cd0748ff3816",
"error_uri": "https://login.microsoftonline.com/error?code=50076",
"suberror": "basic_action"
}
There is two options to make user interations,
OPTION 1
Get Customer Tenant token by calling Officejs.auth.getAccessToken.
Send Customer Tenant token to server.
Create on-behalf-of request from server.
Get 50076 error and then send error response to addin.
Open authorize popup for MFA from addin by using following url. This will do MFA against Home Tenat AD.
const authorizeUrl =
`https://login.microsoftonline.com/2e627697-home-tenant-ad-id/oauth2/v2.0/authorize?client_id=928cd908-multi-tenant-application-id
&response_type=id_token+token
&redirect_uri=https://company-domain.com/928cd908-multi-tenant-application-id
&scope=openid api://company-domain.com/928cd908-multi-tenant-application-id/access_as_user
&response_mode=fragment
&state=12345
&nonce=678910`
window.open(authorizeUrl)
Close authorize popup on after MFA completed.
Call Officejs.auth.getAccessToken to get Customer Tenant token (2e627697-home-tenant-ad-id MFA Passed).
Pass Customer Tenant token with MFA to server.
Create on-behalf-of request from server with Customer Tenant token (2e627697-home-tenant-ad-id MFA Passed).
Get Home Tenant AD token successfully.
OPTION 2
Get Customer Tenant token by calling Officejs.auth.getAccessToken.
Send Customer Tenant token to server.
Create on-behalf-of request from server.
Get 50076 error and then send error response to addin.
Call Officejs.auth.getAccessToken to get Customer Tenant token (MFA Passed). Here I don't have clear understand to force OfficeJs to get Customer Tenant token on after passing MFA for 2e627697-home-tenant-ad-id.
Questions
Option 1 is getting MFA through window.open from Outlook-addin code, instead of getting MFA through OfficeJs. is this correct way?
If Option 2 is correct way, how do I force OfficeJs to perform MFA for 2e627697-home-tenant-ad-id?

To ensure MFA is satisfied use the message returned by MS Graph as the Office.AuthOptions.authChallenge value. E.g.
let exchangeResponse = await getGraphToken(bootstrapToken);
await Officejs.auth.getAccessToken({
authChallenge: exchangeResponse.claims
});

Related

For IMAP.AccessAsUser.All Scope ADSTS65001: The user or administrator has not consented to use the application

In my java web application I want to get access to user's mailbox by using jakarta mail. For that purpose I followed https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth for OAuth2 authorization code flow.
On Azure port I setup my app and added API permissions as below
Now user is redirecting to below authorize endpoint:
https://login.microsoftonline.com/5426ee07-9b73-4a9e-8075-395ab439c6fa/oauth2/v2.0/authorize?client_id=b6067ad9-7195-430b-a35d-97b7aa7beb8f&response_type=code&redirect_uri=http://localhost:8080/callback/microsoft&response_mode=query&scope=offline_access%20https%3A%2F%2Fgraph.microsoft.com%2FIMAP.AccessAsUser.All%20https%3A%2F%2Fgraph.microsoft.com%2FSMTP.Send
After entering credentials and accepting the consent redirect_uri gets hit with auth code. Based on that auth code I formed token endpoint URL and hitting it from server, the token endpoint is as follow:
URL: https://login.microsoftonline.com/5426ee07-9b73-4a9e-8075-395ab439c6fa/oauth2/v2.0/token
Form Data:
client_id=b6067ad9-7195-430b-a35d-97b7aa7beb8f
scope=offline_access%20https%3A%2F%2Foutlook.office.com%2FIMAP.AccessAsUser.All
redirect_uri=http://localhost:8080/callback/microsoft
grant_type=authorization_code
client_secret=QUs8Q~aboLBiopTezMTKwzQjIwWsFFXjc2kCRaRs (I know I have shared the secret)
code={code received from authorize end point}
Response to this post request comes as:
{"error":"invalid_grant","error_description":"AADSTS65001: The user or administrator has not consented to use the application with ID 'b6067ad9-7195-430b-a35d-97b7aa7beb8f' named 'Email Connector'. Send an interactive authorization request for this user and resource.\r\nTrace ID: dc008ced-e23f-4919-bd45-b7ae7c68b000\r\nCorrelation ID: 9b6ede03-3c05-4a78-8975-036a3cb20773\r\nTimestamp: 2022-06-07 19:51:30Z","error_codes":[65001],"timestamp":"2022-06-07 19:51:30Z","trace_id":"dc008ced-e23f-4919-bd45-b7ae7c68b000","correlation_id":"9b6ede03-3c05-4a78-8975-036a3cb20773","suberror":"consent_required"}
Here, I don't understand why the error is saying The user or administrator has not consented to use the application, user has accepted the consent after entering credentials on authorize end point. Event more If we look at the screenshot above admin has already given grant to access the directory.
I tried to reproduce the same scenario in my environment and got the same error as below:
To resolve the error, please check the authorize endpoint you are using to get the code.
Avoid using Microsoft graph API scopes while getting the code.
Replace it with the scope you are using to get access token like below:
https://login.microsoftonline.com/Your_TenantID/oauth2/v2.0/authorize?
client_id=Your_ClientID
&response_type=code
&redirect_uri=http://localhost:8080/callback/microsoft
&response_mode=query
&scope= offline_access https://outlook.office.com/IMAP.AccessAsUser.All
&state=12345
Get the code from the above authorization endpoint.
I got the access token successfully after modifying the endpoint like below:
To validate the access token decode it in jwt.io and check the aud and scp claims like below:

How to implement "Organizational account" authentication in Excel on server side?

I have Java application which provides some reports in plain HTML.
I want to secure these reports with Microsoft SSO (OAuth).
I was able to do this in browser - I created new "AppRegistration" in Azure AD, get client_id, client_secret, Oauth 2 authentication_url, configured correct redirect_uri in this application and implemented Oauth flow in browser - it works as expected.
But users don't want to check reports in browser, they want to process them in Excel 2019.
It has "Organizational account" authentication.
I believe, that it uses the same OAuth 2 flow.
So, I added WWW-Authenticate: Bearer authorization_url="https://login.microsoftonline.com/256be541-f611-4412-975e-cb56ee6fb03b/oauth2/v2.0/authorize"
I'm trying to access URL like: https://localhost:8443/report/1
Now Excel asks me to enter login and password, but after successfull authentication the error is shown:
invalid_resource: AADSTS500011: The resource principal named https://localhost:8443 was not found in the tenant named 256be541-f611-4412-975e-cb56ee6fb03b. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant.
Trace ID: 57324bfe-ab46-4c2e-9128-a336aa287e00
Correlation ID: d9c4c732-76cc-4659-9d8a-d27abec617d3
Timestamp: 2021-02-02 16:50:13Z.
https://localhost:8443 - is address of my application and this address is included to redirect_uri in App registration.
But I don't think that mentioned "resource principal" is about redirect_uri.
So, how can I create "resource" principal in Azure AD and give it name "https://localhost:8443" ?
In fact, this is the case. According to your error message, it says that the resource body of https://localhost:8443 cannot be found, which means that you set the scope to: https://localhost:8443 when requesting an access token.
However, you only set it to redirect_uri at the beginning, and did not set the Application ID URI to: https://localhost:8443 in the Expose API tab, so when you request the resource, the error message will report that it cannot be found the resource.
By the way, scope is different from redirect_uri. The scope puts the resource you want to access, while redirect_uri is just the callback url that is not the resource you want to access. This is why you still get an error when setting the url in redirect_uri.

Why aren't the application permissions being added to my MS Graph token?

I'm trying to get a subscription created with the callRecord resource (https://learn.microsoft.com/en-us/graph/api/subscription-post-subscriptions?view=graph-rest-beta&tabs=http)
In the app registration section of the Azure portal, I've created a multi-tenant app with a client secret. That app has permissions for application-level "CallRecords.Read.All" as well as the default delegated "User.Read". The statuses also have a green checkbox for being granted against my organization by an admin.
I am able to get an access token with the following HTTP POST request to https://login.microsoftonline.com/common/oauth2/v2.0/token:
grant_type:authorization_code
scope:https://graph.microsoft.com/.default
client_secret:<client_secret>
client_id:<client_id>
code:<code>
redirect_uri:http://localhost:3000
However, that token is not able to generate a subscription to my callRecord resource. I get a response with this message: "Operation: Create; Exception: [Status Code: Forbidden; Reason: The request is not authorized for this user or application.]"
The message suggests that the app has not been granted admin-level authorization, but in fact it has. This used to work for me. I'm wondering if there has been a regression on the MS Graph side.
Further, when I examine the JWT, I see that the scope is "User.Read profile openid email". There is no mention of the application-level permission (specifically, CallRecords.Read.All)
Thanks.
Because when you use the auth code flow, just the Delegated permission will take effect. So even if you grant the Application permission, the token you got will not include the permission.
From the doc, to call this API Get callRecord, just the Application permission is supported.
To get the token which include the permission, your option is to use the client credential flow.
Note: You need to use <tenant-id> instead of common in this flow.
POST https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token
client_id=xxxxxxx
&scope=https://graph.microsoft.com/.default
&client_secret=xxxxxxx
&grant_type=client_credentials
Decode the token in https://jwt.io, the roles includes the CallRecords.Read.All permission:

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.

We do not recognize this tenant ID 21f3be0a-713c-4b7f-ab75-3cc5ca25846. Please double-check the tenant ID and try again.",

I want to make multi-tenant application on https://apps.dev.microsoft.com.
This work fine with single tenant but
My process for making multi-tenant app ----
LogIn with Other Tenant and appear consent window and got code successfully which code I passed for getting token
Token successfully generated:
Call graph API for office 365
But error genrate --
"We do not recognize this tenant ID 21f3be0a-xx-4b7f-ab05-xxxxx6. Please double-check the tenant ID and try again.
]2
When access data for office 365 report why this tenant id not recognized error raised?
Need to edit in manifest ?
My App on https://apps.dev.microsoft.com. Have Three 3 Microsoft Graph Permissions
Delegated Permissions - user.read , Reports.Read.All (Admin Only)
You can check whether a tenant is valid or invalid using the Well Known OpenId Configuration endpoint:
https://login.microsoftonline.com/<tenant>/.well-known/openid-configuration
Doing this for your tenant id gives this result:
https://login.microsoftonline.com/21f3be0a-713c-4b7f-ab75-3cc5ca25846/.well-known/openid-configuration
error: "invalid_tenant"
error_description: "AADSTS90002: Tenant
21f3be0a-713c-4b7f-ab75-3cc5ca25846 not found. This may happen if
there are no active subscriptions for the tenant. Check with your
subscription administrator.\r\nTrace ID:
bdd561b1-bc36-44ce-b3ab-33e53fda0100\r\nCorrelation ID:
c6ce0c0d-6550-4f5f-a398-a82f085e28c1\r\nTimestamp: 2017-11-01
17:38:31Z"
So simply stated, the tenant id that you are using is not valid for the AAD Worldwide endpoint.
Is it possible you are getting a token for a different instance of AAD? Can you share the contents of your Access Token (removing any sensitive details)?
I did some mistakes thats why this Error raised then need not do anything becoz this Portal app by default Multitenant and send request to common platform ..
Login with other tenant get code
https://login.microsoftonline.com/common/oauth2/authorize?
client_id=XXXXX-XXX
&response_type=code
&redirect_uri=http://localhost:49876
&response_mode=query
&scope=Reports.Read.All
&prompt=consent
&state=12345
then get token
https://login.microsoftonline.com/common/oauth2/token
client_id =XXX
scope =
code= which you get
grant_type=
client_secret=
after get token use for Graph API & get office365 data for -https://graph.microsoft.com/beta/reports/getMailboxUsageMailboxCounts(period='D7')

Resources