How do I get Azure AD B2C's built-in claims to be returned in the token when I'm using a custom policy? - azure

I've been using the built-in SignUpOrSignIn policy for a while, but I'm now moving to a custom policy.
When I set up the built-in policy, I was able to choose from a list of built-in application claims (like displayName and jobTitle), and select which ones I wanted to be returned in the token when the user signed in.
Now I'm setting up the custom policy I want to do the same thing, but I can't get it to work.
So far, in TrustFrameworkBase I have added a ClaimType of jobTitle:
<ClaimType Id="jobTitle">
<DisplayName>Job Title</DisplayName>
<DataType>string</DataType>
<UserHelpText>Job title.</UserHelpText>
</ClaimType>
I've added the following OutputClaim to the TechnicalProfile with ID login-NonInteractive:
<OutputClaim ClaimTypeReferenceId="jobTitle" PartnerClaimType="jobTitle" />
And I've added the following OutputClaim to the TechnicalProfile with ID SelfAsserted-LocalAccountSignin-Email:
<OutputClaim ClaimTypeReferenceId="jobTitle" />
But the jobTitle claim doesn't come through with the others in the token. I've done the same for given_name and that does work. If I change the first OutputClaim to:
<OutputClaim ClaimTypeReferenceId="jobTitle" PartnerClaimType="given_name" />
then a jobTitle claim does come through, but with the value of the given_name claim. This implies I'm just using the wrong PartnerClaimType but there doesn't seem to be a list of them anywhere.
How can I get the built-in job title attribute to be returned as a claim in the token when the user signs in using their local B2C account?

If you only want to read the jobTitle claim (or other claims) for the user and then issue it (or them) in the token, then you must:
1) Declare the jobTitle claim:
<ClaimType Id="jobTitle">
<DisplayName>Job Title</DisplayName>
<DataType>string</DataType>
<DefaultPartnerClaimTypes>
<Protocol Name="OAuth2" PartnerClaimType="job_title" />
<Protocol Name="OpenIdConnect" PartnerClaimType="job_title" />
</DefaultPartnerClaimTypes>
</ClaimType>
2) Add the jobTitle claim as an output claim to the AAD-UserReadUsingObjectId technical profile:
<TechnicalProfile Id="AAD-UserReadUsingObjectId">
...
<OutputClaims>
...
<OutputClaim ClaimTypeReferenceId="jobTitle" />
</OutputClaims>
...
</TechnicalProfile>
3) Add the jobTitle claim as an output claim to the relying party technical profile:
<RelyingParty>
...
<TechnicalProfile Id="PolicyProfile">
...
<OutputClaims>
...
<OutputClaim ClaimTypeReferenceId="jobTitle" />
</OutputClaims>
...
</TechnicalProfile>
</RelyingParty>

Related

Different claims for id and access token

I know how to configure custom claims https://learn.microsoft.com/en-us/azure/active-directory-b2c/configure-tokens?pivots=b2c-custom-policy
The problem is the same claims are included in access token and id token.
I want to include "displayName" claim in the id token, but not in the access token, but I don't see a way to differentiate the two
Its currently not possible to have different claim set in Access Token vs ID Token.
• You can edit the claims in the ID token and the access token in the implicit flow only while the same is not possible for authorization code flow with PKCE since the tokens in auth code flow with PKCE are set on the client side due to which they need to be flushed out first. Also, you can configure the relying party claims to be issued in the ID token and the access token in the custom policies for that application registered in B2C.
• Also, the relying party claims that are configured in the input claims and output claims section in the technical profile of the custom policy form the relying party definition which determines the ID token and the access token respectively. And both the tokens return with the same set of claims. You can configure the claims to be issued in the JWT token in the RP section for the implicit flow as below: -
‘ <RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignIn" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
<OutputClaim ClaimTypeReferenceId="identityProvider" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
{
...
"sub": "6fbbd70d-262b-4b50-804c-257ae1706ef2",
...
} ‘
Thus, through the token technical profile that you define for each ID and the access token in implicit flow, the claims can be passed and used accordingly.
Please find the below links for more information: -
https://learn.microsoft.com/en-us/azure/active-directory-b2c/session-behavior?pivots=b2c-custom-policy#configure-your-custom-policy

Get email (username) in Claims azure ad b2c signed In with AzureAd like a Social account

