I need to retieve both the current password and the new password the user has set in a XML custom policy and pass that to an API endpoint.
Is this possible? I've tried to get the password using the Read operation of AAD-Common. I created a technical profile which successfully retrieves a custom attribute extension_RegisteredUserId, but if I add an output claim for the password I get the error:
New-AzureADMSTrustFrameworkPolicy : Error occurred while executing NewTrustFrameworkPolicy
Code: AADB2C
Message: Validation failed: 1 validation error(s) found in policy "B2C_1A_PASSWORDRESET" of tenant "rhsb2cdev.onmicrosoft.com".Output Claim 'password'
is not supported in Azure Active Directory Provider technical profile 'Get-RegisteredUserId' of policy 'B2C_1A_PasswordReset'. If it is a claim with
default value, add AlwaysUseDefaultValue="true" to the output claim mapping.Output Claim 'password' is not supported in Azure Active Directory Provider
technical profile 'Get-RegisteredUserId' of policy 'B2C_1A_PasswordReset'. If it is a claim with default value, add AlwaysUseDefaultValue="true" to the
output claim mapping.
InnerError:
RequestId: 3346b8e6-eaa7-4b51-9755-a08b2ce04860
DateTimeStamp: Tue, 14 Feb 2023 08:26:41 GMT
HttpStatusCode: BadRequest
This is my technical profile I use:
<TechnicalProfile Id="Get-RegisteredUserId">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
<Item Key="UserMessageIfClaimsPrincipalDoesNotExist">An account could not be found for the provided user ID.</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.emailAddress" Required="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<!-- This is ok -->
<OutputClaim ClaimTypeReferenceId="extension_RegisteredUserId" PartnerClaimType="extension_RegisteredUserId"/>
<!-- This is not ok -->
<OutputClaim ClaimTypeReferenceId="password" PartnerClaimType="password"/>
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
which I call (in step 3 of the orchestration) prior to calling another technical profile that sends the extension_RegisteredUserId (and also should send the password) to an API (in step 4 of the orchestration). I can see the extension_RegisteredUserId getting sent across. Note also I do need to send the new password that they have chosen, if anyone has an idea of how to do this as well that would be great.
The user journey is this:
<UserJourney Id="PasswordReset">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="PasswordResetUsingEmailAddressExchange" TechnicalProfileReferenceId="LocalAccountDiscoveryUsingEmailAddress" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="NewCredentials" TechnicalProfileReferenceId="LocalAccountWritePasswordUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="GetRegisteredUserId" TechnicalProfileReferenceId="Get-RegisteredUserId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="Web-API-ChangePassword" TechnicalProfileReferenceId="REST-API-ChangePassword" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="5" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
The technical profile for the api call is this:
<!-- Custom Restful service -->
<TechnicalProfile Id="REST-API-ChangePassword">
<DisplayName>Registers a User on the Profile API</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">(url for api)</Item>
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">Body</Item>
<Item Key="AllowInsecureAuthInProduction">true</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_RegisteredUserId" PartnerClaimType="registeredUserId"/>
<InputClaim ClaimTypeReferenceId="password" PartnerClaimType="oldPassword"/>
<InputClaim ClaimTypeReferenceId="newPassword" PartnerClaimType="newPassword"/>
</InputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
Any help would be very welcome. thank you
Related
So I am using Sendgrid to send a validation code during password reset. I am able to get this to work great. After the Flow is completed I want to send another email letting them know the password was changed.
I am getting a bad request. But I think this is just because im doing something wrong in passing my claims.
my password reset flow is as follows.
<!--Sub Journey for Password Reset-->
<SubJourneys>
<SubJourney Id="PasswordReset" Type="Call">
<OrchestrationSteps>
<!-- Validate user's email address. -->
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="PasswordResetUsingEmailAddressExchange" TechnicalProfileReferenceId="LocalAccountDiscoveryUsingEmailAddress" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Show TOU-->
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="SelfAssertedConsentExchange" TechnicalProfileReferenceId="SelfAsserted-PasswordResetConsent" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Collect and persist a new password. -->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="NewCredentials" TechnicalProfileReferenceId="LocalAccountWritePasswordUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="SendPasswordChangeNotification" TechnicalProfileReferenceId="SendPasswordChangedEmail" />
</ClaimsExchanges>
</OrchestrationStep>
</OrchestrationSteps>
</SubJourney>
</SubJourneys>
This flow fails on orchestration step 4
my SendGrid Claims Transformation
<ClaimsTransformation Id="GenerateEmailChangedBody" TransformationMethod="GenerateJson">
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" TransformationClaimType="personalizations.0.to.0.email" />
<InputClaim ClaimTypeReferenceId="email" TransformationClaimType="personalizations.0.dynamic_template_data.email" />
</InputClaims>
<InputParameters>
<!-- Update the template_id value with the ID of your SendGrid template. -->
<InputParameter Id="template_id" DataType="string" Value="template ID"/>
<InputParameter Id="from.email" DataType="string" Value="me#email.com"/>
<!-- Update with a subject line appropriate for your organization. -->
<InputParameter Id="personalizations.0.dynamic_template_data.subject" DataType="string" Value="Account Password Changed"/>
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" TransformationClaimType="outputClaim"/>
</OutputClaims>
</ClaimsTransformation>
and finally my Technical Profile
<TechnicalProfile Id="SendPasswordChangedEmail">
<DisplayName>Use SendGrid's email API to send the code the the user</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://api.sendgrid.com/v3/mail/send</Item>
<Item Key="AuthenticationType">Bearer</Item>
<Item Key="SendClaimsIn">Body</Item>
<Item Key="ClaimUsedForRequestPayload">emailRequestBody</Item>
</Metadata>
<CryptographicKeys>
<Key Id="BearerAuthenticationToken" StorageReferenceId="B2C_1A_SendGridAPIKey" />
</CryptographicKeys>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="GenerateEmailChangedBody" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
</TechnicalProfile>
In my mind I think the customers email is not getting passed into the claim, and if thats the issue, I really not sure how to do that.
Any insight is useful!
Thank you!
I have a current policy that I need to send a confirmation email. I have a send grid account that is currently delivering a verification email and its working fine.
In my password reset flow, this is what I have.
<SubJourneys>
<SubJourney Id="PasswordReset" Type="Call">
<OrchestrationSteps>
<!-- Validate user's email address. -->
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="PasswordResetUsingEmailAddressExchange" TechnicalProfileReferenceId="LocalAccountDiscoveryUsingEmailAddress" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Show TOU-->
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="SelfAssertedConsentExchange" TechnicalProfileReferenceId="SelfAsserted-PasswordResetConsent" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Collect and persist a new password. -->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="NewCredentials" TechnicalProfileReferenceId="LocalAccountWritePasswordUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="GetAccessTokenPwdChangeNotification" TechnicalProfileReferenceId="SendPasswordChangeEmail" />
</ClaimsExchanges>
</OrchestrationStep>
</OrchestrationSteps>
</SubJourney>
In step one of this flow there is a verification email that takes place that uses send grid and it works fine.
step 4 is where the process seems to fail.
There is the technical profile for step 4
<ClaimsProvider>
<DisplayName>RestfulProvider</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="SendPasswordChangeEmail">
<DisplayName>Use SendGrid's email API to send the code the the user</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://api.sendgrid.com/v3/mail/send</Item>
<Item Key="AuthenticationType">Bearer</Item>
<Item Key="SendClaimsIn">Body</Item>
<Item Key="ClaimUsedForRequestPayload">emailChangedBody</Item>
</Metadata>
<CryptographicKeys>
<Key Id="BearerAuthenticationToken" StorageReferenceId="B2C_1A_SendGridSecret" />
</CryptographicKeys>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="GenerateEmailChangedBody" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="emailChangedBody" />
</InputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
That references this claims transformation.
<ClaimsTransformation Id="GenerateEmailChangedBody" TransformationMethod="GenerateJson">
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" TransformationClaimType="personalizations.0.to.0.email" />
</InputClaims>
<InputParameters>
<!-- Update the template_id value with the ID of your SendGrid template. -->
<InputParameter Id="template_id" DataType="string" Value="my template"/>
<InputParameter Id="from.email" DataType="string" Value="my email"/>
<!-- Update with a subject line appropriate for your organization. -->
<InputParameter Id="personalizations.0.dynamic_template_data.subject" DataType="string" Value="Account Password Changed"/>
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="emailChangedBody" TransformationClaimType="outputClaim"/>
</OutputClaims>
</ClaimsTransformation>
I am getting a bad request when this goes out.
My ONLY guess is that the email is not getting passed into that claims transformation so it does not know where to send the email.
Any help would be appreicated.
I'm asking this behalf of one mobile/web developer, i have created some custom policies in AD B2C. Now the developer is trying to integrate this with an application. For some reason i have used:
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:1.2.0
</DataUri>
as the data uri for signin techinical profile. And content defenition is:
<ContentDefinition Id="api.localaccount.login">
<LoadUri>my custom html</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:1.2.0</DataUri>
<Metadata>
<Item Key="DisplayName">Local Account Login</Item>
</Metadata>
<LocalizedResourcesReferences MergeBehavior="Prepend">
<LocalizedResourcesReference Language="en" LocalizedResourcesReferenceId="signin_en" />
<LocalizedResourcesReference Language="es" LocalizedResourcesReferenceId="signin_es" />
</LocalizedResourcesReferences>
</ContentDefinition>
Login TP:
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-CustomUserName-WithoutSignup">
<DisplayName>Local Account Signin UserName</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="setting.operatingMode">Username</Item>
<Item Key="setting.retryLimit">11</Item>
<Item Key="ContentDefinitionReferenceId">api.localaccount.login</Item>
<Item Key="UserMessageIfUserAccountDisabled">You account locked.</Item>
<Item Key="UserMessageIfUserAccountLocked">You've made too many incorrect attempts. Please try again later.</Item>
<Item Key="setting.showCancelButton">false</Item>
<Item Key="language.button_continue">LOG IN</Item>
<Item Key="ServiceThrottled">There are too many requests at this moment. Please wait for some time and try again.</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="signInName" Required="true" />
<OutputClaim ClaimTypeReferenceId="password" Required="true" />
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
<OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="false" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="CheckUserExist" />
<ValidationTechnicalProfile ReferenceId="SendOTP" />
<ValidationTechnicalProfile ReferenceId="login-NonInteractive" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
And my user journey is as follows:
<UserJourneys>
<UserJourney Id="OnBoarding">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsProviderSelection" ContentDefinitionReferenceId="api.idpselections">
<ClaimsProviderSelections>
<ClaimsProviderSelection TargetClaimsExchangeId="LocalAccountSigninEmailExchange" />
</ClaimsProviderSelections>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-CustomUserName-WithoutSignup" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="B2CUserProfileUpdateExchange" TechnicalProfileReferenceId="SelfAsserted-AccountValueUpdate" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="5" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadUserWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="6" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
</UserJourneys>
Policy works fine and when he tries to integrate it with replying party app, after user signup profile update journey called (above user journey), but it is showing the login screen again. How can bypass the login after registration? Is this something that we need to with policy?
Edit:
I'm using separate policies for sign in and sign up. After signin journey works as expected, but after signup the login screen comes.
Look at the starter pack again, you have removed the Session management technical profile reference (SM-AAD) from your sign in page. So during profile edit it won’t get skipped for a signed in user.
https://github.com/Azure-Samples/active-directory-b2c-custom-policy-starterpack/blob/master/LocalAccounts/TrustFrameworkBase.xml#L687
In our custom signIn/SignUp policy, whenever a user clicks on "Forgot your Password?" link, we don't want the user to re-type the email address when it navigates to passwordReset policy, so wanted to make use of login_hint, which is not working.
Here is my code
<UserJourney Id="PasswordReset">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="PasswordResetUsingEmailAddressExchange" TechnicalProfileReferenceId="LocalAccountDiscoveryUsingEmailAddress" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="NewCredentials" TechnicalProfileReferenceId="LocalAccountWritePasswordUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
<TechnicalProfile Id="LocalAccountDiscoveryUsingEmailAddress">
<DisplayName>Reset password using email address</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="IpAddressClaimReferenceId">IpAddress</Item>
<Item Key="ContentDefinitionReferenceId">api.localaccountpasswordreset</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" DefaultValue="{OIDC:LoginHint}"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingEmailAddress" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
When we run the policy , we notice "{OIDC:LoginHint}" displayed in the EmailAddress text box.
Please let me know how I can make login_hint to work with PasswordReset UserJourney.
Thanks,
I am creating a custom B2C policy and I am trying to replicate the password reset journey for local accounts created with a username.
I can read the username from AD but I am unsure how to validate the verified email address against the account.
Currently if the username is correct any email address can be used to verify.
Technical profile:
<TechnicalProfile Id="SA-LocalAccountDiscoveryUsingLogonName">
<DisplayName>Reset password using logon name</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="IpAddressClaimReferenceId">IpAddress</Item>
<Item Key="ContentDefinitionReferenceId">api.localaccountpasswordreset</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="signInName" Required="true" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingLogonName" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
Validation Technical Profile:
<TechnicalProfile Id="AAD-UserReadUsingLogonName">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">true</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="signInNames.userName" Required="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
User Journey:
<UserJourney Id="PasswordReset">
<OrchestrationSteps>
<!--Get user by username-->
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="PasswordResetUsingEmailAddressExchange" TechnicalProfileReferenceId="SA-LocalAccountDiscoveryUsingLogonName" />
</ClaimsExchanges>
</OrchestrationStep>
<!--Reset password-->
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="NewCredentials" TechnicalProfileReferenceId="SA-LocalAccountPasswordReset" />
</ClaimsExchanges>
</OrchestrationStep>
<!--Read remaining attributes of user-->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="ReadUser" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<!--Create token-->
<OrchestrationStep Order="4" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
If you write the email address to both the "otherMails" and "strongAuthenticationEmailAddress" properties during the sign-up policy, then you can verify that the email address is associated with the user name during the password reset policy using a REST API.
This REST API must be declared as a claims provider:
<ClaimsProvider>
<DisplayName>REST APIs</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="RestApi-CheckUser">
<DisplayName>Check User REST API</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">Insert the REST API endpoint URL</Item>
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">Body</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" />
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
The REST API can query the user object by the "signInNames" and "otherMails" properties using the Azure AD Graph API (you can't read the "strongAuthenticationEmailAddress" property using this Graph API) and, as described in the REST API walkthrough, then return 200 OK if the email address is associated with the user name or 409 Conflict if not so.
The REST API technical profile can then be invoked as a validation technical profile from the "SA-LocalAccountDiscoveryUsingLogonName" technical profile:
<TechnicalProfile Id="SA-LocalAccountDiscoveryUsingLogonName">
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="RestApi-CheckUser" />
<ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingLogonName" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
If the "RestApi-CheckUser" technical profile returns 200 OK, then the "AAD-UserReadUsingLogonName" technical profile is invoked and the end user can continue with the password reset. If the "RestApi-CheckUser" technical profile returns 409 Conflict, then the "AAD-UserReadUsingLogonName" technical profile isn't invoked and the end user can't continue.