Azure AD B2C breaks OIDC spec - azure

I've configured a custom policy with AAD B2C IEF per this link and am now trying to integrate it into API Gateway as a JWT authorizer per this link.
However, attempting to configure the authorizer throws an error
error updating API Gateway v2 authorizer
BadRequestException
Caught exception when connecting to https://tenant-domain.b2clogin.com/tenant-id-here/v2.0/.well-known/openid-configuration for issuer https://tenant-domain.b2clogin.com/tenant-id-here/v2.0/.
Please try again later.
Error:
Invalid issuer:
https://tenant-domain.b2clogin.com/tenant-id-here/v2.0/.
Issuer must have a valid discovery endpoint ended with '/.well-known/openid-configuration
The actual discovery endpoint is https://tenant-domain.b2clogin.com/tenant-domain.onmicrosoft.com/b2c_1a_signup_signin/v2.0/.well-known/openid-configuration, however, that returns a doc as below, which has a different issuer than the discovery URL.
{
"issuer": "https://tenant-domain.b2clogin.com/tenant-id-here/v2.0/",
"authorization_endpoint": "https://tenant-domain.b2clogin.com/tenant-domain.onmicrosoft.com/b2c_1a_signup_signin/oauth2/v2.0/authorize",
"token_endpoint": "https://tenant-domain.b2clogin.com/tenant-domain.onmicrosoft.com/b2c_1a_signup_signin/oauth2/v2.0/token",
"end_session_endpoint": "https://tenant-domain.b2clogin.com/tenant-domain.onmicrosoft.com/b2c_1a_signup_signin/oauth2/v2.0/logout",
"jwks_uri": "https://tenant-domain.b2clogin.com/tenant-domain.onmicrosoft.com/b2c_1a_signup_signin/discovery/v2.0/keys",
"response_modes_supported": [
"query",
"fragment",
"form_post"
],
"response_types_supported": [
"code",
"code id_token",
"code token",
"code id_token token",
"id_token",
"id_token token",
"token",
"token id_token"
],
"scopes_supported": [
"openid"
],
"subject_types_supported": [
"pairwise"
],
"id_token_signing_alg_values_supported": [
"RS256"
],
"token_endpoint_auth_methods_supported": [
"client_secret_post",
"client_secret_basic"
],
"claims_supported": [
"name",
"given_name",
"family_name",
"email",
"sub",
"tid",
"iss",
"iat",
"exp",
"aud",
"acr",
"nonce",
"auth_time"
]
}
Looking at this issue and the spec, it looks like AAD is not spec compliant.
Is there any way to get this to work or do I have to move to a spec-compliant OIDC provider?

Please try to configure issuer URL including tfp for token compatibility.
For more details see: Token compatibility
which says:
Note : iss claim i.e; issuer identifies tenant of azure ad b2c that
issued the token. Usually the value is some thing like this
:https://<domain>/{B2C tenant GUID}/v2.0/
But If your application or library needs Azure AD B2C to be
compliant with the OpenID Connect Discovery 1.0 spec, use this
https://<domain>/tfp/{B2C tenant GUID}/{Policy ID}/v2.0/ as it
includes IDs for both the Azure AD B2C tenant and the user flow that
was used in the token request.
For example:
“issuer” : “https://your-tenant-name.b2clogin.com/tfp/c5b2xxxxxxxxx0-8axxxxxx3d3b/B2C_1A_signin/v2.0/”
or
https://{tenantID}.b2clogin.com/tfp/{tenantID}/{policy-name}/v2.0/
References:
Configure the Azure Active Directory B2C provider manually - Power
Apps | Microsoft Docs
AzureAD Authentication with AWS API Gateway v2 JWT Authorizers |
rayterrill.com

In addition to the answer by #kavyasaraboju-MT, if you're using custom policies, you must set the IssuanceClaimPattern to AuthorityWithTfp in the JwtIssuer Tehcnical Profile per these docs.
e.g. using the LocalAccounts pack in active-directory-b2c-custom-policy-starterpack, add the element <Item Key="IssuanceClaimPattern">AuthorityWithTfp</Item> to the <Metadata> element

Related

How to differentiate between 'local' and 'social' user types after login via Azure AD B2C?

