Create Custom Policies For Azure Active Directory B2C

Introduction

In some cases, we need to allow users from a specific organization in Azure AD B2C flows. Azure B2C provides 2 types of flows. User flows and Custom Policies. Custom policies are used to handle complex business scenarios in B2C.

This article demonstrates how to configure specific Azure Active Directory organizational user to Azure AD B2C.

Prerequisite

  1. Configure custom policies in Azure AD B2C from Local And Social Account with MFA
  2. B2C Custom Policy
  3. Register a web application

Learning Objectives

  1. Register Azure AD Application
  2. Configuring optional claims
  3. Create policy key in Azure AD B2C
  4. Configure Azure AD as an identity provider
  5. Add a user journey and Add Identity provider
  6. Configure the relying party policy
  7. Upload custom policy
  8. Test custom policy

Let's get started. Before we move forward, Please confirm that you have registered Identity framework and proxy identity framework applications, created policy keys, and uploaded custom policies in Azure AD B2C tenant. If it is not done then follow prerequisites section first and come back again.  

Register Azure AD Application

  1. Sign in to Azure Portal
  2. Select App registration
  3. Click on new registration
  4. Enter name of the application “Azure AD B2C App”
  5. Accept the default selection of supported account types for this application
  6. Select web for redirect URI, enter the following URL in all lower case letters.
    https:// <B2C-tenant-name>.b2clogin.com/<B2C-tenant-name>.onmicrosoft.com/oauth2/authresp
    Replace “B2C-tenant-name” with the name of your azure ad B2C tenant.
  7. Click on register and copy application ID which we will be using it in a later step.
  8. Generate new secret from Certificates and secret and copy it.

create custom policy to connect Azure AD user in Azure AD B2C Flow

Configuring optional claims in Azure AD

If B2C requires claims from Azure AD tenant, you have to configure claims in your application.

  1. Go to your application which we have registered earlier.
  2. Click on token configurations under the manage section
  3. Add optional claims
  4. Select Token type, ID
  5. Select optional claims to add family_name and given_name
  6. Select add.

create custom policy to connect Azure AD user in Azure AD B2C Flow

 

Create policy key in Azure AD B2C

We must store application secret key to Azure AD Policy key section. This key will be using in custom B2C policy.

  1. Login to Azure AD B2C tenant
  2. Click on Identity Experience Framework under policies
  3. Click on policies under manage
  4. Click on add
    1. Select manual in options
    2. Enter name of the policy
    3. Enter secret key which we have copied while registering application in Azure AD
    4. Select signature for key usage
    5. Click on create

create custom policy to connect Azure AD user in Azure AD B2C Flow

Configure Azure AD as an identity provider

It is necessary to configure Azure Ad as a claim provider so that Azure Ad B2C may interact over an endpoint in order to allow users to sign in using Organization Accounts.The endpoint offers a collection of claims that are utilised by Azure Ad B2C to confirm that a particular user has successfully authenticated.

  1. Open the TrustFrameworkExtensions.xml file.
  2. Search for ClaimsProvider element. If does not exist, add it under the root element.
  3. Add new ClaimsProvider
<ClaimsProvider>
  <Domain>Contoso</Domain>
  <DisplayName>Login using Azure AD</DisplayName>
  <TechnicalProfiles>
    <TechnicalProfile Id="AzureAD-OpenIdConnect">
      <DisplayName>Contoso Employee</DisplayName>
      <Description>Login with your Contoso account</Description>
      <Protocol Name="OpenIdConnect"/>
      <Metadata>
        <Item Key="METADATA">https://login.microsoftonline.com/AzureADTenant.onmicrosoft.com/v2.0/.well-known/openid-configuration</Item>
        <Item Key="client_id">11111111-1111-1111-1111-111111111111</Item>
        <Item Key="response_types">code</Item>
        <Item Key="scope">openid profile</Item>
        <Item Key="response_mode">form_post</Item>
        <Item Key="HttpBinding">POST</Item>
        <Item Key="UsePolicyInRedirectUri">false</Item>
      </Metadata>
      <CryptographicKeys>
        <Key Id="client_secret" StorageReferenceId="B2C_1A_ContosoAppSecret"/>
      </CryptographicKeys>
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="oid"/>
        <OutputClaim ClaimTypeReferenceId="tenantId" PartnerClaimType="tid"/>
        <OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name" />
        <OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="family_name" />
        <OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
        <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" AlwaysUseDefaultValue="true" />
        <OutputClaim ClaimTypeReferenceId="identityProvider" PartnerClaimType="iss" />
      </OutputClaims>
      <OutputClaimsTransformations>
        <OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName"/>
        <OutputClaimsTransformation ReferenceId="CreateUserPrincipalName"/>
        <OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId"/>
        <OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId"/>
      </OutputClaimsTransformations>
      <UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin"/>
    </TechnicalProfile>
  </TechnicalProfiles>
</ClaimsProvider>
  • Replace "AzureADTenant" with your Azure Active Directory Tenant Name
  • Repalce "11111111-1111-1111-1111-111111111111" with Application ID which we have copied earlier.
  • Replace "B2C_1A_ContosoAppSecret" with Policy name which is configured in azure ad b2c

Add user journey

Identity Provider has been configured at this stage, but it is not available in any custom flows in B2C. Create a duplicate user journey from an existing one.

  1. Open a TrustFrameworkExtensions.xml file
  2. Search for UserJourneys
  3. Paste below code inside UserJourneys
