Post

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.

This post is licensed under CC BY 4.0 by the author.

Comments powered by Disqus.