Reading Extension Claims in Azure AD B2C - azure-ad-b2c

I have 2 claims that I want to store in the Directory for my application to use. These are not available for the user to edit however is available for the application to read from the Token.
The BuiltIn policies are able to retrieve the claims however, I have not had any success with retrieving these claims using Custom Policies.
Reading through Next Steps of the article “Creating and using custom attributes in a custom profile edit policy” the claims will need to be added to the RP and TechnicalProfile to read from Directory. I accordingly updated the RP and as well the TP's that read from Directory such as
<TechnicalProfile Id="AAD-UserReadUsingAlternativeSecurityId">
<TechnicalProfile Id="AAD-UserReadUsingObjectId">
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
<TechnicalProfile Id="AAD-UserReadUsingEmailAddress">
Unable to figure out what might be missing to retreive the 2 extension claims.

Assuming you are reading the custom claims in the user journeys and writing them via the Azure AD Graph API, then you must:
1: Add the custom claims as <ClaimType />s to the base policy.
<ClaimType Id="extension_UserAttribute1">
<DisplayName>User Attribute 1</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="extension_UserAttribute2">
<DisplayName>User Attribute 2</DisplayName>
<DataType>string</DataType>
</ClaimType>
2: Add the application and object identifiers for the extensions app to the "AAD-Common" technical profile which is required to read the custom claims from the Azure AD B2C directory.
<TechnicalProfile Id="AAD-Common">
<DisplayName>Azure Active Directory</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.AzureActiveDirectoryProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ApplicationObjectId">Insert the object identifier for the b2c-extensions-app application here</Item>
<Item Key="ClientId">Insert the application identifier for the b2c-extensions-app application here</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="TokenSigningKeyContainer" />
</CryptographicKeys>
...
</TechnicalProfile>
Note: If you are wanting to read the custom claims in both built-in policies and custom policies, then you must use the application and object identifiers for the built-in b2c-extensions-app application rather than a custom extensions app as suggested by the Azure Active Directory B2C: Creating and using custom attributes in a custom profile edit policy tutorial.
3: Add the custom claims as <OutputClaim />s to the following technical profiles:
"AAD-UserReadUsingObjectId" for local account sign-in and profile editing
"AAD-UserReadUsingAlternativeSecurityId" for a social account sign-in and profile editing
"LocalAccountDiscoveryUsingEmailAddress" and "AAD-UserReadUsingEmailAddress" for a local account password reset
<OutputClaims>
...
<OutputClaim ClaimTypeReferenceId="extension_UserAttribute1" />
<OutputClaim ClaimTypeReferenceId="extension_UserAttribute2" />
</OutputClaims>
4: Issue the custom claims as <OutputClaim />s in any relying party policies.

Thanks #ChrisPadget. For anybody still struggling. Make sure that the UserJourney Step that reads data from AD is actually available in your User Journey. In my case, I had to add:
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>

Related

How Do I Whitelist Email Domains In Signup With Azure B2C?

I am using simple email signup for Azure B2C and not SSO and my user flow is the basic one from this tutorial
https://learn.microsoft.com/en-ca/azure/active-directory-b2c/tutorial-create-user-flows
At the moment I am using temporary email addresses for my testing, however, when I go live I want to whitelist the domains that people can sign up with.
How do I do this whitelisting?
Currently Using Azure AD B2C Userflows we can't Whitelist the users while signup based on the email domain. We Need to handle it using Custom policies by calling the Rest API and need to validate the email address.
Please go through the documents and sample on using Custom policies with Rest API.
A Restful technical profile provides support for interfacing with your own RESTful service. Azure AD B2C sends data to the RESTful service in an InputClaims collection and receives data back in an OutputClaims collection. Find the ClaimsProviders element in your
Configure the REST API technical profile
<ClaimsProvider>
<DisplayName>REST APIs</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="REST-GetProfile">
<DisplayName>Get user extended profile Azure Function web hook</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">https://your-account.azurewebsites.net/api/GetProfile?code=your-code</Item>
<Item Key="SendClaimsIn">Body</Item>
<!-- Set AuthenticationType to Basic or ClientCertificate in production environments -->
<Item Key="AuthenticationType">None</Item>
<!-- REMOVE the following line in production environments -->
<Item Key="AllowInsecureAuthInProduction">true</Item>
</Metadata>
<InputClaims>
<!-- Claims sent to your REST API -->
<InputClaim ClaimTypeReferenceId="objectId" />
<InputClaim ClaimTypeReferenceId="userLanguage" PartnerClaimType="lang" DefaultValue="{Culture:LCID}" AlwaysUseDefaultValue="true" />
</InputClaims>
<OutputClaims>
<!-- Claims parsed from your REST API -->
<OutputClaim ClaimTypeReferenceId="balance" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
You need to handle the Email validation logic in your REST API. The above Provideed document links helps you in building the custom policy.
This limitation is already raised in the Azure Feedback uservoice and to consider this feature request for the future, please upvote on the existing UserVoice.
That way the product group can prioritize this accordingly.