<UserJourney Id="CustomSignUpSignIn">
  <OrchestrationSteps>

    <OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
      <ClaimsProviderSelections>
        <ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
        <ClaimsProviderSelection TargetClaimsExchangeId="AzureADContosoExchange" />
      </ClaimsProviderSelections>
      <ClaimsExchanges>
        <ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
      </ClaimsExchanges>
    </OrchestrationStep>

    <!-- Check if the user has selected to sign in using one of the social providers -->
    <OrchestrationStep Order="2" Type="ClaimsExchange">
      <Preconditions>
        <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
          <Value>objectId</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>
      <ClaimsExchanges>
        <ClaimsExchange Id="AzureADContosoExchange" TechnicalProfileReferenceId="AADRCDemo-OpenIdConnect" />
        <ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail" />
      </ClaimsExchanges>
    </OrchestrationStep>

    <!-- For social IDP authentication, attempt to find the user account in the directory. -->
    <OrchestrationStep Order="3" Type="ClaimsExchange">
      <Preconditions>
        <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
          <Value>authenticationSource</Value>
          <Value>localAccountAuthentication</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>
      <ClaimsExchanges>
        <ClaimsExchange Id="AADUserReadUsingAlternativeSecurityId" TechnicalProfileReferenceId="AAD-UserReadUsingAlternativeSecurityId-NoError" />
      </ClaimsExchanges>
    </OrchestrationStep>

    <!-- Show self-asserted page only if the directory does not have the user account already (i.e. we do not have an objectId). 
      This can only happen when authentication happened using a social IDP. If local account was created or authentication done
      using ESTS in step 2, then an user account must exist in the directory by this time. -->
    <OrchestrationStep Order="4" Type="ClaimsExchange">
      <Preconditions>
        <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
          <Value>objectId</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>
      <ClaimsExchanges>
        <ClaimsExchange Id="SelfAsserted-Social" TechnicalProfileReferenceId="SelfAsserted-Social" />
      </ClaimsExchanges>
    </OrchestrationStep>

    <!-- This step reads any user attributes that we may not have received when authenticating using ESTS so they can be sent 
      in the token. -->
    <OrchestrationStep Order="5" Type="ClaimsExchange">
      <Preconditions>
        <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
          <Value>authenticationSource</Value>
          <Value>socialIdpAuthentication</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>
      <ClaimsExchanges>
        <ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
      </ClaimsExchanges>
    </OrchestrationStep>
    <!-- The previous step (SelfAsserted-Social) could have been skipped if there were no attributes to collect 
         from the user. So, in that case, create the user in the directory if one does not already exist 
         (verified using objectId which would be set from the last step if account was created in the directory. -->
    <OrchestrationStep Order="6" Type="ClaimsExchange">
      <Preconditions>
        <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
          <Value>objectId</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>
      <ClaimsExchanges>
        <ClaimsExchange Id="AADUserWrite" TechnicalProfileReferenceId="AAD-UserWriteUsingAlternativeSecurityId" />
      </ClaimsExchanges>
    </OrchestrationStep>

    <!-- Phone verification: If MFA is not required, the next three steps (#5-#7) should be removed.
         This step checks whether there's a phone number on record,  for the user. If found, then the user is challenged to verify it. -->
    <OrchestrationStep Order="7" Type="ClaimsExchange">
      <ClaimsExchanges>
        <ClaimsExchange Id="PhoneFactor-Verify" TechnicalProfileReferenceId="PhoneFactor-InputOrVerify" />
      </ClaimsExchanges>
    </OrchestrationStep>

    <!-- Save MFA phone number: The precondition verifies whether the user provided a new number in the 
         previous step. If so, then the phone number is stored in the directory for future authentication 
         requests. -->
    <OrchestrationStep Order="8" Type="ClaimsExchange">
      <ClaimsExchanges>
        <ClaimsExchange Id="AADUserWriteWithObjectId" TechnicalProfileReferenceId="AAD-UserWritePhoneNumberUsingObjectId" />
      </ClaimsExchanges>
    </OrchestrationStep>
    <OrchestrationStep Order="9" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />

  </OrchestrationSteps>
  <ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>

Configure the relying party Policy

  1. Open existing “SignUpOrSignin.xml”
  2. Replace below code with <RelyingParty> node.
<RelyingParty>
  <DefaultUserJourney ReferenceId="CustomSignUpSignIn" />
  <Endpoints>
    <!--points to refresh token journey when app makes refresh token request-->
    <Endpoint Id="Token" UserJourneyReferenceId="RedeemRefreshToken" />
  </Endpoints>
  <TechnicalProfile Id="PolicyProfile">
    <DisplayName>PolicyProfile</DisplayName>
    <Protocol Name="OpenIdConnect" />
    <OutputClaims>
      <OutputClaim ClaimTypeReferenceId="displayName" />
      <OutputClaim ClaimTypeReferenceId="givenName" />
      <OutputClaim ClaimTypeReferenceId="surname" />
      <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
      <OutputClaim ClaimTypeReferenceId="identityProvider" />
      <OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />
    </OutputClaims>
    <SubjectNamingInfo ClaimType="sub" />
  </TechnicalProfile>
</RelyingParty>

Enter custom user journey ID which we have created in DefaultUserJourney node. 

Upload custom policy

  1. Upload below custom policies to Azure AD B2C tenant
    1. TrustFrameworkExtensions.xml
    2. SignUpOrSignin.xml

To test this application

  1. Open Azure AD B2C Tenant
  2. Click on Identity experience framework under policies
  3. Select “B2C_1A_SIGNUP_SIGNIN” policy
  4. Select Application where redirect URI is https://jwt.ms
  5. Click on run now.

create custom policy to connect Azure AD user in Azure AD B2C Flow

Conclusion

We have learned how to create a custom policy to connect Azure AD user in Azure AD B2C Flow.

Let me know in comments if anyone has found any issue in provision. I am happy to help you.