In AAD B2C I have users created with 2 identities. I used graph to create them with this body :
{
"displayName": "John Doe",
"mail":"johndoe19287456@gmail.com",
"identities": [
{
"signInType": "userName",
"issuer": "mytenant.onmicrosoft.com",
"issuerAssignedId": "606198"
},
{
"signInType": "emailAddress",
"issuer": "mytenant.onmicrosoft.com",
"issuerAssignedId": "johndoe19287456@gmail.com"
}
],
"passwordProfile" : {
"password": "Soleil!23",
"forceChangePasswordNextSignIn": false
},
"passwordPolicies": "DisablePasswordExpiration"
}
This allow the user to connect either with an email (johndoe19287456@gmail.com) or an ID (606198).
When a user input his ID and then click on the "Forgot password?" link, I'd like to get the email value from AAD so the user cannot input whatever he wants. But I'd still like it to be "verified" by sending a code to that email address. I have 2 problems :
- I can't fnd a way to get the email value from AzureActiveDirectoryProvider
- I can't find a way to populate the Verified.Email field (and make it readonly).
Here's a sample of one of the many things I've tried yet.
Building blocks custom claims :
<ClaimType Id="ReadOnlyEmail">
<DisplayName>Verified Email Address</DisplayName>
<DataType>string</DataType>
<UserInputType>Readonly</UserInputType>
</ClaimType>
<ClaimType Id="emailFromAAD">
<DisplayName>Email from AAD</DisplayName>
<DataType>string</DataType>
<UserHelpText />
<UserInputType>Readonly</UserInputType>
</ClaimType>
<ClaimType Id="readOnlySignInName">
<DisplayName>Sign in name</DisplayName>
<DataType>string</DataType>
<UserHelpText />
<UserInputType>Readonly</UserInputType>
</ClaimType>
<ClaimType Id="emailValue">
<DisplayName>Matched mail</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="isEmailBoolean">
<DisplayName>is Email</DisplayName>
<DataType>boolean</DataType>
</ClaimType>
<ClaimType Id="strongAuthenticationEmailAddress">
<DisplayName>string</DisplayName>
<DataType>string</DataType>
<AdminHelpText>Email address that the user can use for strong authentication.</AdminHelpText>
<UserHelpText>Email address to use for strong authentication.</UserHelpText>
<UserInputType>TextBox</UserInputType>
</ClaimType>
Claims transformation :
<ClaimsTransformation Id="CopySignInNameFromReadOnly" TransformationMethod="FormatStringClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="readOnlySignInName" TransformationClaimType="inputClaim" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{0}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="signInName" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
<!-- If signin name match the regex, it is an email identifier. Oterwise, it'll be considered as username -->
<ClaimsTransformation Id="isEmail" TransformationMethod="setClaimsIfRegexMatch">
<InputClaims>
<InputClaim ClaimTypeReferenceId="readOnlySignInName" TransformationClaimType="claimToMatch" />
</InputClaims>
<InputParameters>
<InputParameter Id="matchTo" DataType="string" Value="[^@]+@[^\.]+\..+" />
<InputParameter Id="outputClaimIfMatched" DataType="string" Value="isEmail" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="emailValue" TransformationClaimType="outputClaim" />
<OutputClaim ClaimTypeReferenceId="isEmailBoolean" TransformationClaimType="regexCompareResultClaim" />
</OutputClaims>
</ClaimsTransformation>
Technical profiles:
<!-- Password reset step 1b - Included in step SelfAsserted-LocalAccountLookup-Combined-PwdReset
That's where the input claim is define. signInName = the Username field on the screen -->
<TechnicalProfile Id="SelfAsserted-LocalAccountLookup-Combined-SignUp">
<DisplayName>Local Account Sign Up</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</Item>
<Item Key="IncludeClaimResolvingInClaimsHandling">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="readOnlySignInName" DefaultValue="{OIDC:LoginHint}" AlwaysUseDefaultValue="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="readOnlySignInName" Required="true" />
<OutputClaim ClaimTypeReferenceId="isEmailBoolean" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CopySignInNameFromReadOnly" />
</OutputClaimsTransformations>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="regexAnalysisUsername" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
<!-- Password reset step 1a. Includes SelfAsserted-LocalAccountLookup-Combined-SignUp
Input claim is defined in this. Here, we define output claims -->
<TechnicalProfile Id="SelfAsserted-LocalAccountLookup-Combined-PwdReset">
<DisplayName>Reset password</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="setting.showCancelButton">false</Item>
<Item Key="IpAddressClaimReferenceId">IpAddress</Item>
<Item Key="ContentDefinitionReferenceId">api.localaccountpasswordreset</Item>
<Item Key="UserMessageIfClaimsTransformationBooleanValueIsNotEqual">Your account has been locked. Contact your support person to unlock it, then try again.</Item>
</Metadata>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="readOnlySignInName" Required="true" />
<OutputClaim ClaimTypeReferenceId="isEmailBoolean" />
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="emailFromAAD" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingIdentifier" />
<ValidationTechnicalProfile ReferenceId="regexAnalysisUsername" />
</ValidationTechnicalProfiles>
<IncludeTechnicalProfile ReferenceId="SelfAsserted-LocalAccountLookup-Combined-SignUp" />
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
<!-- Password reset step 1c. Verify signin name on the "Continue" button clicked in the first screen -->
<TechnicalProfile Id="AAD-UserReadUsingIdentifier">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="readOnlySignInName" PartnerClaimType="signInNames" Required="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="emailFromAAD" PartnerClaimType="signInNames.emailAddress" />
<OutputClaim ClaimTypeReferenceId="strongAuthenticationEmailAddress" />
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
<!-- Passwod reset step 2. Only if sign in data was a username -->
<TechnicalProfile Id="LocalAccountDiscoveryUsingUserNameAndValidateStrongAuthenticationEmailAddress">
<DisplayName>Reset password using username</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<!-- Other values maybe defined in localized api.selfasserted.fr and api.selfasserted.en -->
<Item Key="IpAddressClaimReferenceId">IpAddress</Item>
<Item Key="ContentDefinitionReferenceId">api.localaccountpasswordreset</Item>
<Item Key="AllowGenerationOfClaimsWithNullValues">true</Item>
<Item Key="UserMessageIfClaimsTransformationStringsAreNotEqual">An account could not be found for the provided User ID and email combination.</Item>
<Item Key="UserMessageIfClaimsTransformationBooleanValueIsNotEqual">Your account has been locked. Contact your support person to unlock it, then try again.</Item>
<Item Key="LocalAccountType">Username</Item>
<Item Key="LocalAccountProfile">true</Item>
<!-- Reduce the default self-asserted retry limit of 7 for the reset journey -->
<Item Key="setting.retryLimit">5</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="readOnlySignInName" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="readOnlySignInName" Required="true" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" DefaultValue="strongAuthenticationEmailAddress" Required="true" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
<OutputClaim ClaimTypeReferenceId="strongAuthenticationEmailAddress" />
<OutputClaim ClaimTypeReferenceId="emailFromAAD" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingUserNameAndValidateStrongAuthenticationEmailAddress" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
For now, I get the first screen with the readonly ID working.
The next screen presents the textboxes for email. The Email from AAD shouldn't visible.
But anyway it is empty, showing that it didn't get anything from AAD or I failed to properly store it in the claim bag or pass it down. Note that I have tried getting the value both from "strongAuthenticationEmailAddress" and "signInNames.emailAddress" based on Microsoft documentation but none of it works. Maybe it's in the way I define my output claim with PartnerClaimType in the AAD-UserReadUsingIdentifier profile?
To make it clear for everyone, here's what I'd like to have. A simple page with 2 reaonly fields with a button to send the code and then another one to continue after the code has been verified.
Can anyone help me with this one?
I started from the B2C custom policies starter pack and added customization from this community repo.
UPDATE Here is what I get from MS Graph when querying for my user :
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users(identities,id,displayName,mail,surname,userPrincipalName,extension_06a19ccd80a0430c9730c62e4d96c895_ClientID,extension_06a19ccd80a0430c9730c62e4d96c895_requiresMigration)/$entity",
"id": "051e***************",
"displayName": "test - Tests2",
"mail": "johndoe19287456@gmail.com",
"surname": null,
"userPrincipalName": "051ea****************@mytenant.onmicrosoft.com",
"identities": [
{
"signInType": "emailAddress",
"issuer": "mytenant.onmicrosoft.com",
"issuerAssignedId": "johndoe19287456@gmail.com"
},
{
"signInType": "userName",
"issuer": "mytenant.onmicrosoft.com",
"issuerAssignedId": "606198"
},
{
"signInType": "userPrincipalName",
"issuer": "mytenant.onmicrosoft.com",
"issuerAssignedId": "051ea****************@mytenant.onmicrosoft.com"
}
]
}