How to prompt for new claims for a new application using the same Azure AD B2C tenant - azure-ad-b2c

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

Related

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>

How do I find out what is causing Azure B2C 500 Internal Server Error?

I am trying to add orchestration steps to my Azure B2C IEF user journey however, when I make changes I often times get error: "500 - Internal Server Error"
I have tried using Application Insights, but that does not tell you anything related to error 500.
Here is my Technical Profile
<TechnicalProfile Id="Step1">
<DisplayName>Step 1</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Email" Required="true"/>
<OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
</OutputClaims>
</TechnicalProfile>
And here is my User Journey Step
<OrchestrationStep Order="3" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="Step1" />
</ClaimsExchanges>
</OrchestrationStep>
Is there a way to find out what is causing these 500 - internal server errors?
ContentDefinition: The SelfAssertedAttributeProvider technical profile must have a ContentDefinition specified in the Metadata section. That is missing in your technical profile.
OutputClaims:
There is no ValidationTechnicalProfile in the technical profile Step1. That could potentially be an issue. Since these are OutputClaims, the policy must specify a way to create a value for each of those (even if at run time it may not actually get created). So an OutputClaim must have one of the three:
Specify a DefaultValue, which guarantees that it will have that value after the TechnicalProfile has been invoked.
Specify a UserInputType in the ClaimType under the ClaimsSchema section, which indicates that there is a way to retrieve that value from the user.
Specify it as an OutputClaim of a ValidationTechnicalProfile, which will allow another provider to retrieve such a value (e.g. from AD Graph, or a Rest API).
CryptographicKeys: SelfAssertedAttributeProvider TechnicalProfile also needs a CryptographicKeys section which specifies a key used by the provider.
I would recommend copying a technical profile from the Starter Packs Github and modify those since those will contain all the required elements.
(The fact that the service is returning 500 is a bug, and needs to be fixed.)

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

Azure AD B2C - Refresh_Token refresh claims via REST (Identity Experience Framework)

We have Azure AD B2C setup to use Identity Experience Framework, and on sign-in/sign-up a REST call is made to get extra security credential claims via an Azure Function. This works fine.
When we request an Access/Id Token via Refresh_Token via Azure AD B2C it looks like we get the same token back, and it doesn't call the REST API to get the latest updated token claims. Is it possible to make change this User Journey so it does?
Is there another solution to refresh token without logging in again to get latest updates?
(We could get around this in code and not using the Token, but for various reasons we want to explore this first).
You can declare a refresh token user journey, which calls your REST API, as follows:
<UserJourney Id="TokenRefresh">
<PreserveOriginalAssertion>false</PreserveOriginalAssertion>
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="RefreshTokenExchange" TechnicalProfileReferenceId="TpEngine_RefreshToken" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- TODO: Add an orchestration step that calls the REST API. -->
<OrchestrationStep Order="3" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
</UserJourney>
The initial orchestration step invokes the TpEngine_RefreshToken technical profile that reads the objectId claim from the current refresh token:
<ClaimsProvider>
<DisplayName>Trustframework Policy Engine Technical Profiles</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="TpEngine_c3bd4fe2-1775-4013-b91d-35f16d377d13">
<DisplayName>Trustframework Policy Engine Default Technical Profile</DisplayName>
<Protocol Name="None" />
<Metadata>
<Item Key="url">{service:te}</Item>
</Metadata>
</TechnicalProfile>
<TechnicalProfile Id="TpEngine_RefreshToken">
<DisplayName>Trustframework Policy Engine Refresh Token Technical Profile</DisplayName>
<Protocol Name="None" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
The second orchestration step invokes the AAD-UserReadUsingObjectId technical profile that reads claims from the Azure AD B2C directory for the signed-in user by the objectId claim.
Another orchestration step can call your REST API.
The final orchestration step issues new tokens.
You must reference the TokenRefresh user journey using the RefreshTokenUserJourneyId metadata item with the JwtIssuer technical profile so that tokens that are issued by this technical profile are refreshed by this user journey:
<ClaimsProvider>
<DisplayName>Token Issuer</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="JwtIssuer">
<DisplayName>JWT Issuer</DisplayName>
<Protocol Name="None" />
<OutputTokenFormat>JWT</OutputTokenFormat>
<Metadata>
<Item Key="client_id">{service:te}</Item>
<Item Key="issuer_refresh_token_user_identity_claim_type">objectId</Item>
<Item Key="RefreshTokenUserJourneyId">TokenRefresh</Item>
<Item Key="SendTokenResponseBodyWithJsonNumbers">true</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
<Key Id="issuer_refresh_token_key" StorageReferenceId="B2C_1A_TokenEncryptionKeyContainer" />
</CryptographicKeys>
<InputClaims />
<OutputClaims />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>

Reading Extension Claims in 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>

Resources