How to populate Display Name with Email using B2C custom user flow - azure

I'm implementing a local user B2C custom user flow policy based on Microsoft's guide.
My custom signup/signin policy is working but I want to modify it so that the Display Name field in B2C is automatically populated with the user's email address.
Currently I've disabled removed the Display Name claim from the signup form, meaning it get's populated with "unknown".
I'm using the provided samples as the basis of my policy so if someone can provide an example of how to modify these to support automatically populating the Display Name with the users email that would be perfect.

Sounds like you already removed it as an Output Claim from the LocalAccountSignUpWithLogonEmail.
In that same step, you should be able to add an OutputClaimsTransformation with TransformationMethod="CopyClaim", the input being the "email" and the output being the "displayName".
The claims transformation will look like this:
<ClaimsTransformation Id="CopyEmailAddressToDisplayName" TransformationMethod="CopyClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" TransformationClaimType="inputClaim"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" TransformationClaimType="outputClaim"/>
</OutputClaims>
</ClaimsTransformation>
and to call it from the technical profile, add this section right below the output claims.
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CopyEmailAddressToDisplayName" />
</OutputClaimsTransformations>
Reference: https://learn.microsoft.com/en-us/azure/active-directory-b2c/general-transformations
NOTE: If you're going to change these, I recommend copying the technical profile to the TrustFrameworkExtensions file to avoid confusion between what existed in the base and what you customized.

Related

How do I add a user attribute in a SAML SSO response

