Azure AD B2C Custom policy, how to put application scopes in access token - azure-ad-b2c

Here's the configuration for the Azure AD B2C, create two applications: web and api. added two scopes read and write to the api scope. configure web application to web application. tested with the built-in user flows e.g. sign up sign in. run the flow for the web app, get the access token, scopes are in the token.
now create a custom policy to use multitenants to authenticate the users with Azure AD. created a custom signup/in policy. run the policy, got the access token by specifying the api scopes in the access token, however the return token does not contain the scope claims. my question is how to configure the custom policy to have the api scopes in the access token?

When you run the custom policy, it will only return an ID token rather than access token.
So your scope claims won't be included in the ID token.
You should refer to Request an access token in Azure Active Directory B2C.
After you have Added a web API application to your Azure Active Directory B2C tenant, use authorization code flow to get the access token.
GET https://<tenant-name>.b2clogin.com/tfp/<tenant-name>.onmicrosoft.com/<policy-name>/oauth2/v2.0/authorize?
client_id=<application-ID>
&nonce=anyRandomValue
&redirect_uri=https://jwt.ms
&scope=https://<tenant-name>.onmicrosoft.com/api/read
&response_type=code
The response with the authorization code should be similar to this example:
https://jwt.ms/?code=eyJraWQiOiJjcGltY29yZV8wOTI1MjAxNSIsInZlciI6IjEuMC...
After successfully receiving the authorization code, you can use it to request an access token:
POST <tenant-name>.onmicrosoft.com/oauth2/v2.0/token?p=<policy-name> HTTP/1.1
Host: <tenant-name>.b2clogin.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&client_id=<application-ID>
&scope=https://<tenant-name>.onmicrosoft.com/api/read
&code=eyJraWQiOiJjcGltY29yZV8wOTI1MjAxNSIsInZlciI6IjEuMC...
&redirect_uri=https://jwt.ms
&client_secret=2hMG2-_:y12n10vwH...
The response:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrN...",
"token_type": "Bearer",
"not_before": 1549647431,
"expires_in": 3600,
"expires_on": 1549651031,
"resource": "f2a76e08-93f2-4350-833c-965c02483b11",
"profile_info": "eyJ2ZXIiOiIxLjAiLCJ0aWQiOiJjNjRhNGY3ZC0zMDkxLTRjNzMtYTcyMi1hM2YwNjk0Z..."
}
See details here.

Related

Why kid is missing in Azure DevOps System.AccessToken?

System.AccessToken is can be used as a means of authenticating requests to the Azure DevOps REST APIs from within a pipeline
https://learn.microsoft.com/fr-fr/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml
In the header of this Access Token, the kid attribute is missing.
{
"typ": "JWT",
"alg": "RS256",
"x5t": "oOvcz5M_7p-HjIKlFXz93u_V0Zo"
}
Is there anyway to have in the Azure DevOps Access Token the kid field ?
To get kid field in Azure DevOps access token, you can generate OAuth token using Azure AD to call DevOps REST API.
I tried to reproduce the same in my environment via Postman and got below results:
I registered one Azure AD application and added Azure DevOps permissions like below:
Make sure to grant admin consent to the permissions like below:
Now, I generated access token using client credentials flow via Postman with below parameters:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
client_id: <appID>
grant_type:client_credentials
scope: https://app.vssps.visualstudio.com/.default
client_secret: <secret>
Response:
When I decoded the above access token in jwt.ms, I got kid field successfully like below:

Bypass Azure AD SAML

I got SAML setup on my AD and I have the private and certificate PEMs along with the certificate and metadata and I want to know if it's possible to bypass the login from API? I'm developing a Node.JS API which I want to make requests to the server behind the SAML login but I need to bypass it with what I got.
Maybe a way to generate a token from Azure's API (to use as bearer authorization) would work like how you do it with OAuth?
I'm not asking for any other solution (like whitelisting etc.), I just need it to be token / API based
To bypass azure ad , you may chose for the On behalf flow.
For single-page apps (SPAs), here we pass an access token to a middle-tier confidential client to perform OBO flows instead.
This will only work, if the respective permissions (scope grants) are
already granted when you try to get an access token using the
on-behalf of flow.
Here API A authenticates to the Microsoft identity platform token
issuance endpoint and requests a token to access API B.
https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token
with grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
client_id=xxx
&client_secret=saxx1s
&scope=https://graph.microsoft.com/user.read+offline_access
&requested_token_use=on_behalf_of
Here we need to expose api and here I am giving user.read
And exposed api
The response has access token , refresh token
{
"token_type": "Bearer",
"scope": "https://graph.microsoft.com/user.read",
"expires_in": 3269,
"ext_expires_in": 0,
"access_token": "xx",
"refresh_token": "xxxx"
}
Please check Microsoft identity platform and OAuth2.0 On-Behalf-Of flow - Microsoft Entra | Microsoft Learn
Reference : how-to-use-azure-ad-access-token-to-bypass-microsoft-online-login