I have a Node.js/Express application that uses Azure AD B2C for authentication.
Currently, two types of users can login:
Local accounts - EXTERNAL users in the Azure AD B2C tenant
Social accounts - INTERNAL users from a single Azure AD tenant
Desired Behaviour
I want to display different frontend user interface elements based on the type of user that has logged in.
Local accounts will be able to call endpoints relevant to EXTERNAL users
Social accounts will be able to call endpoints relevant to INTERNAL users
What is the recommended way to differentiate between who logged in?
I can see that the authToken object that is returned from msal-node's acquireTokenByCode() method is different depending on which type of user logged in.
Should I use a property from the authToken object to differentiate between user types?
If so, which property is the best one to use?
And, so that I can perform relevant UI actions accordingly, is it acceptable to store this value as a cookie in the browser (eg 'user_type': 'EXTERNAL' or 'user_type': 'INTERNAL') using something like js-cookie?
Below is a redacted version of the authToken object that is returned after login.
All values are the same for different users types, unless otherwise specified.
Notably, these two properties are only present after a 'social account' login:
idp_access_token
idp
{
"authority": "https://<my-azure-ad-b2c-tenant>.b2clogin.com/<my-azure-ad-b2c-tenant>.onmicrosoft.com/b2c_1_signin1/",
"uniqueId": "*******", // <-- different for each user, this is the object id of the user as represented in the Azure AD B2C tenant
"tenantId": "",
"scopes":
[
"https://<my-azure-ad-b2c-tenant>.onmicrosoft.com/my-web-app-api/tasks.write", // <-- these are the api permissions granted to my web app
"https://<my-azure-ad-b2c-tenant>.onmicrosoft.com/my-web-app-api/tasks.read" // <-- these are the api permissions granted to my web app
],
"account":
{
"homeAccountId": "***uniqueId***-b2c_1_signin1.<the-directory-id-of-my-azure-ad-b2c-tenant>", // unique id is different for each user
"environment": "<my-azure-ad-b2c-tenant>.b2clogin.com",
"tenantId": "",
"username": "<user-email-address>", // different for each user
"localAccountId": "***uniqueId***", // different for each user
"idTokenClaims":
{
"exp": 1673069519,
"nbf": 1673065919,
"ver": "1.0",
"iss": "https://<my-azure-ad-b2c-tenant>.b2clogin.com/<the-directory-id-of-my-azure-ad-b2c-tenant>/v2.0/",
"sub": "***uniqueId***", // different for each user
"aud": "<the-application/client id of my web app>",
"iat": 1673065919,
"auth_time": 1673065918,
"idp_access_token": "********", // <-- this property is ONLY present in the authToken returned from the SOCIAL login
"idp": "https://login.microsoftonline.com/<the-directory-id-of-my-azure-ad-tenant>/v2.0", // <-- this property is ONLY present in the authToken returned from the SOCIAL login
"emails":
[
"<user-email-address>" // different for each user
],
"tfp": "B2C_1_signin1",
"at_hash": "*******" // different for each user
}
},
"idToken": "*******", // different for each user
"idTokenClaims":
{
"exp": 1673069519,
"nbf": 1673065919,
"ver": "1.0",
"iss": "https://<my-azure-ad-b2c-tenant>.b2clogin.com/<the-directory-id-of-my-azure-ad-b2c-tenant>/v2.0/",
"sub": "***uniqueId***", // different for each user
"aud": "<the-application/client id of my web app>",
"iat": 1673065919,
"auth_time": 1673065918,
"idp_access_token": "********", // <-- this property is ONLY present in the authToken returned from the SOCIAL login
"idp": "https://login.microsoftonline.com/<the-directory-id-of-my-azure-ad-tenant>/v2.0", // <-- this property is ONLY present in the authToken returned from the SOCIAL login
"emails":
[
"<user-email-address>" // different for each user
],
"tfp": "B2C_1_signin1", // different for each user
"at_hash": "*******"
},
"accessToken": "*******", // different for each user
"fromCache": false,
"expiresOn": "2023-01-07T05:31:57.000Z",
"correlationId": "*******", // different for each user
"requestId": "",
"extExpiresOn": "2023-01-07T05:31:57.000Z",
"familyId": "",
"tokenType": "Bearer",
"state": "",
"cloudGraphHostName": "",
"msGraphHost": "",
"fromNativeBroker": false
}
For Reference
For reference, below are the steps I took to:
Enable login from a single Azure AD tenant
Enable Azure AD users to be able to call Graph API in their own tenant
I added a single Azure AD tenant as an Identity Provider by going to:
Azure Portal > Azure AD B2C Tenant > Azure AD B2C > Identity Providers > + New OpenID Connect provider > [ fill in required fields and click 'Save' ]
Detailed steps on adding an Azure AD tenant as an identity provider can be found here:
https://learn.microsoft.com/en-us/azure/active-directory-b2c/identity-provider-azure-ad-single-tenant?pivots=b2c-user-flow#configure-azure-ad-as-an-identity-provider
In the Scope field, I added the scopes that I wanted the Azure AD users to be able to use in their own tenant, i.e:
openid profile User.ReadWrite.All Directory.ReadWrite.All Team.ReadBasic.All TeamSettings.ReadWrite.All
Then I went to the relevant 'user flow' at:
Azure Portal > Azure AD B2C Tenant > Azure AD B2C > User flows > B2C_1_signin1 > Identity providers > [ check the recently added OpenID connect provider ] > [ click 'Save' ]
In order for Azure AD users to be able to make Graph API calls to their own tenant, I did the following to make sure that an idp_token was returned when they logged in - I believe this is essentially an 'access token' for their tenant:
Azure Portal > Azure AD B2C Tenant > Azure AD B2C > User flows > B2C_1_signin1 > Application claims > [ select 'Identity Provider Access Token' ] > [ click 'Save' ]
The idp claim is the one that you are looking for since it will be set of all external providers of the Azure AD B2C tenant.
Note that the idp_access_token claim is present when pass through is enabled but is only supported for OAuth 2.0 providers, so it may be blank for some.