I am working on using Azure AD B2C as the Identity Provider for a custom site that also provides SSO for a Blackboard Learn LMS site. Blackboard can do SSO through SAML, so I am using those capabilities within B2C. Unfortunately, SAML is not a protocol I am well-acquainted with, so my ability to troubleshoot is limited. I am working off of the tutorial at:
https://learn.microsoft.com/en-us/azure/active-directory-b2c/saml-service-provider?tabs=windows&pivots=b2c-custom-policy
Everything is going mostly pretty well, but I am trying to map user attributes that are shared with Blackboard so that when a new user is created, they have at least a basic profile. There are a couple attributes that are coming through naturally in this tutorial, but I am trying to specify the username for the profile (specs require that it is the email address), and I am not having any luck.
I added the OutputClaim named "userPrincipalName" in the SignUpOrSignIn policy (see code below), as it has the appropriate value coming from B2C, but according to a Blackboard test page, it is not among the values that are being sent in the SAML response (see screenshot further down).
...
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignIn" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="SAML2"/>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
<OutputClaim ClaimTypeReferenceId="email" DefaultValue="" />
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="objectId"/>
</OutputClaims>
<SubjectNamingInfo ClaimType="objectId" ExcludeAsClaim="true"/>
</TechnicalProfile>
</RelyingParty>
...
Are there further steps that need to happen? I have tinkered with the TrustFrameworkExtensions (and Base) here an there, as well, but to no avail, and I can't find any described process for adding new attributes to the SAML response. Does anyone have an example/tutorial that could walk me through it?
(I looked at this question and an answer indicates that I might need to change the NameIdFormat. I can see that the format in my B2C app metadata is incorrect as it suggests, but I'm not sure how I would go about changing it)
Update
Based on #JasSuri-MSFT's suggestion, I set a (non-empty) default value for the UPN, and sure enough, that default value came through. So it would seem that the problem is that the UPN is null.
This is strange, as in the edit page for the user in B2C, there is clearly a UPN value. It also highlights another oddity that I had not mentioned. But there are other OutputClaims listed in that policy that are not being included either. One of them, email, I explicitly provided a value for, and it is apparently presenting as null as well.
Clearly there is another piece in the plumbing somewhere that I don't have established.
Add signInNames.emailAddress as an output claim in AAD-UserReadUsingObjectId and add signInNames.emailAddress as an output claim in the RelyingParty section. Use a partnerClaimType in the output claim in the RelyingParty to issue the claim with the name you like into the SAML Response.
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" PartnerClaimType="email"/>
https://github.com/azure-ad-b2c/azureadb2ccommunity.io/wiki/LocalAccount-Sign-In-and-Sign-Up-policy
https://learn.microsoft.com/en-us/azure/active-directory-b2c/user-profile-attributes#identities-attribute
https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-technical-profile#outputclaims

Registration with Azure AD B2C custom policy fails due to missing claim information

I am currently investigating the possibilities to send emails from a custom policy in Azure AD B2C through a custom email service provider. To do so, I was following this tutorial from the Microsoft documentation: https://learn.microsoft.com/en-us/azure/active-directory-b2c/custom-email-sendgrid.
Since we have been using custom policies for quite some time now, I tried to adopt the tutorial right away to our own custom policy: we use a multistep registration process with the email verification step being the first and the personal data step being the second step.
My efforts have been successful with connecting the email service provider, sending the code through it and proceed to the second step of the registration process after successfully verifing the email address.
However, when I want to complete the second step of the registration and have the user be actually created in AD I see the following error in the frontend:
Unable to validate the information provided.
To gain further insights I activated developer mode in the policy and checked the traces in Application Insights where I found the following error:
The claim type "signInNames.emailAddress", designated as the identifier claim type, could not be found in the claims collection for the claims principal in tenant id "B2C_1A_signup_notificationtest".
caused in the technical profile AAD-UserWriteUsingLogonEmail. However, as you can see below in that technical profile the aforementioned claim type signInNames.emailAddress is derived throught the partnerClaimType directive in the input claims of the technical profile:
<TechnicalProfile Id="AAD-UserWriteUsingLogonEmail">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<!-- THIS IS THE LINE I AM REFERRING TO: -->
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.emailAddress"
Required="true"/>
</InputClaims>
<PersistedClaims>
<!-- Required claims -->
<PersistedClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.emailAddress"/>
<PersistedClaim ClaimTypeReferenceId="newPassword" PartnerClaimType="password"/>
<PersistedClaim ClaimTypeReferenceId="displayName" DefaultValue="unknown"/>
<PersistedClaim ClaimTypeReferenceId="passwordPolicies"
DefaultValue="DisablePasswordExpiration"/>
<!-- Optional claims. -->
<PersistedClaim ClaimTypeReferenceId="extension_Salutation"/>
<PersistedClaim ClaimTypeReferenceId="givenName"/>
<PersistedClaim ClaimTypeReferenceId="surname"/>
<PersistedClaim ClaimTypeReferenceId="country"/>
<PersistedClaim ClaimTypeReferenceId="extension_Company"/>
<PersistedClaim ClaimTypeReferenceId="extension_Kundennummer"/>
</PersistedClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId"/>
<OutputClaim ClaimTypeReferenceId="newUser" PartnerClaimType="newClaimsPrincipalCreated"/>
<OutputClaim ClaimTypeReferenceId="authenticationSource"
DefaultValue="localAccountAuthentication"/>
<OutputClaim ClaimTypeReferenceId="userPrincipalName"/>
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress"/>
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common"/>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD"/>
</TechnicalProfile>
So my question would be: where can I look to debug further? Am I looking in the right place or does the error message mean something completely different?
Also as a side note: due to the nature of the identity experience frameworks configuration, there is a lot of xml involved. Therefore I have posted the relying party policy file that also includes the relevant parts for the service connection as a gist here: https://gist.github.com/mmaedler/0a555fc3f9e6036e235a15419e7afdd5
UPDATE Additionally I have added all relevant (hopefully) Technical Profiles as well as the UserJourney itself to this new gist: https://gist.github.com/mmaedler/19ab309897f3a7d993816eb34adc7edb
Also I have used your advise to investigate further. Since I overwrite the Technical Profile LocalAccountSignUpMultiStep-1 to replace the current built in code verification with the one using our mailing backend it suggests the output of this is lacking the email address in a field/format that is expected by the following technical profile in step 2. I have added a new OutputClaim to LocalAccountSignUpMultiStep-2 with a ClaimTypeReferenceId="email" which lead to a new input appearing on registration step 2. Entering an email address there ended in a successful signup and token creation.
Misunderstanding, this XML snippet input claim translates to - "Please find me this user by searching for the email through all users' signInNames.emailAddress attribute".
The error from the log states that, when this lookup operation was performed, the value for signInNames.emailAddress was null. This means that the claim email was never output as an output claim in any previous technical profile called by an orchestration step.
Look back through all technical profiles called prior to this and determine which claim has the user email inside it. Make sure that claim is output by a technical profile that is called by an orchestration step.

How to make a Custom Login Flow reset password on Force Password Change

I have created a custom signin flow using the starter pack (with only changes for my ad tenant). I create a new user in Ad, with a temporary password that the user is forced to change on first signin.
When the user signs in for the first time, an error is displayed that the username/password is invalid, rather than going to a password reset page.
What changes do I need to make to the Custom signin flow so it presents a password reset page?
In the B2C sign-in flow, you cannot use the new created user with the temporary password. For the flow, it cannot redirect to the user change password page.
If you can create a new user with a random password using the Azure AD Graph API, rather than using the Azure portal, then you can implement a custom policy that instructs the new user to reset the random password.
To implement this, you must create a custom attribute, e.g. ForceChangePasswordNextSignIn, set this custom attribute for the new user to true, and then test the custom attribute during the sign-in flow, as follows.
To set the custom attribute for the new user to true:
{
"accountEnabled": true,
"creationType": "LocalAccount",
"displayName": "Alex Wu",
"passwordProfile": {
"password": "Test1234",
"forceChangePasswordNextLogin": false
},
"signInNames": [
{
"type": "emailAddress",
"value": "AlexW#example.com"
}
],
"extension_<b2cExtensionApplicationObjectIdWithoutHyphens>_ForceChangePasswordNextSignIn": true
}
To test the custom attribute during the sign-in flow:
Declare a claim type that represents the custom attribute.
<ClaimType Id="extension_ForceChangePasswordNextSignIn">
<DisplayName>Force Change Password Next Sign-In</DisplayName>
<DataType>boolean</DataType>
</ClaimType>
Declare an AssertBooleanClaimIsEqualToValue claims transformation to ensure that the custom attribute isn't set to true (i.e. the new user doesn't have to reset to the random password).
<ClaimsTransformation Id="EnsureForceChangePasswordNextSignInIsFalse" TransformationMethod="AssertBooleanClaimIsEqualToValue">
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_ForceChangePasswordNextSignIn" TransformationClaimType="inputClaim" />
</InputClaims>
<InputParameters>
<InputParameter Id="valueToCompareTo" DataType="boolean" Value="false" />
</InputParameters>
</ClaimsTransformation>
Invoke the EnsureForceChangePasswordNextSignInIsFalse claims transformations from a copy of the AAD-UserReadUsingEmailAddress technical profile to test the custom attribute isn't set to true.
<TechnicalProfile Id="AAD-UserReadUsingSignInName-EnsureForceChangePasswordNextSignInIsFalse">
...
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="signInNames" Required="true" />
</InputClaims>
<OutputClaims>
...
<OutputClaim ClaimTypeReferenceId="extension_ForceChangePasswordNextSignIn" />
</OutputClaims>
<OutputClaimsTransformations>
...
<OutputClaimsTransformation ReferenceId="EnsureForceChangePasswordNextSignInIsFalse" />
</OutputClaimsTransformations>
</TechnicalProfile>
Invoke the AAD-UserReadUsingSignInName-EnsureForceChangePasswordNextSignInIsFalse technical profile from the SelfAsserted-LocalAccountSignin-Email technical profile to show an error message if the custom attribute is set to true (i.e. the new user does have to reset to the random password).
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
...
<Metadata>
...
<Item Key="UserMessageIfClaimsTransformationBooleanValueIsNotEqual">Whoops, you have to change your admin-created password, so please click 'Forgot your password?'.</Item>
</Metadata>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingSignInName-EnsureForceChangePasswordNextSignInIsFalse" />
<ValidationTechnicalProfile ReferenceId="login-NonInteractive" />
</ValidationTechnicalProfiles>
...
</TechnicalProfile>
MS have provided a sample that describes how to enact the force password reset. See Here.
This only works if you create the users programmatically. One point to note is you should make sure you set passwordProfile.ForceChangePasswordNextLogin to false when creating users.

