Integrate DRS with Azure AD B2C

Integrate Detection and response services into applications relying on Azure AD B2C as IDP. This describes how to assess sign-in/sign-up attempts for risk and fraud, and respond accordingly using custom policies.

How it works

The integration includes the following components:

  • Azure AD B2C tenant : Authenticates the user and hosts a script that collects device telemetry as users execute a target policy. It blocks or challenges sign-in/up attempts based on the risk recommendation returned by Mosaic.
  • Custom UI templates : Customizes HTML content of the pages rendered by Azure AD B2C. These pages include the JavaScript snippets required for risk detection.
  • Mosaic data collection service : Dynamically embedded script that logs device telemetry, which is used to continuously assess risk during user interactions.
  • Mosaic DRS API endpoint : Provides the risk recommendation based on collected data. Azure AD B2C communicates with this endpoint using a REST API connector.
  • Azure Functions : Your hosted API endpoint that's used to obtain a recommendation from the DRS API endpoint via the API connector.

The following architecture diagram illustrates the implementation described in the guide:

Diagram of the Mosaic and Azure AD B2C architecture

  1. The user opens the Azure AD B2C sign-in page.
  2. The page initializes the Platform SDK, which starts streaming device telemetry to Mosaic.
  3. Azure AD B2C reports a login action event to Mosaic in order to obtain an action token.
  4. Mosaic returns an action token, and Azure AD B2C proceeds with the user sign in/up.
  5. After authenticating the user, Azure AD B2C requests a risk recommendation from Mosaic via Azure Functions.
  6. The Azure Function sends Mosaic the recommendation request with the action token.
  7. Mosaic returns a recommendation (challenge/allow/deny) based on the collected device telemetry.
  8. The Azure Function passes the recommendation result to Azure AD B2C to handle accordingly.
  9. Azure AD B2C performs additional steps if needed (e.g., MFA) and completes the sign-in/up flow.

Before you start

To get started, you need:

Step 1: Get client credentials