How can I read the mobile value from a B2C user record and transform it to a strongAuthenticationPhoneNumber?

I have the need to create Azure B2C user accounts programmatically. In a separate user data store I hold pertinent information about the users I need to set up in B2C including their mobile phone number, which we've already been communicating with them on.
My business requirement is that this mobile phone number is used as a secondary factor during the user's first-time login/password reset experience. I have an initial login experience which uses an externally-created JWT token to take the user to a custom User Journey where they can set a password for the first time.
I understand that it is not yet possible to set the Azure MFA mobile number via Graph API or PowerShell. (Is this still true?). Therefore B2C asks the user to enter their mobile number in the exemplar PhoneFactor-InputOrVerify Technical Profile. This is a security hole as you can just enter any mobile number in there and verify that number.
I can easily programmatically add the user's number to some other field - e.g. the mobile field on the user record.
Question 1.
Is there a way to read the user account mobile value and present it to a Technical Profile as if it is the strongAuthenticationPhoneNumber value or Verified.strongAuthenticationPhoneNumber?
Question 2.
Is this even a good idea? I imagine there are good reasons not to do this, but I can't fathom what they might be.
I've tried creating new ClaimTypes, reading the 'mobile' field value, creating ClaimsTranfromations to try to make the mobile claim appear to be the strongAuthenticationPhoneNumber claim, and trying generally to 'spoof' B2C into thinking this is the actual number as stored in the MFA data store.
This is the the standard PhoneFactor-InputOrVerify Technical Profile from the starterpack:
<ClaimsProvider>
<DisplayName>PhoneFactor</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="PhoneFactor-InputOrVerify">
<DisplayName>PhoneFactor</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.PhoneFactorProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ContentDefinitionReferenceId">api.phonefactor</Item>
<Item Key="ManualPhoneNumberEntryAllowed">true</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CreateUserIdForMFA" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="userIdForMFA" PartnerClaimType="UserId" />
<InputClaim ClaimTypeReferenceId="strongAuthenticationPhoneNumber" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" PartnerClaimType="Verified.OfficePhone" />
<OutputClaim ClaimTypeReferenceId="newPhoneNumberEntered" PartnerClaimType="newPhoneNumberEntered" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-MFA" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
I can provide more code samples of the custom User Journey I mentioned earlier but I don't think this will help with this problem.
You have a few options:
You can add the strongAuthenticationPhoneNumber claim to the same JWT that is used for the onboarding flow and prompt for verification of this phone number during this onboarding flow.
You can map to the strongAuthenticationPhoneNumber claim for the PhoneFactor-InputOrVerify technical profile from the mobile property (or an extension property) of the user object.
For option 1, the onboarding user journey should write the verified phone number to the user object, without the newPhoneNumberEntered-based precondition:
<OrchestrationStep Order="8" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserWriteWithObjectId" TechnicalProfileReferenceId="AAD-UserWritePhoneNumberUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
For option 2, you can map to the strongAuthenticationPhoneNumber claim from the mobile property, as follows:
<InputClaims>
<InputClaim ClaimTypeReferenceId="userIdForMFA" PartnerClaimType="UserId" />
<InputClaim ClaimTypeReferenceId="mobile" PartnerClaimType="strongAuthenticationPhoneNumber" />
</InputClaims>