How to get Facebook "age_range" and "gender" using Azure AD B2C

I'm currently using built-in attributes.
I'd like to get the "age_range" and "gender" from the FB.
Do I need to deal with custom policies as explained in the following topic:
how to get Facebook profile picture using Azure AD B2C
how to get Facebook profile picture using Azure AD B2C
to get them?
Thanks!
Yes, you will have to create a custom policy for that, and then:
1: Declare the "ageRange" and "gender" claim types in the extension file.
2: Add both the "age_range" and "gender" fields to the "ClaimsEndpoint" metadata item and the "ageRange" and "gender" output claims to the "Facebook-OAUTH" technical profile.
3: Issue the "ageRange" and "gender" claims in the relying party file.
If you are wanting to save the "age_range" and "gender" fields from Facebook as attributes to Azure AD B2C, then you must:
1: Follow the Azure Active Directory B2C: Creating and using custom attributes in a custom profile edit policy steps to create the custom attributes for "AgeRange" and "Gender".
2: Change the claim type declarations, as well as all other references to them, from "ageRange" and "gender" to "extension_AgeRange" and "extension_Gender".
3: Add the "extension_AgeRange" and "extension_Gender" claims in the extension file to the "AAD-UserWriteUsingAlternativeSecurityId" and "AAD-UserReadUsingAlternativeSecurityId" technical profile:
<ClaimsProvider>
<DisplayName>Facebook</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-UserReadUsingAlternativeSecurityId">
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="extension_AgeRange" />
<OutputClaim ClaimTypeReferenceId="extension_Gender" />
</OutputClaims>
</TechnicalProfile>
<TechnicalProfile Id="AAD-UserWriteUsingAlternativeSecurityId">
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="extension_AgeRange" />
<PersistedClaim ClaimTypeReferenceId="extension_Gender" />
</PersistedClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>