I am using starter pack of custom polices with the SocialAndLocalAccounts package to sign in with an Azure Active Directory account.
I customized the flows with custom attributes and it works well for me.
I need to receive the email used for the login (username = email).
In RelyingParty I have this OutputClaims
<OutputClaim ClaimTypeReferenceId = "signInName" />
<OutputClaim ClaimTypeReferenceId = "signInNames.emailAddress" PartnerClaimType = "email" />
<OutputClaim ClaimTypeReferenceId = "otherMails" />
When a user signs-in with a local b2c account, I get the email
in "signInName" and "email" claims,
but when a user signs-in with an AzureAd account , the claims are empty.
How can I get the email?
How must I write the custom policies (TrustFrameworkBase and TFExtensions) ?
Can you help me ?
When the Azure AD identity is signed-in with for the first time, you must map from the upn claim that is issued by Azure AD to the email claim that is used by Azure AD B2C, so that this email claim can be:
Written as the otherMails property in the user object to the Azure AD B2C directory.
Issued by Azure AD B2C in the ID token to the client application.
To map from the upn claim that is issued by Azure AD to the email claim that is used by Azure AD B2C, add a new <OutputClaim /> to the Azure AD authentication technical profile:
<ClaimsProvider>
<Domain>commonaad</Domain>
<DisplayName>Common AAD</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Common-AAD">
<DisplayName>Multi-Tenant AAD</DisplayName>
<Protocol Name="OpenIdConnect" />
...
<OutputClaims>
...
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="upn" />
</OutputClaims>
...
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
The AAD-UserWriteUsingAlternativeSecurityId technical profile converts the email claim to the otherMails claim by invoking the CreateOtherMailsFromEmail claims transformation and then saves the otherMails claim to the user object.
To issue the email claim in the ID token to your client application, add a new <OutputClaim /> to the relying party technical profile:
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignIn" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
...
<OutputClaims>
...
<OutputClaim ClaimTypeReferenceId="otherMails" PartnerClaimType="emails" />
</OutputClaims>
...
</TechnicalProfile>
</RelyingParty>

How to output group claims in B2C from ADFS as an identity provider

I'm using ADFS as an IdP for Azure B2C through OpenID Connect.
Login works and B2C sends UPN from ADFS as socialIdpUserId claim in JWT token.
But group claims from ADFS do not work.
How to receive group claims in JWT?
Here is the setup:
ADFS claim rule: domain security groups and upn
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname",
Issuer == "AD AUTHORITY"] =>
issue(store = "Active Directory",
types = ("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn", "http://schemas.xmlsoap.org/claims/Group"),
query = ";userPrincipalName,tokenGroups(longDomainQualifiedName);{0}",
param = c.Value);
Client permissions are set to openid and allatclaims
New group claim type definition in TrustFrameworkBase policy in ClaimsSchema:
<ClaimsSchema><ClaimType Id="group">
<DisplayName>group</DisplayName>
<DataType>string</DataType>
<DefaultPartnerClaimTypes>
<Protocol Name="OAuth2" PartnerClaimType="group" />
<Protocol Name="OpenIdConnect" PartnerClaimType="group" />
<Protocol Name="SAML2" PartnerClaimType="http://schemas.xmlsoap.org/claims/Group" />
</DefaultPartnerClaimTypes>
</ClaimType></ClaimsSchema>
Output group claim definition in TechnicalProfile in TrustFrameworkExtensions policy:
<OutputTokenFormat>JWT</OutputTokenFormat><OutputClaims>
<OutputClaim ClaimTypeReferenceId="socialIdpUserId" PartnerClaimType="UPN" />
<OutputClaim ClaimTypeReferenceId="group" PartnerClaimType="group" />
</OutputClaims>
Output group claim definition in TechnicalProfile in SignUpOrSignIn policy file
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="socialIdpUserId" />
<OutputClaim ClaimTypeReferenceId="group" />
<OutputClaim ClaimTypeReferenceId="authmethod" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
<OutputClaim ClaimTypeReferenceId="identityProvider" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
But there is no group claim comes with JWT token! Why?
Here is how to issue group claims out of B2C:
1. Define a new claim type in for groups in the Base policy file. This definition should be at the end of < ClaimsSchema > element (yes, the man who wrote about stringCollection was write!)
<ClaimType Id="IdpUserGroups">
<DisplayName>Security groups</DisplayName>
<DataType>stringCollection</DataType>
<DefaultPartnerClaimTypes>
<Protocol Name="OAuth2" PartnerClaimType="groups" />
<Protocol Name="OpenIdConnect" PartnerClaimType="groups" />
<Protocol Name="SAML2" PartnerClaimType="http://schemas.xmlsoap.org/claims/Group" />
</DefaultPartnerClaimTypes>
</ClaimType>
Use this new defined claim in the < OutputClaims > in the extenstion policy in < ClaimsProvider > definition for ADFS
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="socialIdpUserId" PartnerClaimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn" />
<OutputClaim ClaimTypeReferenceId="IdpUserGroups" PartnerClaimType="http://schemas.xmlsoap.org/claims/Group" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="SAML fmdadfs4.local"/>
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="SAML ADFS4 fmdadfs4.local" />
</OutputClaims>
Use the same claim in the < OutputClaims > dfinition in relyng party definition under < RelyngParty > elemnt in your SignIn policy file
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="socialIdpUserId" />
<OutputClaim ClaimTypeReferenceId="IdpUserGroups" />
<OutputClaim ClaimTypeReferenceId="identityProvider" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" PartnerClaimType="userPrincipalName" />
Issue group claims from ADFS as it is shown here
Looks like OP simply has the partnerclaimtype misspelled.
Not certain because you may have mapped something non-standard, but I'm thinking you just need to change your PartnerClaimType from group to groups.
<ClaimType Id="groups">
<DisplayName>Groups</DisplayName>
<DataType>stringCollection</DataType>
<DefaultPartnerClaimTypes>
<Protocol Name="OpenIdConnect" PartnerClaimType="groups" />
</DefaultPartnerClaimTypes>
<UserHelpText>List of group memberships</UserHelpText>
</ClaimType>
Once you define the ClaimType, you don't need to specify the PartnerClaimType anywhere else - unless you're overriding the value.
I'd also consider using the DefaultValue="" attribute so you can check your policy is properly executing the output claim.
OutputClaim ClaimTypeReferenceId="groups" DefaultValue="no groups assigned