Azure B2C: Adding an orchestration step to ask for more information

I'm looking to break up our registration journey into multiple pages, so we don't end up with a massive form.
I'm trying to add an orchestration step after the initial registration page to ask for the user's favourite colour.
I have added the following claims provider:
<ClaimsProvider>
<DisplayName>Self Asserted</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="SATP-GetFavouriteColour">
<DisplayName>Local Account Sign In</DisplayName>
<Protocol Name="Proprietary"
Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
<Metadata>
<Item Key="ContentDefinitionReferenceId">api.signuporsignin</Item>
</Metadata>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="favouriteColour" Required="true" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop"/>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
and have updated my SignUpOrSignIn journey to include it just before the final step of returning the claims to the RP, like so:
<OrchestrationStep Order="8" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="GetFavouriteColour" TechnicalProfileReferenceId="SATP-GetFavouriteColour" />
</ClaimsExchanges>
</OrchestrationStep>
I have also added the output claim to my Relying Party file like so:
<OutputClaim ClaimTypeReferenceId="favouriteColour" DefaultValue="Lemons"/>
The policy files validate and upload successfully, but when I go through the journey, I simply get the default value of "Lemons" returned to my RP.
I expected B2C to ask the user for their favourite colour. Why isn't B2C asking the user for the new field I added?
Am I right in thinking this is possible, and I'm just missing something simple?
Thanks in advance
I found the cause of this, I had missed off <UserInputType>TextBox</UserInputType> from my claimType declaration

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>

How to prompt for new claims for a new application using the same Azure AD B2C tenant

I have an existing application "A" using an Azure AD B2C tenant. During registration users have been asked to enter a number (specific for this application "A") that is stored in a Claim with the name "NumberA".
Now I want to create an new application "B" and I want the existing users of my tenant to be able to log into the application "B". But before they can use it they have to be prompted to enter a new number (specific for application "B") that is stored in a Claim with the name "NumberB".
When new users of application "B" register themselves they only have to enter the number for "B".
I think this must be possible but I am not sure how to do this.
Create a new Custom Policy "B2C_AppB_signup_signin"?
And then add a new Claim "NumberB" in a new "Extensions" file and "override" the technical profiles (AAD-UserWriteUsingLogonEmail, AAD-UserReadUsingEmailAddress etc)
Or is this the wrong path..
You are on the right track.
This can be implemented by creating two user journeys -- one for Application A and another for Application B -- and then adding a ClaimsExist precondition to an orchestration step in both user journeys that prompts for the application-specific claim.
For example: For Application B's sign-up or sign-in user journey, you can add the following orchestration step after the user object is read from Azure Active Directory (after either the end user has signed in with an existing account or signed up with a new account), which checks whether the "extension_NumberB" claim exists for this user object and if not then prompts for it:
<OrchestrationStep Order="4" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>extension_NumberB</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SelfAssertedApplicationBRegistrationExchange" TechnicalProfileReferenceId="SelfAsserted-ApplicationB-Registration" />
</ClaimsExchanges>
</OrchestrationStep>
Then add the "SelfAsserted-ApplicationB-Registration" technical profile:
<TechnicalProfile Id="SelfAsserted-ApplicationB-Registration">
<DisplayName>Application B Registration</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ContentDefinitionReferenceId">api.selfasserted.applicationb.registration</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" Required="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="extension_NumberB" Required="true" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteProfileUsingObjectId" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
You will then have to add the "extension_NumberB" claim as an <OutputClaim /> for the "AAD-UserReadUsingObjectId" technical profile and it as a <PersistedClaim /> for the "AAD-UserWriteProfileUsingObjectId" technical profile.
The option outlined is good although you have to manage to 2 policies and if you decide to have a 3rd or a 4th client the more policies you will have to manage.
I would suggest you create a rest call to a function app that accepts the client_id of the app {OIDC:ClientId} and then returns the value of a claim based on it.
That way you only ever have one policy and then you can modify the function app rather than a policy
I have detailed this approach here
Get the Azure AD B2C Application client id in the custom policy

Resources