Azure B2C: Default Display Name

Our Sys Admins want to be able to search for users and identify them by their Given Name and Surname, both of which we are collecting/requiring when they sign up.
We are NOT allowing users to specify a "Display Name" value when they create an account.
The management portal uses the "Display Name" as one of the three columns displayed in the "Users" pane.
It would be useful for Systems Administrators to be able to see the values given for Surname and Given Name in the user management portal to be able to identify the correct account.
I do know that there is a filter/search that I can type keywords into, and it does in fact find accounts with first or last names matching those, but if their name does not show in the "User Name" (which has been set to be the email), it is difficult to be sure of a match.
I could write code to update the display name using the graph API when we receive a connection from a new account, but I'd rather not.
Is it possible to configure the Display Name to use the "Given Name" + "Surname" values when viewing the list of users that have created B2C accounts?
Or perhaps, is it possible to show other attributes as columns as well as or instead of "Display Name"?
I use Azure B2C custom policy for signup process and had the same requirement - DisplayName = FirstName + LastName. I use claims transformation to achieve that.
<ClaimsTransformations>
<ClaimsTransformation Id="CreateDisplayNameFromFirstNameAndLastName" TransformationMethod="FormatStringMultipleClaims">
<InputClaims>
<InputClaim ClaimTypeReferenceId="givenName" TransformationClaimType="claim1" />
<InputClaim ClaimTypeReferenceId="surname" TransformationClaimType="claim2" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{0} {1}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
</ClaimsTransformations>
I fix that with a Web Job and Graph API. If that is an option for you I'm happy to share the code.

Resources