Azure Ad B2C vs Keycloak

I have a question regarding keycloak and Azure Ad b2c. Both offres authentication with different identity providers. Main difference I can see is in authorization. With Keycloak I can easily manage claims and roles of users via admin portal. In Azure B2c it looks very hard (setting custom policies) maintaining all the data via custom created app. Am I missing something? Relying app is strongly cloud native (Azure of course) and thats why I'm thinking about azure b2c. Relying app will strongly use roles and claims also.
What do you think about these two identity solutions (pros and cons).
What is the easiest way to manage implemented (by custom policies) user claims and roles in azure b2c? (do I have to write separate management app by myself or there is easiesy solution?)
Edit: first conclusion is that right nów azure requires separate app for connecting to graphapi (via rest or using sdk). Keycloak has it built in. Any pros of using b2c in this case (for c# dev)?
You don't have to use custom policies to use custom claims. Below an example :
On your B2C tenant, go to Azure AD B2C > Manage > User attributes and create a custom attribute; here a client ID attribute :
Then Azure AD B2C > Policies > User flows and create a flow, for example "Sign In" :
Use the recommended flow :
During the flow's creation, at the last steps, you can specific what claim will be included in the returned jwt token on Sign in; select our custom attribute Client ID :
Then when I login into my application using a B2C account, I have the below JWT token returned :
JWT token's payload, base64 decoded :
{
"iss": "https://mytenant.b2clogin.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/v2.0/",
"exp": 1664181788,
"nbf": 1664178188,
"aud": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"oid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"sub": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"extension_ClientID": 1,
"name": "myUserName",
"emails": [
"user#email.com"
],
"tfp": "B2C_1_SI",
"nonce": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"scp": "user_impersonation",
"azp": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"ver": "1.0",
"iat": 1664178188
}
I have set the "extension_ClientID": 1 using the msGraph API but this could also be done using a SignUp flow.

How to add roles claim in access_token , currently it is coming in id_token?

I am following Authentication code flow with PKCE and my Identity provider is Azure Active directory.
I have created a App , "client-app" from App Registrations. In the manifest I have added appRoles like the following.
"appRoles": [
{
"allowedMemberTypes": [
"User"
],
"displayName": "StoreGroupManager",
"id": "47fbb575-859a-4941-89c9-0f7a6c30beac",
"isEnabled": true,
"description": "Consumer apps have access to the consumer data.",
"value": "StoreGroupManager"
}
]
I am assigning this role StoreGroupManager to Users. Now when I follow Authorization code flow with PKCE and obtain the id_token , refresh token and access_token. I can see that the id_token has a claim roles but not the access_token.
I need to have roles claim claim in the access_token. Can this be possible?
The following is the decoded id_token.
Roles will be in the access token if the app registration for the API that the access token is for defines those roles and they are assigned to the user.
So if you use the same app registration for the client and API, they should be there.
But if you have separate app registrations for the client and API, you will need to define the role in both apps and assign the user to it on both of them as well.

AAD B2C - Missing upn claim in access token

I have an Angular Application which is authenticated using AAD B2C. This talks to a .Net Core API using an access token.
My problem is that I am not receiving a User Principal Name (upn) in my access token.
I have been adding additional "Application claims" like "Given name" and "Surname" and these appear in my access token just fine! Therefore, I believe that my scopes (openid, profile, email) are set correctly and that this in theory is working.
I believe since I am using version 1.0 of the token, that I do not need to configure an any additional claims in my application manifest. My user is a standard AD user not a guest.
The following document states that the upn claim should be included in the v1.0 tokens:
https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-optional-claims
What am I doing wrong?!
Decoded Access Token:
{
"iss": "https://(my-tenant-name).b2clogin.com/(guid)/v2.0/",
"exp": (number),
"nbf": (number),
"aud": "(guid)",
"oid": "(guid)",
"sub": "(guid)",
"name": "My Name",
"given_name": "Given name",
"family_name": "Surname",
"country": "Norge",
"tfp": "B2C_1_signupsignin2",
"nonce": "(guid)",
"scp": "basic",
"azp": "(guid)",
"ver": "1.0",
"iat": (number)
}
UPN is not returned in AAD B2C tokens because it is an irrelevant random string that is set.
Rather AAD B2Cs unique name is stored in signInNames attribute, and returned in your token as email or username.
The doc you linked is for AAD, and irrelevant to AAD B2C. These are two seperate token issuer services. Select in your User Flow Application Claims to return “email addresses”.
https://learn.microsoft.com/en-us/azure/active-directory-b2c/user-flow-overview#email-address-storage
At this time, apps that support both personal accounts and Azure AD (registered through the app registration portal) cannot use optional claims. However, apps registered for just Azure AD using the v2.0 endpoint can get the optional claims they requested in the manifest.
To achieve UPN Claim in the token, use B2C Custom Policy.
Refer this link for the starter pack: https://github.com/Azure-Samples/active-directory-b2c-custom-policy-starterpack
Add below claim to the TechnicalProfile AAD-UserReadUsingObjectId and in the Relying Party Policy (Eg: SignUpOrSignin.xml):
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
As per the code you have provided we observed you are using v2 endpoint( "iss": "https://(my-tenant-name).b2clogin.com/(guid)/v2.0/")
As per the document if you see iss:If the token was issued by the v2.0 endpoint, the URI will end in /v2.0.
In the V2 endpoint you need to make a request explicitly for UPN claim.
In v1.0 endpoint they are returned by default but v2.0 made smaller tokens so they made it optional.
Please go through the following links for more understanding. https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-optional-claims How to add optional claims in application manifest https://learn.microsoft.com/en-us/azure/active-directory/develop/reference-app-manifest

Microsoft graph and Azure Ad user authentication

I have an application registered in Azure ad.
When i do ADAL with the following details i get a authoriazation token to use with microsoft graph api.
`username = 'admin#domain.com'
password = 'password123'
client_id = application id from azure ad
client_secret = keys from application on azure ad
tenant = directory id from azure ad`
Using this token i can fetch the list of all sites in my sharepoint account.
Below is the endpoint i call to fetch the sites with the bearer token:
https://graph.microsoft.com/v1.0/sites?search=*
But when I just do the client authentication using token generated using below endpoint Iam not able to access the sites list.
login.microsoftonline.com/tenant_id/oauth2/v2.0/token
`grant_type : client_credentials
clientid : client_id
clientsecrte : client_secret
scope : https://graph.microsoft.com`
It does not return all of the sites.
Is there a way of getting all sites list with just client authentication.
Or can i get a token for user authentication without user password.
Here is the token decoded that i am using:
{
"aud": "https://graph.microsoft.com",
"iss": "https://sts.windows.net/586145ec-0428-4da6-8061-fb114257ab70/",
"iat": 1528949458,
"nbf": 1528949458,
"exp": 1528953358,
"aio": "Y2dgYLh*************xAAA=",
"app_displayname": "App Name",
"appid": "504ddb16-2899-48be-be57-**********",
"appidacr": "1",
"idp": "https://sts.windows.net/586145ec-0428-4da6-8061-fb114257ab70/",
"oid": "afcf166f-24c2-49f1-b285-b672d0413c50",
"roles": [
"Sites.Read.All",
"Sites.ReadWrite.All",
"Sites.Manage.All",
"Sites.FullControl.All",
],
"sub": "afcf166f-24c2-49f1-b285-b672d0413c50",
"tid": "586145ec-0428-4da6-8061-fb114257ab70",
"uti": "hwYd8FZCH0KruWGRFiIHAA",
"ver": "1.0"
}
I get other permissions also but these are site related in Microsoft Graph api
Cause:
The successful one is using ROPC flow and it can get delegated permissions onbehlaf of the user. But the failed one is using client_credentials flow which get application permissions and cannot onbehlf of the user.
Updated Answer
Solution:(Before you do this test, ensure you have SPO license in your tenant)
Try to add Sites.Read.All Application permission in your registrated AAD Application and do admin consent for it before you getting token.
If you're using AAD v1 endpoint, you can do admin consent by clicking Grant permissions button.If you're using v2 endpoint, please input this kind of URL in your internet browser to do admin grant:
https://login.microsoftonline.com/{yourtenant}/adminconsent?client_id={the applicationid of your client}&state=123&redirect_uri={the redirect uri of your app}
and sign in with Global admin account and accept this permission.
In my test lab, I used v2 endpoint.Here is the token I got via Postman:
Here is decoded token in https://jwt.ms , we can decoded the token to ensure it has the permissions we want.
Then I use this token in the head to call Microsoft Graph API and succeeded:
For more detials about Site permission for Microsoft Graph, please refer to this documentation.
Please let me know if this helps!

Resources