Azure Ad b2c: Get email in Claims after successfully Signin in azure ad b2c

I am using starter pack of custom polices with SocialAndLocalAccounts pack.
It is working fine for me.
But I am facing one issue.I need to get email as claim after successfully login.
I am getting email as claim, once user has been been signed-up and redirects back immediately to application.
but I am not getting it when a user simply signs-in.
How can I get that?
where do I need to write an Output Claim to get the value of email in claim?
Kindly help me.
Thanks
For Chris Padgett's answer, you can add other emails (Alternate email) into the claim.
If you just want to add email claim from the SignIn name into the token, you can just take following steps:
Open your SignUporSignIn.xml file
Replace <OutputClaim ClaimTypeReferenceId="email" /> with <OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" PartnerClaimType="email" />
Save this SignUporSignIn.xml file and upload it to Azure AD B2C to overwrite the policy.
Run the SignUporSignIn policy to test it.
Here is my test result, you can see the email claim in the token:
Hope this helps.
Following describes how you can save, load, and then issue the otherMails claim as emails from the sign-up/sign-in and password reset policies.
When writing a local account: You must create the otherMails claim from the email claim using the CreateOtherMailsFromEmail claims transformation and then persist the otherMails claim in the AAD-UserWriteUsingLogonEmail technical profile:
<TechnicalProfile Id="AAD-UserWriteUsingLogonEmail">
...
<IncludeInSso>false</IncludeInSso>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CreateOtherMailsFromEmail" />
</InputClaimsTransformations>
<InputClaims>
...
</InputClaims>
<PersistedClaims>
...
<PersistedClaim ClaimTypeReferenceId="otherMails" />
</PersistedClaims>
<OutputClaims>
...
<OutputClaim ClaimTypeReferenceId="otherMails" />
</OutputClaims>
...
</TechnicalProfile>
You must then pass the otherMails claim out from the LocalAccountSignUpWithLogonEmail technical profile that is invoked to register a local account:
<TechnicalProfile Id="LocalAccountSignUpWithLogonEmail">
...
<OutputClaims>
...
<OutputClaim ClaimTypeReferenceId="otherMails" />
</OutputClaims>
</TechnicalProfile>
When writing a social account: The otherMails claim is already created from the email claim and then persisted in the AAD-UserWriteUsingAlternativeSecurityId technical profile.
You must then pass the otherMails claim out from the SelfAsserted-Social technical profile that is invoked to register a social account:
<TechnicalProfile Id="SelfAsserted-Social">
...
<OutputClaims>
...
<OutputClaim ClaimTypeReferenceId="otherMails" />
</OutputClaims>
</TechnicalProfile>
When reading a local or social account: The otherMails claim is already read in the AAD-UserReadUsingObjectId, AAD-UserReadUsingEmailAddress, and AAD-UserReadUsingAlternativeSecurityId technical profiles.
You must then pass the otherMails claim out from the LocalAccountDiscoveryUsingEmailAddress technical profile that is invoked to recover a local password:
<TechnicalProfile Id="LocalAccountDiscoveryUsingEmailAddress">
...
<OutputClaims>
...
<OutputClaim ClaimTypeReferenceId="otherMails" />
</OutputClaims>
</TechnicalProfile>
To issue the otherMails claim as emails from the sign-up/sign-in and password reset policies: You must add the otherMails claim as <OutputClaim /> to the relying party policies:
<RelyingParty>
...
<TechnicalProfile Id="PolicyProfile">
<OutputClaims>
...
<OutputClaim ClaimTypeReferenceId="otherMails" PartnerClaimType="emails" />
</OutputClaims>
</TechnicalProfile>
</RelyingParty>
Another option that is working for me was to extend AAD-UserReadUsingObjectId so that it copies the signInNames.emailAddress claim into email. That brought sign-in into alignment with our other journeys/sub-journeys for integrated sign-up, password reset, and social log-in -- which each populate email during first log-in/sign-up.
All I needed to do was add this to TrustFrameworkExtension.xml (under <ClaimsProviders>):
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-UserReadUsingObjectId">
<OutputClaims>
<OutputClaim
ClaimTypeReferenceId="email"
PartnerClaimType="signInNames.emailAddress"
/>
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>

