Azure B2C password reset link
Forget forcing users to sign-in before resetting passwords! This guide unlocks the secrets 📜 of crafting an Azure B2C custom policy for an unsolicited 🔐 password reset experience. Dive into the essential steps, from crafting error messages ⚠️ to configuring claims providers and orchestration steps. Empower your users and streamline your authentication workflow with this powerful technique.
A password reset link enables your end user to set a new password for Azure B2C. This link authenticates the user without needing them to enter the credentials again and directly takes them to the new password page.
1. Send users’s email as input
Please follow steps on this post to send user’s email
as input to the custom policy.
2. Add ClaimsTransformation in your policy.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<ClaimsTransformations>
<ClaimsTransformation Id="CreateUnsolicitedErrorMessage" TransformationMethod="CreateStringClaim">
<InputParameters>
<InputParameter Id="value" DataType="string" Value="You cannot sign-in without invitation" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="errorMessage" TransformationClaimType="createdClaim" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="CreateUserNotFoundErrorMessage" TransformationMethod="CreateStringClaim">
<InputParameters>
<InputParameter Id="value" DataType="string" Value="You aren't registered in the system!" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="errorMessage" TransformationClaimType="createdClaim" />
</OutputClaims>
</ClaimsTransformation>
3. Add ClaimsProvider
This <ClaimsProvider> ... </ClaimsProvider>
should be added to the same XML file in which you have defined your OrchestrationSteps
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<ClaimsProvider>
<DisplayName>Self Asserted</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="SelfAsserted-Error">
<DisplayName>Unsolicited error message</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="setting.showContinueButton">false</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="errorMessage" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="errorMessage" />
</OutputClaims>
</TechnicalProfile>
<TechnicalProfile Id="SelfAsserted-Unsolicited">
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CreateUnsolicitedErrorMessage" />
</InputClaimsTransformations>
<IncludeTechnicalProfile ReferenceId="SelfAsserted-Error" />
</TechnicalProfile>
<TechnicalProfile Id="SelfAsserted-UserNotFound">
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CreateUserNotFoundErrorMessage" />
</InputClaimsTransformations>
<IncludeTechnicalProfile ReferenceId="SelfAsserted-Error" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
4. Update the OrchestrationSteps
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<OrchestrationSteps>
<!-- Read input claims from the id_token_hint-->
<OrchestrationStep Order="1" Type="GetClaims" CpimIssuerTechnicalProfileReferenceId="IdTokenHint_ExtractClaims" />
<!-- Check if user tries to run the policy without invitation -->
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>email</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SelfAsserted-Unsolicited" TechnicalProfileReferenceId="SelfAsserted-Unsolicited" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Read the user properties from the directory-->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadUsingEmailAddress" TechnicalProfileReferenceId="AAD-UserReadUsingEmailAddress" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Check if the user exists -->
<OrchestrationStep Order="4" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SelfAssertedUserNotFound" TechnicalProfileReferenceId="SelfAsserted-UserNotFound" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Set New Password -->
<OrchestrationStep Order="5" Type="InvokeSubJourney">
<JourneyList>
<Candidate SubJourneyReferenceId="SetNewPassword" />
</JourneyList>
</OrchestrationStep>
<!-- Issue an access token-->
<OrchestrationStep Order="6" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
<OrchestrationSteps>
5. Done
Now your custom policy will not require the user to sign-in before setting a new password.
Feel free to add more OrchestrationSteps
in your UserJourney
after Step-4.
The password-reset URL with id_token_hint
will be a sensitive URL as it can bypass the sign-in step, so handle it carefully.
Ideally this URL should be sent to the user via a secured email.
Comments powered by Disqus.