Integrating Descope with Azure AD B2C as a Federated Identity Provider

In this guide, we'll walk through the steps to configure Descope as a federated Identity Provider (IdP) with Azure AD B2C. This setup allows you to use Descope Flows and authentication methods, such as passkeys and webauthn, while retaining the use of Azure B2C and the Active Directory in your application.

Prerequisites

  • An active Azure AD B2C Tenant.
  • An active Descope Account.

Step 1: Setting up your Descope Flow

Note: If you want to use Passkeys, make sure that you're verifing the user's email the first time you create a passkey for a specific user account and that you're checking to make sure user's without a verified email cannot login, for security purposes.

Descope OIDC with Auth0 as auth provider flow configuration 1.

Your flows are automatically hosted with our Descope Auth Hosting Application. To learn more about our hosted app, you can read about it in our Docs page here. You can also host the flow yourself with any one of our client SDKs as well.

Step 2: Configuring Descope as an OpenID Connect (OIDC) Identity Provider in Azure AD B2C

Finding Your Azure AD B2C Tenant Domain

  1. Log in to Azure Portal: Navigate to the Azure Portal and sign in.
  2. Access Azure AD B2C Service: Search for and select the Azure AD B2C service.
  3. Tenant Overview: Your B2C Tenant domain is displayed in the format <Your Tenant Name>.onmicrosoft.com.
Tenant domain location in Azure B2C Portal

Note: For those who already have a working application with a Azure AD B2C tenant, you can also alter your pre-existing flow with the following steps.

Setting Up a New User Flow in Azure AD B2C

  1. Create User Flow: In Azure AD B2C, go to User flows and create a new sign-up and sign-in flow.
Create new user flow in Azure B2C Portal.
Create new user flow in Azure B2C Portal.
  1. Add Identity Provider: In Azure AD B2C, navigate to Identity providers and select "New OpenID Connect provider". Here you'll need to gather the following information the Descope Console:
InformationDescription
Metadata URLFound in the Descope Console under: Applications -> Select your App -> App Discovery URL. See picture below for location.
Client IDYour Descope Project ID, located in the Console under Project Settings.
Client SecretYour Descope Access Key, generated in the Console under Access Keys.
ScopesShould be set to openid profile email descope.custom_claims. Optionally, descope.claims can be included for passing role/tenant information to Azure.

You'll then input it in the identity provider config in the Azure Portal like this:

Azure identity provider config.

This is where you can get the Metadata URL:

Create new user flow in Azure B2C Portal.

Note: If you wish to use a different flow or want to host the flow yourself, you'll want to change the Flow Hosting URL to wherever the flow is located, and which one in the query parameter at the end of the URL string.

  1. Configure User Attributes: Choose the user attributes you want to collect and return during the authentication process. You will need User ID at the very least, which is typically mapped to sub.
Note: This part you can configure exactly how you want, with you passing the claims in your flow with the Custom Claims action at the end.
User attributes in azure identity provider config

This is how the attribute keys are mapped in the flow:

Descope flow custom claims action

Once you've completed these steps, you can save your identity provider configuration and then proceed to configuring the rest of your Azure User Flow.

Step 3: Adjusting Azure AD B2C Settings

  1. Select Descope as Identity Provider: Ensure Descope is selected as the Identity Provider in your User Flow.
  2. Disable Local Accounts and Other Identity Providers: To enforce authentication via Descope, disable local account sign-ins and other identity providers in the User Flow.
Note: If you still want to use other auth methods as a backup, you can continue allowing Local Accounts (with email) however you won't be able to have a seamless redirect to your hosted flow page.
Disabled local accounts in azure config

By doing this, you'll make sure that you're automatically redirected to either the Hosted Auth page (auth.descope.io) or wherever else you're hosting the flow component, configured in the Descope Console under Applications.

Step 4: Merging User Identities Between Descope and Azure B2C

At this point, you're almost done with the setup process. The problem right now is that if you sign in as a pre-existing user with Descope, instead of merging the identities and logging you in as that same pre-existing user, a new user will be created.