Accessing AD secured Azure Function via AJAX

I have an Azure Function setup with a Web Trigger endpoint that I want to use as my backend for a React app. Without authentication setup, it works fine. When I setup App Service Authentication using AD, it works fine when I access directly via the browser (after authentication), but when I try to access from JS providing the Bearer token I get a 401.
const response = await axios.get(`${window.apiUrl}api/jobs`, {
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer ' + token.accessToken,
},
});
The client app is running on Azure and is registered as an Azure AD app. I am able to authenticate, query AD, and use MS Graph API successfully.
I am using the built-in Azure App Services AD authentication. I have the Client ID set as the same client ID as the previously mentioned Azure AD app, as well as the same Issuer Url.
Attempt to get session token:
const accessToken = await authProvider.getAccessToken();
const idToken = await authProvider.getIdToken();
const login = await axios.post(
'https://<appname>.azurewebsites.net/.auth/login/aad',
{ access_token: accessToken.accessToken },
{
headers: {
'Content-Type': 'application/json',
},
},
);
More Info
My aud claim is 00000003-0000-0000-c000-000000000000. In Azure Portal, my Azure Function is configured to use the same Azure AD App as my SPA. I am using MSAL.js for authentication in my SPA. I am requesting the User.Read and Directory.Read.All scopes.
Microsoft has published a how-to article entitled Advanced usage of authentication and authorization in Azure App Service. In the section on validating tokens from providers, it says:
In a client-directed sign-in, the application signs in the user to the
provider manually and then submits the authentication token to App
Service for validation (see Authentication flow). This validation
itself doesn't actually grant you access to the desired app resources,
but a successful validation will give you a session token that you can
use to access app resources.
So you need to get the session token to access app resources.
Request:
POST https://<appname>.azurewebsites.net/.auth/login/aad HTTP/1.1
Content-Type: application/json
{"id_token":"<token>","access_token":"<token>"}
Response:
{
"authenticationToken": "...",
"user": {
"userId": "sid:..."
}
}
Once you have this session token(authenticationToken), you can access protected app resources by adding the X-ZUMO-AUTH header to your HTTP requests
GET https://<appname>.azurewebsites.net/api/products/1
X-ZUMO-AUTH: <authenticationToken_value>
You cannot request a token for Microsoft Graph and use it to call your own API. The Audience "00000003-0000-0000-c000-000000000000" means "intended for Microsoft Graph".
In MSAL, when you request the token, you need to adjust the scopes. Delete User.Read, delete Directory.Read.All and add the "Application ID URI" with a /.default at the end of it. You can find the Application ID URI in the "Expose an API" blade of your application registration on portal.azure.com. Example: https://SaeedApp/.default
If you need to do both, you can only request an access token for one resource at a time. However, you can request as many scopes as you need for one resource (User.Read and Directory.Read.All are both scopes for the same resource).
So you'll need to make two sets of requests:
1) to get an access token with all the scopes you need for Microsoft Graph
2) to get an access token with all of the scopes you need for your API
The reason behind why: If I could take an access token that's intended for your API and call Microsoft Graph with it, then that would open up "replay" attacks where one Resource API is hacked and the hacker that controls one resource can now reply access tokens it receives from clients against all the other Resource APIs.

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).

B2C OAuth2 API error: Signature validation failed. Unable to resolve SecurityKeyIdentifier: 'SecurityKeyIdentifier

