azure ad b2c custom policy - ROPC - azure

as part of the "SignUpOrSignIn" userJourney, i would like to call MyAPI and run some business logic.
to accomplish that, i added a couple of orchestration steps to the "SignUpOrSignIn" userJourney after the user has successfully authenticated.
Step 1) make a simple http request to azure ad b2c token endpoint and get an access token to MyAPI
https://<tenant-name>.b2clogin.com/<tenant-name>.onmicrosoft.com/B2C_1_ROPC_Auth/oauth2/v2.0/token
username <user-name>
password <password>
grant_type password
scope <MyAPI Scope>
client_id <application-id of the client that has been granted access to MyAPI>
response_type token id_token
Step 2) use the access token from the previous response to call MyAPI.
I got this working. However, in Step (1), when i make the http request to get the access token, i am having to provide a hard coded username and password in the http request.
Is there a way to use the values of the username and password that were entered as part of "SignUpOrSignIn" to get the access token?
Thanks,
-Sashi.

Related

Azure B2C ROPC flow (Web Api): Let Users reset their password

I've just found out how to update the password of a signed-in user via Graph api in my Web Api. Now I need to send a user that is not signed-in an email with a new password so they can sign in and change their password.
How can I send an email to an User without a signed-in user (so no token)? And if that's not possible, how can I give users the ability to change their forgotten passwords in my Web API (ROPC flow)? Thank you very much!
I tried to reproduce the same in my environment and got below results:
I registered one application and added API permission like below:
Now I generated one access token using ROPC flow via Postman like below:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
client_id: <appID>
grant_type:password
scope: https://graph.microsoft.com/Directory.AccessAsUser.All
username:devi#sritenantb2c.onmicrosoft.com↵
password: xxxxxxxxxxx
client_secret:<secret>
Response:
Now, I used the above token in below graph call and changed password successfully like below:
POST https://graph.microsoft.com/beta/users/<userID>/changePassword
Content-type: application/json
{
"currentPassword": "xxxxxxxxxx",
"newPassword": "yyyyyyyyyy"
}
Response:
Reference:
How to reset and change the password using Microsoft graph API of Azure AD B2C users by AmanpreetSingh-MSFT

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

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
});

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:

Azure AD me profile

I have error "Access token validation failure. Invalid audience."
For application set api permissions to offline_access, openid, profile, User.Read.
User start auth, go to MS auth site, ask about login, password and grand.
After exchange code to access token i well receive
{'token_type': 'Bearer', 'scope': 'offline_access openid profile User.Read', 'expires_in': '3906', 'ext_expires_in': '3906', 'expires_on': '1653988700', 'not_before': '1653984493', 'resource': 'my_azure_client_id', ....}
Then i try get profile for current user with this access token.
As result i have error "Access token validation failure. Invalid audience."
Help pease)
UPDATE
Configured permissions
The reason behind getting that error is because your token has wrong audience.
Please check what token you are using to call Graph API.
I tried to reproduce the same in my environment.
If you are using ID Token instead of Access Token, you may get error like below:
To know whether you are giving access token or id token, decode it in JSON Web Tokens - jwt.io.
For access token, aud claim will be "https://graph.microsoft.com" or "00000003-0000-0000-c000-000000000000"
For id token, aud claim will be "your_app_client_id"
Choose the access token carefully with aud as "https://graph.microsoft.com" while calling Microsoft Graph API:
To get profile for current user, you can make use of below query:
GET https://graph.microsoft.com/v1.0/me
I got the profile successfully using access token like below:
Replace your scope with https://graph.microsoft.com/.default while generating access token to avoid confusion.
Reference:
oauth 2.0 - Microsoft Graph API: Access token validation failure. Invalid audience - Stack Overflow
UPDATE:
In order to get authorization code, make the request by changing scope like below:
https://login.microsoftonline.com/your_tenant_id/oauth2/v2.0/authorize?
client_id=your_client_id
&response_type=code
&redirect_uri=xxxxxx
&response_mode=query
&scope=https://graph.microsoft.com/.default
&state=12345

Wrong access_token from AAD with OAuth2 flow

I am making OAuth 2.0 auth code authentication flow with multi-tenant application.
Here is my authorize url:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=my_id&prompt=consent&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauthorize&response_type=code&scope=openid+offline_access&state=17
It goes fine and I receive auth_code. Then I make request with this auth_code to token_url and receive a lot of information, like:
token_type
scope
id_token
access_token
refresh_token
expires_at
ext_expires_in
Seems fine to me, but when I make request on API with access_token like:
https://management.azure.com/subscriptions/my_sub_id/locations?api-version=2016-06-01
with headers:
Content-Type:
- application/json
Authorization:
- Bearer EwBQA8l6BAAURSN/FHlDW5xN74t6GzbtsBBeBUYAAV1IHgHb4dOWblzfd/YsSuFicAMDYbua17QivnAT9/pIaeKAg3uKsK5VGqWLzjMOUQrCpd7R1RAM6RkzI0u8e4rpO7DISG7qLso5H5+U1jb+38/j1urcwlXMMxhy83ZXmdpkLXpZV+vcOV...
It responds with 401 error
body:
encoding: UTF-8
string: '{"error":{"code":"InvalidAuthenticationToken","message":"The access token is invalid."}}'
To be honest I think something wrong with my access_token. It seems not like JWT for me. Documentation says it looks like:
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCEV1Q..."
But my access_token looks like:
"access_token": "EwBYA8l6BAAURSN/FHlDW5xN74t6GzbtsBBeBUYAAZDe7JE/MPLoAi+Fr+1Xxq5eBe5N9l8Q+c4QjkY5PGEzRnBpPe7+v6h+PLdh1cceBQx+/JsB2QCrYSCt7x/zGsQAhwoY/"
Is it fine?
Here is my permissions for application:
Permissions
The main issue you have here is that you have only asked for an access token for the scopes openid offline_access. The resulting access token will be for Microsoft Graph (https://graph.microsoft.com), not for the Azure REST API (https://management.azure.com).
To indicate you would like a token for a given API, the scope parameter in your authorization request should include the delegated permission you would like the app to have for the API. In the case of Azure REST API, there's only one delegated permission: user_impersonation. The identifier URI for the Azure REST API is https://management.azure.com, so the scope value you want to use is:
openid offline_access https://management.azure.com/user_impersonation
Two more important notes:
As you've discovered, you will not always be issued an access token as a JWT which you can decode peek at. The format of the access token is an agreement between the service which issued the token (Azure AD or Microsoft Accounts, in this case), and the service for which the token was issued (Microsoft Graph, in this example).
You should not always include prompt=consent. prompt=consent should only be used if you have already tried signing in the user without the user needs to be re-prompted for consent for a new permission.
If you simply include the required scopes in the scopes parameter, the Microsoft Identity platform will take care of figuring out if it needs to prompt for consent or not. If you always include prompt=consent, you will find that many organizations will be blocked from accessing your app, because they've disabled the ability for users to grant consent themselves (and this parameter specifically states that you require the user to be prompted again).

Resources