Client credentials are used to identify your app and generate access tokens for authorizing Mosaic requests. To obtain them, you'll need to create an application in the Admin Portal (if you don’t have one yet).

  1. From Applications , click Add application .
  2. Add the friendly application name to display in the Admin Portal.
  3. Add a client display name, and your website URL as a redirect URI (e.g., https://your-domain.com ).
    Note

    These fields are required for all Mosaic apps, but won’t be used for Detection and Response.

  4. Click Add to create your application. This will automatically generate your client credentials.

Step 2: Create your custom UI

Start by integrating DRS into your Azure AD B2C frontend application. This involves creating a custom sign-in page that integrates the Platform SDK, and will replace the default Azure AD B2C UI. Once activated, DRS starts collecting telemetry data for the user interacting with your app. DRS returns an action token that Azure AD B2C will need to get a risk recommendation.

  1. Prepare a custom HTML file for your sign-in page based on the sample templates . Add the script below to load and initialize the SDK, and to obtain an action token. The returned action token should be stored in a hidden HTML element ( ts-drs-response in our example).
Copy
Copied
    <!-- Function that obtains an action token -->
    <script>
    function fill_token() {
        window.tsPlatform.drs.triggerActionEvent("login", { correlationId: "[CORRELATION_ID]" }).then((actionResponse) => {
            let actionToken = actionResponse.actionToken;
            document.getElementById("ts-drs-response").value = actionToken;
            console.log(actionToken);
        });
    }
    </script>

    <!-- Loads DRS SDK -->
    <script src="https://platform-websdk.transmitsecurity.io/platform-websdk/latest/ts-platform-websdk.js" defer> </script>

    <!-- Upon page load, initializes DRS SDK and calls the fill_token function -->
    <script defer>
    window.onload = function() {
        if (window.tsPlatform) {
            // Client ID found in the app settings in Mosaic Admin Portal
            window.tsPlatform.initialize({ clientId: "[clientId]" });
            console.log("Mosaic platform initialized");
            fill_token();
        } else {/
            console.error("Mosaic platform failed to load");
        }
    };
    </script>
  1. Host the HTML page on a Cross-Origin Resource Sharing (CORS) enabled web endpoint by creating a storage account and adding CORS support for Azure Storage .

Step 3: Create an Azure Function

Azure AD B2C can obtain a risk recommendation from Mosaic using a REST API connector. Passing this request through an intermediate web API (such as using Azure Functions) provides more flexibility in your implementation logic. This step creates an Azure function that uses the action token from the frontend application to get a recommendation.

  1. Create the entry point of your Azure Function, an HTTP-triggered function that processes incoming HTTP requests.
Copy
Copied
public static async Task<HttpResponseMessage> Run(HttpRequest req, ILogger log)
{
    // Function code goes here
}
  1. Extract the action token from the request. Your Azure custom policy will define whether it's passed in the query or body.
Copy
Copied
// Checks for the action token in the query string
string actionToken = req.Query["actiontoken"];

// Checks for the action token in the request body
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
actionToken = actionToken ?? data?.actiontoken;
  1. Validate the action token by checking that the provided value isn't empty or null:
Copy
Copied
// Returns an error response if the action token is missing
if (string.IsNullOrEmpty(actionToken))
   {
       var respContent = new { version = "1.0.0", status = (int)HttpStatusCode.BadRequest, userMessage = "Invalid or missing action token" };
       var json = JsonConvert.SerializeObject(respContent);
       log.LogInformation(json);
       return new HttpResponseMessage(HttpStatusCode.BadRequest)
       {
           Content = new StringContent(json, Encoding.UTF8, "application/json")
       };
   }
  1. Call the Recommendations API . The Client ID and Client Secret obtained in Step 1 should be used to generate bearer tokens for API authorization. Make sure to add the necessary environment variables (like ClientId and ClientSecret) in your local.settings.json file.
Copy
Copied
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {transmitSecurityApiKey}");

// Add code here that sends this GET request:
// https://api.transmitsecurity.io/risk/v1/recommendation?action_token=[YOUR_ACTION_TOKEN]

HttpResponseMessage response = await client.GetAsync(urlWithActionToken);
  1. Process the API response. The code below forwards the API response if successful; otherwise, handles any errors.
Copy
Copied
 if (response.IsSuccessStatusCode)
   {
       log.LogInformation(responseContent);
       return new HttpResponseMessage(HttpStatusCode.OK)
       {
           Content = new StringContent(responseContent, Encoding.UTF8, "application/json")
       };
   }
   else
   {
       var errorContent = new { version = "1.0.0", status = (int)response.StatusCode, userMessage = "Error calling Mosaic API" };
       var json = JsonConvert.SerializeObject(errorContent);
       log.LogError(json);
       return new HttpResponseMessage(response.StatusCode)
       {
           Content = new StringContent(json, Encoding.UTF8, "application/json")
       };
   }

Step 4: Configure custom policies

You can incorporate DRS into your Azure B2C application by extending your custom policies.

  1. Download the custom policy starter pack to get started (see Create custom policies in Azure AD B2C )
  2. Create a new file that inherits from TrustFrameworkExtensions , which will extend the base policy with tenant-specific customizations for DRS.
Copy
Copied
  <BasePolicy>
    <TenantId>YOUR AZURE TENANT</TenantId>
    <PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
  </BasePolicy>
  1. In the BuildingBlocks section, define actiontoken , ts-drs-response , and ts-drs-recommendation as claims:
Copy
Copied
  <BuildingBlocks>
    <ClaimsSchema>
      <ClaimType Id="ts-drs-response">
        <DisplayName>ts-drs-response</DisplayName>
        <DataType>string</DataType>
        <UserHelpText>Parameter provided to the DRS service for the response</UserHelpText>
        <UserInputType>TextBox</UserInputType>
      </ClaimType>
      <ClaimType Id="actiontoken">
        <DisplayName>actiontoken</DisplayName>
        <DataType>string</DataType>
        <UserHelpText />
        <UserInputType>TextBox</UserInputType>
      </ClaimType>
      <ClaimType Id="ts-drs-recommendation">
        <DisplayName>recommendation</DisplayName>
        <DataType>string</DataType>
        <UserHelpText />
        <UserInputType>TextBox</UserInputType>
      </ClaimType>
    </ClaimsSchema>
  <BuildingBlocks>
  1. In the BuildingBlocks section, add a reference to your custom UI:
Copy
Copied
  <BuildingBlocks>
    <ClaimsSchema>
      <!-- your claim schemas-->
    </ClaimsSchema>

    <ContentDefinitions>
      <ContentDefinition Id="api.selfasserted">
      <!-- URL of your hosted custom HTML file-->
        <LoadUri>YOUR_SIGNIN_PAGE_URL</LoadUri>
      </ContentDefinition>
    </ContentDefinitions>
  </BuildingBlocks>
  1. In the ClaimsProviders section, configure a claims provider that includes the following technical profiles: one ( SelfAsserted-LocalAccountSignin-Email ) that outputs the action token, and another ( login-DRSCheck in our example) for the Azure function that receives the action token as input and outputs the risk recommendation.
Copy
Copied
  <ClaimsProviders>
    <ClaimsProvider>
      <DisplayName>Sign in using DRS</DisplayName>
      <TechnicalProfiles>
        <TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
          <DisplayName>Local Account Signin</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <Metadata>
            <Item Key="SignUpTarget">SignUpWithLogonEmailExchange</Item>
            <Item Key="setting.operatingMode">Email</Item>
            <Item Key="setting.showSignupLink">true</Item>
            <Item Key="setting.showCancelButton">false</Item>
            <Item Key="ContentDefinitionReferenceId">api.selfasserted</Item>
            <Item Key="language.button_continue">Sign In</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" />
            <!-- Outputs the action token value provided by the frontend-->
            <OutputClaim ClaimTypeReferenceId="ts-drs-response" />
          </OutputClaims>
          <ValidationTechnicalProfiles>
            <ValidationTechnicalProfile ReferenceId="login-DRSCheck" />
            <ValidationTechnicalProfile ReferenceId="login-NonInteractive" />
          </ValidationTechnicalProfiles>
        </TechnicalProfile>
        <TechnicalProfile Id="login-DRSCheck">
          <DisplayName>DRS check to validate the interaction and device </DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <Metadata>
            <!-- Azure Function App -->
            <Item Key="ServiceUrl">YOUR_FUNCTION_URL</Item>
            <Item Key="AuthenticationType">None</Item>
            <Item Key="SendClaimsIn">Body</Item>
            <!-- JSON, Form, Header, and Query String formats supported -->
            <Item Key="ClaimsFormat">Body</Item>
            <!-- Defines format to expect claims returning to B2C -->
            <!-- REMOVE the following line in production environments -->
            <Item Key="AllowInsecureAuthInProduction">true</Item>
          </Metadata>
          <InputClaims>
            <!-- Receives the action token value as input -->
            <InputClaim ClaimTypeReferenceId="ts-drs-response" PartnerClaimType="actiontoken" DefaultValue="0" />
          </InputClaims>
          <OutputClaims>
            <!-- Outputs the risk recommendation value returned by Mosaic (via the Azure function) -->
            <OutputClaim ClaimTypeReferenceId="ts-drs-recommendation" PartnerClaimType="recommendation.type" />
          </OutputClaims>
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>
  </ClaimsProviders>
  1. In the UserJourneys section, create a new user journey ( SignInDRS in our example) that identifies the user and performs the appropriate identity protection steps based on the Mosaic risk recommendation. For example, the journey can proceed normally if Mosaic returns 'allow' or 'trust', terminate and inform the user of the issue if 'deny', or trigger a step-up authentication process if 'challenge'.
Copy
Copied
  <UserJourneys>
    <UserJourney Id="SignInDRS">
      <OrchestrationSteps>
        <!-- Step that identifies the user by email and stores the action token -->
        <OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.selfasserted">
          <ClaimsProviderSelections>
            <ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
          </ClaimsProviderSelections>
          <ClaimsExchanges>
            <ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
          </ClaimsExchanges>
        </OrchestrationStep>

        <!-- Step to perform DRS check -->
        <OrchestrationStep Order="2" Type="ClaimsExchange">
          <ClaimsExchanges>
            <ClaimsExchange Id="DRSCheckExchange" TechnicalProfileReferenceId="login-DRSCheck" />
          </ClaimsExchanges>
        </OrchestrationStep>

        <!-- Conditional step for ACCEPT or TRUST -->
        <OrchestrationStep Order="3" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimEquals" ExecuteActionsIf="false">
              <Value>ts-drs-recommendation</Value>
              <Value>ACCEPT</Value>
              <Value>TRUST</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <!-- Define the ClaimsExchange or other actions for ACCEPT or TRUST -->
        </OrchestrationStep>

        <!-- Conditional step for CHALLENGE -->
        <OrchestrationStep Order="4" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimEquals" ExecuteActionsIf="false">
              <Value>ts-drs-recommendation</Value>
              <Value>CHALLENGE</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <!-- Define the ClaimsExchange or other actions for CHALLENGE -->
        </OrchestrationStep>

        <!-- Conditional step for DENY -->
        <OrchestrationStep Order="5" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimEquals" ExecuteActionsIf="false">
              <Value>ts-drs-recommendation</Value>
              <Value>DENY</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <!-- Define the ClaimsExchange or other actions for DENY -->
        </OrchestrationStep>
    </UserJourney>
  </UserJourneys>
  1. Save the policy file as DRSTrustFrameworkExtensions.xml .
  2. Create a new file that inherits from the file you just saved. It will extend the SignIn policy that works as an entry point for the Signin and Signup user journeys with DRS.
Copy
Copied
  <BasePolicy>
    <TenantId>YOUR AZURE TENANT</TenantId>
    <PolicyId>B2C_1A_DRSTrustFrameworkExtensions</PolicyId>
  </BasePolicy>
  1. In the RelyingParty section, configure your DRS-enhanced user journey ( SignInDRS in our example).
Copy
Copied
  <RelyingParty>
    <DefaultUserJourney ReferenceId="SignInDRS" />
    <UserJourneyBehaviors>
      <ScriptExecution>Allow</ScriptExecution>
    </UserJourneyBehaviors>
    <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" />
      </OutputClaims>
      <SubjectNamingInfo ClaimType="sub" />
    </TechnicalProfile>
  </RelyingParty>
  1. Save the policy file as DRSSignIn.xml .

Step 5: Upload the custom policy

Using the directory with your Azure AD B2C tenant, upload the custom policy:

  1. Sign in to the Azure portal .
  2. In the portal toolbar, select Directories + subscriptions .
  3. On the Portal settings | Directories + subscriptions page, in the Directory name list, find the Azure AD B2C directory and then select Switch .
  4. Under Policies , select Identity Experience Framework .
  5. Select Upload Custom Policy , and then upload the updated custom policy files.

Step 6: Test your custom policy

Using the directory with your Azure AD B2C tenant, test your custom policy:

  1. In the Azure AD B2C tenant, and under Policies, select Identity Experience Framework.
  2. Under Custom policies , select the Sign in policy.
  3. For Application , select the web application you registered.
  4. Select Run now .
  5. Complete the user flow.