To avoid this duplication of users, you'll need to create a Custom Policy in your Azure B2C configuration to handle this.

Typically this can require a bit of complexity in setting up, however, you can use the Azure B2C Policy Setup Tool to help automatically add the necessary policy to your Azure instance. Follow the steps below to do this:

If you want to look at other pre-built custom policies you can visit this website, or if you want to download the Azure Custom Policy starter pack and modify them yourself you can get it from GitHub

Instructions for Setting Up Auto-Linking Policy

  1. Navigate to the Setup Tool, enter your Azure B2C domain, and deploy the custom starter pack:

Note: You'll need to give permission to the app in order for it to deploy the custom starter pack on your behalf. If you wish to do this manually, follow the instructions on Microsoft's documentation page

Custom starter pack deployment

Then select the next few links shown below, to complete the setup and configuration of the starter pack.

Second step in the setup process
  1. Once the starter pack has been deployed, navigate to the list of custom policies here, input your Azure Domain and auto-account-linking under Sample Folder Name, and click Deploy custom policy sample.
Installing auto-linking policy

This will install the custom policy that will automatically link user accounts together with the same email address.

After this is complete, you should see this screen:

Installation of auto-linking policy complete
  1. Next, you'll need to download your current user flow XML, for which you'll need to modify over the next few steps. This can be done in the Azure Portal here:
Downloading the user flow XML
  1. Then, you'll need to merge the that existing policy, with this account linking policy:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TrustFrameworkPolicy
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06"
  PolicySchemaVersion="0.3.0.0"
  TenantId="<YOUR TENANT>.onmicrosoft.com"
  PolicyId="<YOUR POLICY ID>"
  PublicPolicyUri="http://<YOUR TENANT>.onmicrosoft.com/<YOUR POLICY ID>"
>

  <BasePolicy>
    <TenantId><YOUR TENANT>.onmicrosoft.com</TenantId>
    <PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
  </BasePolicy>

  <BuildingBlocks>
    <ClaimsTransformations>
      <ClaimsTransformation Id="CreateRandomPassword" TransformationMethod="CreateRandomString">
        <InputParameters>
          <InputParameter Id="randomGeneratorType" DataType="string" Value="GUID" />
        </InputParameters>
        <OutputClaims>
          <OutputClaim ClaimTypeReferenceId="newPassword" TransformationClaimType="outputClaim" />
        </OutputClaims>
      </ClaimsTransformation>
    </ClaimsTransformations>
  </BuildingBlocks>

  <ClaimsProviders>
    <ClaimsProvider>
      <DisplayName>Azure Active Directory</DisplayName>
      <TechnicalProfiles>
        <!-- 
          Extend with storing the emailAddress as way to signin with Local Account. That way we're
          blocking the possibility of creating a duplicate account with the same email account.
          Yes, the local account doesn't have a password, but if you really want to use that way of 
          logging in, you can do Password Reset. Also, this blocks the opposite, ie, signing in 
          with alicecontoso@gmail.com via Google federation if the user already signed up with 
          alicecontoso@gmail.com as a local account.
        -->
        <TechnicalProfile Id="AAD-UserWriteUsingAlternativeSecurityId">
          <InputClaimsTransformations>
            <InputClaimsTransformation ReferenceId="CreateRandomPassword" />
          </InputClaimsTransformations>
          <PersistedClaims>
            <PersistedClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.emailAddress" />
            <PersistedClaim ClaimTypeReferenceId="newPassword" PartnerClaimType="password"/>
            <PersistedClaim ClaimTypeReferenceId="passwordPolicies" DefaultValue="DisablePasswordExpiration" />
          </PersistedClaims>
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>
  </ClaimsProviders>

  <RelyingParty>
    <DefaultUserJourney ReferenceId="SignUpOrSignIn" />
    <TechnicalProfile Id="PolicyProfile">
      <DisplayName>PolicyProfile</DisplayName>
      <Protocol Name="OpenIdConnect" />
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="displayName" />
        <OutputClaim ClaimTypeReferenceId="givenName" />
        <OutputClaim ClaimTypeReferenceId="surname" />
        <OutputClaim ClaimTypeReferenceId="email" />
        <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
        <OutputClaim ClaimTypeReferenceId="identityProvider" />
        <OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />
      </OutputClaims>
      <SubjectNamingInfo ClaimType="sub" />
    </TechnicalProfile>
  </RelyingParty>