I have spent quite a bit of time getting our .NET MVC web application to integrate with Azure Active Directory B2C, with reasonable success, using a custom profile to allow users of other Azure Active Directories to log in to us.
Now I want to incorporate an API, roughly following this process:
https://github.com/Azure-Samples/active-directory-b2c-dotnet-webapp-and-webapi
I say roughly because I'm trying to fit this functionality into an application that's already been under development for several months.
I'm using Postman to hit this URL a and get a bearer token:
https://login.microsoftonline.com/ourtenant.onmicrosoft.com/oauth2/token
I use the grant_type=client_credentials, and the client_id and client_secret specified in Active Directory (added in the the "not-B2C" App registrations blade because apparently B2C doesn't yet support the client_credentials flow)
It appears to work fine and I get a response like this:
{
"token_type": "Bearer",
"expires_in": "3599",
"ext_expires_in": "0",
"expires_on": "1513906161",
"not_before": "1513902261",
"resource": "00000002-0000-0000-c000-000000000000",
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ing0Nzh4eU9wbHNNMUg3TlhrN1N4MTd4MXVwYyIsImtpZCI6Ing0Nzh4eU9wbHNNMUg3TlhrN1N4MTd4MXVwYyJ9.eyJhdWQiOiIwMDAwMDAwMi0wMDAwLTAwMDAtYzAwMC0wMDAwMDAwMDAwMDAiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83YjY1ZDY0NC0xNDM0LTQxZDQtYTFhMC04MjVlZjgwOTAyZDMvIiwiaWF0IjoxNTEzOTAyMjYxLCJuYmYiOjE1MTM5MDIyNjEsImV4cCI6MTUxMzkwNjE2MSwiYWlvIjoiWTJOZ1lKaTJWbkhKTXQwNUcrZmMrL2pFNmRQLzdRQT0iLCJhcHBpZCI6IjZkZmVkNGVkLTU2ZDktNDQ5Ny04M2JhLTkzOWJmNGI3OGUyNSIsImFwcGlkYWNyIjoiMSIsImlkcCI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzdiNjVkNjQ0LTE0MzQtNDFkNC1hMWEwLTgyNWVmODA5MDJkMy8iLCJvaWQiOiIxYTYxNGM5Yy00Nzc5LTQ2OTctOThjNC05OWNlZTJlZTVkY2IiLCJzdWIiOiIxYTYxNGM5Yy00Nzc5LTQ2OTctOThjNC05OWNlZTJlZTVkY2IiLCJ0ZW5hbnRfcmVnaW9uX3Njb3BlIjoiTkEiLCJ0aWQiOiI3YjY1ZDY0NC0xNDM0LTQxZDQtYTFhMC04MjVlZjgwOTAyZDMiLCJ1dGkiOiJjV2lzVmxDRDEwdW9Ra3BHbWRBdkFBIiwidmVyIjoiMS4wIn0.BiXHI5Sp0t2k_npJYdWjclSXGOMbxniR8G1ifOCNUuiNUZRFG6DsbIqkJEBXSFFUxQpvtGkBaI5oF2u4oJ5Ed37thh_gOLJ1TKBaubGusv7vgUVoIk9A5F8H_HeX57zyRR2XU3czdSC4uZC_XpVwV7eT4-Z4bNooL0WJi1ZNx6ZFBC4qktNf7yifc7-iAEEDTWj3clwA81RJwAe9YbUMI3q640sNg8QlrZDiKFzuEuFocHces0bAYSyfLu5cwDw2wvJwQzYEMahjQ3V7RXpqg-YktsUoSTkLOHm7QNrM2Pko8ZAye58O-nTv1gD5yYDZ8st74x4MUHhNZhaR44byjw"
}
When I use this bearer token in the Authorization header an API call, I get the response:
{"Message":"Authorization has been denied for this request."}
I switched on diagnostic tracing and found this in the output:
Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationMiddleware Error: 0 : Authentication failed
System.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException: IDX10500: Signature validation failed. Unable to resolve SecurityKeyIdentifier: 'SecurityKeyIdentifier
(
IsReadOnly = False,
Count = 2,
Clause[0] = X509ThumbprintKeyIdentifierClause(Hash = 0xC78EFCC723A996C3351FB35793B4B1D7BC75BA97),
Clause[1] = System.IdentityModel.Tokens.NamedKeySecurityKeyIdentifierClause
)
',
token: '{"typ":"JWT","alg":"RS256","x5t":"x478xyOplsM1H7NXk7Sx17x1upc","kid":"x478xyOplsM1H7NXk7Sx17x1upc"}.{"aud":"00000002-0000-0000-c000-000000000000","iss":"https://sts.windows.net/7b65d644-1434-41d4-a1a0-825ef80902d3/","iat":1513901664,"nbf":1513901664,"exp":1513905564,"aio":"Y2NgYPg7bbbRmu/aXjwejXZs73e5AgA=","appid":"6dfed4ed-56d9-4497-83ba-939bf4b78e25","appidacr":"1","idp":"https://sts.windows.net/7b65d644-1434-41d4-a1a0-825ef80902d3/","oid":"1a614c9c-4779-4697-98c4-99cee2ee5dcb","sub":"1a614c9c-4779-4697-98c4-99cee2ee5dcb","tenant_region_scope":"NA","tid":"7b65d644-1434-41d4-a1a0-825ef80902d3","uti":"5nMOpv6eok60JyzWwksuAA","ver":"1.0"}
RawData: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ing0Nzh4eU9wbHNNMUg3TlhrN1N4MTd4MXVwYyIsImtpZCI6Ing0Nzh4eU9wbHNNMUg3TlhrN1N4MTd4MXVwYyJ9.eyJhdWQiOiIwMDAwMDAwMi0wMDAwLTAwMDAtYzAwMC0wMDAwMDAwMDAwMDAiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83YjY1ZDY0NC0xNDM0LTQxZDQtYTFhMC04MjVlZjgwOTAyZDMvIiwiaWF0IjoxNTEzOTAxNjY0LCJuYmYiOjE1MTM5MDE2NjQsImV4cCI6MTUxMzkwNTU2NCwiYWlvIjoiWTJOZ1lQZzdiYmJSbXUvYVhqd2VqWFpzNzNlNUFnQT0iLCJhcHBpZCI6IjZkZmVkNGVkLTU2ZDktNDQ5Ny04M2JhLTkzOWJmNGI3OGUyNSIsImFwcGlkYWNyIjoiMSIsImlkcCI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzdiNjVkNjQ0LTE0MzQtNDFkNC1hMWEwLTgyNWVmODA5MDJkMy8iLCJvaWQiOiIxYTYxNGM5Yy00Nzc5LTQ2OTctOThjNC05OWNlZTJlZTVkY2IiLCJzdWIiOiIxYTYxNGM5Yy00Nzc5LTQ2OTctOThjNC05OWNlZTJlZTVkY2IiLCJ0ZW5hbnRfcmVnaW9uX3Njb3BlIjoiTkEiLCJ0aWQiOiI3YjY1ZDY0NC0xNDM0LTQxZDQtYTFhMC04MjVlZjgwOTAyZDMiLCJ1dGkiOiI1bk1PcHY2ZW9rNjBKeXpXd2tzdUFBIiwidmVyIjoiMS4wIn0.mPzogfR2ndo89P-qWIypdPjrrBb0uEOO0Fo-H164C4Rm21zFQpkwVSFe-NP4MtvMnB5fJdhzGxzPDACFHBiQi7k7ZZVGv5bWaIbhGlPmKCQ1j6XaweYp7pm66R-RIsokZvR87nJ4ZkvYJIkuxnXPjChC-3FjsLDf43FKcByDPvvJKpVj48JW9N79vq77HQ2w8bnq172zOUflxGbuC2nDiwzkgWQiFboL-H3LLUxHqZHeE46u7pDSOrE3DSY1F5aPqBq1IDCg6ELcBcaLN27509oAH2rghkvXjHWOs9Nw3tszVoza7CpEGV7fjtSGN874GV_vx-ziqIOf1EgSBPEH6Q'.
at System.IdentityModel.Tokens.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.JwtSecurityTokenHandler.ValidateToken(String securityToken, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
at Microsoft.Owin.Security.Jwt.JwtFormat.Unprotect(String protectedText)
at Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationHandler.<AuthenticateCoreAsync>d__0.MoveNext()
What am I missing?
The sample that you linked shows you how to secure and call a web API using Azure AD B2C. It seems like you are trying to obtain an Azure AD token, and then trying to use that to sign into an API that is secured using Azure AD B2C.
While client credentials is not supported in Azure AD B2C, it doesn't seem like you need the client credential flow. Client credential flow is used for an API to API call. If you want to call an API from the app that the users are signing into, you can use access tokens. Check out this document: https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-access-tokens

Resources