Limiting Claims by App in Azure AD B2C

We have 3 different applications requiring different sets of extension claims.
Application A - Claim A1, Claim A2, Claim A3
Application B - Claim B1, Claim B2, Claim B3
We defined the six Claims in TrustFrameworkExtensions, updated the various TechnicalProfiles to take the input from user and as well write it to directory.
To support the needs of the individual applications, we created 2 RP files, one for each Application and defined the User Journey's specific to the Apps.
The 6 claims are showing up for both Apps, and we want to restrict by App the Claims.
Tried to copy everything from TrustFrameworkExtensions into RP file, the RP does not load and throws an error as follows
Unable to upload policy. Reason : Validation failed: 3 validation error(s) found in policy "B2C_1A_1182017SIGNUP_SIGNIN" of tenant "XXXXXXXXX.onmicrosoft.com".
A required Metadata item with key "ApplicationObjectId" was not found in the TechnicalProfile with id "AAD-UserWriteUsingAlternativeSecurityId" in policy "B2C_1A_1182017signup_signin" of tenant "XXXXXXXXX.onmicrosoft.com".
A required Metadata item with key "ApplicationObjectId" was not found in the TechnicalProfile with id "AAD-UserWriteUsingLogonEmail" in policy "B2C_1A_1182017signup_signin" of tenant "XXXXXXXXX.onmicrosoft.com".
A required Metadata item with key "ApplicationObjectId" was not found in the TechnicalProfile with id "AAD-UserWriteProfileUsingObjectId" in policy "B2C_1A_1182017signup_signin" of tenant "XXXXXXXXX.onmicrosoft.com".
Appreciate advise and guidance to support multiple Apps with different claims.
To use extension attributes in your custom policy you need to add some configuration to your file involving the b2c-extensions-app that is automatically created and registered in the Portal for each B2C tenant.
In your case, you seem to be missing the ApplicationObjectID and possibly the ClientId of the b2c-extensions-app in the Metadata key section of your AAD-Common technical profile.
The Next Steps section of the Create Custom Attribute documentation describes how to perform this configuration.
https://learn.microsoft.com/en-us/azure/active-directory-b2c/custom-policy-custom-attributes#modify-your-custom-policy
Open the extensions file of your policy. For example, SocialAndLocalAccounts/TrustFrameworkExtensions.xml.
Find the ClaimsProviders element. Add a new ClaimsProvider to the ClaimsProviders element.
Replace ApplicationObjectId with the Object ID that you previously recorded. Then replace ClientId with the Application ID that you previously recorded in the below snippet.
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-Common">
<Metadata>
<!--Insert b2c-extensions-app application ID here, for example: 11111111-1111-1111-1111-111111111111-->
<Item Key="ClientId"></Item>
<!--Insert b2c-extensions-app application ObjectId here, for example: 22222222-2222-2222-2222-222222222222-->
<Item Key="ApplicationObjectId"></Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
Use <RelyingParty><TechnicalProfile><OutputClaims> to control the claims returned.
<RelyingParty>
<DefaultUserJourney ReferenceId="SignInAppA" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="ClaimA1" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
and
<RelyingParty>
<DefaultUserJourney ReferenceId="SignInAppB" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="ClaimB1" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>

Resources