</TrustFrameworkPolicy>

The way you merge these is by following the comments in the XML below and doing each of the instructions one by one:

<!-- Replace YOUR TENANT and YOUR EXISTING POLICY ID, everywhere you see it -->
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TrustFrameworkPolicy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06"
  PolicySchemaVersion="0.3.0.0"
  TenantId="<YOUR TENANT>.onmicrosoft.com"
  PolicyId="<YOUR EXISTING POLICY ID (e.g. `B2C_1_sign-up-or-in`)>"
  PublicPolicyUri="http://<YOUR TENANT>.onmicrosoft.com/<YOUR POLICY ID>"

  <BasePolicy>
    <TenantId><YOUR TENANT>.onmicrosoft.com</TenantId>
    <PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
  </BasePolicy>

  <!-- Include all BuildingBlocks, ClaimsProviders, and RelyingParty sections from both policies (existing and the one above) -->
  <!-- Merge BuildingBlocks -->
  <BuildingBlocks>
    <!-- Include all ClaimsTransformations, ContentDefinitions, ClaimsSchema from both policies -->
    <!-- For example, ClaimsTransformations from the account linking policy -->
    <ClaimsTransformations>
      <ClaimsTransformation Id="CreateRandomPassword" TransformationMethod="CreateRandomString">
        <InputParameters>
          <InputParameter Id="randomGeneratorType" DataType="string" Value="GUID" />
        </InputParameters>
        <OutputClaims>
          <OutputClaim ClaimTypeReferenceId="newPassword" TransformationClaimType="outputClaim" />
        </OutputClaims>
      </ClaimsTransformation>
    </ClaimsTransformations>
    <!-- Add other BuildingBlocks elements here -->
  </BuildingBlocks>

  <!-- Merge ClaimsProviders -->
  <ClaimsProviders>
    <!-- Include all ClaimsProviders from both policies -->
    <!-- For example, ClaimsProvider from the account linking policy -->
    <ClaimsProvider>
      <!-- Example TechnicalProfile -->
      <DisplayName>Azure Active Directory</DisplayName>
      <TechnicalProfiles>
        <TechnicalProfile Id="AAD-UserWriteUsingAlternativeSecurityId">
          <!-- Configuration details -->
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>
    <!-- Add other ClaimsProviders elements here -->
  </ClaimsProviders>

  <!-- Merge RelyingParty -->
  <RelyingParty>
    <!-- Include all elements from the RelyingParty section of both policies -->
    <!-- For example, DefaultUserJourney and TechnicalProfile from the sign-up-or-in policy -->
    <DefaultUserJourney ReferenceId="SignUpOrSignIn" />
    <TechnicalProfile Id="PolicyProfile">
      <!-- Configuration details -->
    </TechnicalProfile>
    <!-- Add other RelyingParty elements here -->
  </RelyingParty>
</TrustFrameworkPolicy>
  1. Finally, you'll want to upload and test your new modified policy. You can do so under Upload Custom Policies in the Azure Portal:
Uploading custom policy

From this screen, once the policy is uploaded, you can test the user journey to make sure everything is working ok.

For this, you can use the Run now feature in the Azure portal. You'll want to verify that users with the same email address in Descope and Azure AD B2C are merged correctly.

Once you've done this, you should be all set to start using Descope in your apps!

Understanding the Sample App

There is a React and .NET sample app showcases how to use Descope with Azure B2C. These sample apps can help you understand how Descope integrates with Azure, and how the OIDC redirection works.

These apps are also helpful if you're also interested in how you can get user information and display it in your frontend, from the attributes/claims you pass back from Azure to the application. These are controlled under Application Claims in your Azure B2C User Flow configuration page.

  1. React - GitHub Link
  2. ASP.NET - GitHub Link