Integrate Fraud Prevention 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.
This guide describes the backend integration approach, where the B2C custom policy calls APIs directly via REST technical profiles—no Azure Function intermediary required.
For the client-side integration approach using triggerActionEvent() and an Azure Function, see Integrate Fraud Prevention with Azure AD B2C.
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 API endpoints: Triggers action events and provides the risk recommendations based on collected data. Azure AD B2C communicates with Mosaic endpoints directly using a REST technical profile in the custom policy.
The following describes the implementation:

- The user opens the Azure AD B2C sign-in page.
- The page initializes the Platform SDK with
enableSessionToken: true, which starts streaming device telemetry to Mosaic and requests a session token. - Mosaic returns a session token and it gets stored in a hidden form field for B2C to pick up as a claim.
- The B2C custom policy calls the Trigger action event API with
get_recommendation=true, passing the session token, action type, and claimed user ID. - Mosaic returns the risk recommendation inline (challenge/allow/deny) along with an action token.
- Azure AD B2C validates user credentials and performs additional steps if needed (e.g., MFA) based on the recommendation, and completes authentication procedure.
- After authentication completes, the B2C custom policy reports the action result back to Mosaic.
To get started, you need:
- A Microsoft Entra subscription. If you don't have one, get a free account
- An Azure AD B2C tenant linked to the Entra subscription
- A registered web application in your Azure AD B2C tenant
- Azure AD B2C custom policies
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).
- From Applications, click Add application.
- Add a new application: provide a friendly application name to display in the Admin Portal.
- Add an OIDC client: provide client display name and add your website URL as a redirect URI (e.g.,
https://your-domain.com).
These fields are required for all Mosaic apps, but won't be used for Fraud Prevention.
- Saving the client will automatically generate client credentials. Ensure to store client ID and secret.
Start by integrating Fraud Prevention 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, Fraud Prevention returns a session token and starts collecting telemetry data for the user interacting with your app. The session token is stored in a hidden field (session_token) for Azure AD B2C to pick up as a claim.
- Prepare a custom HTML file for your sign-in page based on the sample templates and a separate file with JavaScript code.
Azure AD B2C does not allow executing inline scripts. You must place the JavaScript code in a separate file and reference it from your HTML. Use a MutationObserver to detect when B2C injects elements into the DOM, as recommended by Microsoft.
Add the following to your custom HTML file to load the SDK and your custom script, and include a hidden field to hold the session token:
<!-- Loads Fraud Prevention SDK -->
<script src="https://platform-websdk.transmitsecurity.io/platform-websdk/2.x/ts-platform-websdk.js" defer></script>
<!-- Loads custom Mosaic initialization script (host this file alongside your HTML) -->
<script src="https://[YOUR_STORAGE_ACCOUNT].blob.core.windows.net/[CONTAINER]/mosaic-drs.js" defer></script>
<!-- Hidden field to hold the session token for B2C -->
<input type="hidden" id="session_token" name="session_token" />Create a separate JavaScript file (mosaic-drs.js) with the following code.
// Initializes SDK and retrieves a session token
async function initAndGetSessionToken() {
await window.tsPlatform.initialize({
clientId: "[clientId]", // Client ID found in the app settings in Mosaic Admin Portal
drs: {
serverPath: "https://api.transmitsecurity.io/risk-collect/", // Required: Set serverPath based on your region or custom domain
enableSessionToken: true, // Required for backend integration
},
});
// Obtains session token
const sessionToken = await window.tsPlatform.drs.getSessionToken();
document.getElementById("session_token").value = sessionToken;
}
// Uses MutationObserver to detect when B2C injects elements into the DOM
const observer = new MutationObserver(function (mutations, obs) {
const tokenElement = document.getElementById("session_token");
if (tokenElement && window.tsPlatform) {
obs.disconnect(); // Stop observing once element is found
initAndGetSessionToken();
}
});
// Start observing the document body for changes
observer.observe(document.body, {
childList: true,
subtree: true
});enableSessionToken: true must be set during initialization for getSessionToken() to work. The session token represents the device session established when the SDK initialized—it is passed to the backend Trigger action event API instead of an action token.
- Host the HTML page and JavaScript file on a Cross-Origin Resource Sharing (CORS) enabled web endpoint by creating a storage account and adding CORS support for Azure Storage.
You can incorporate Fraud Prevention into your Azure B2C application by extending your custom policies.
Download the custom policy starter pack to get started (see Create custom policies in Azure AD B2C)
Create a new file that inherits from TrustFrameworkExtensions, which will extend the base policy with tenant-specific customizations for Fraud Prevention.
<BasePolicy>
<TenantId>YOUR AZURE TENANT</TenantId>
<PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
</BasePolicy>- In the
BuildingBlockssection, define the claims needed for the backend integration:
<BuildingBlocks>
<ClaimsSchema>
<!-- Session token from the frontend SDK -->
<ClaimType Id="session_token">
<DisplayName>Fraud Prevention Session Token</DisplayName>
<DataType>string</DataType>
<UserInputType>TextBox</UserInputType>
</ClaimType>
<!-- Action type sent to the Trigger Action Event API (e.g. "login") -->
<ClaimType Id="action_type">
<DisplayName>Action Type</DisplayName>
<DataType>string</DataType>
<UserInputType>Readonly</UserInputType>
</ClaimType>
<!-- Correlation ID for tracing the action across systems -->
<ClaimType Id="correlation_id">
<DisplayName>Correlation ID</DisplayName>
<DataType>string</DataType>
<UserInputType>Readonly</UserInputType>
</ClaimType>
<!-- Claimed (pre-auth) user identifier -->
<ClaimType Id="claimed_user_id">
<DisplayName>Claimed User ID</DisplayName>
<DataType>string</DataType>
<UserInputType>Readonly</UserInputType>
</ClaimType>
<!-- Bearer token obtained from Mosaic via client credentials -->
<ClaimType Id="bearerToken">
<DisplayName>Transmit Bearer Token</DisplayName>
<DataType>string</DataType>
<UserInputType>Readonly</UserInputType>
</ClaimType>
<!-- Risk recommendation returned inline by the Trigger Action Event API -->
<ClaimType Id="recommendation">
<DisplayName>Risk Recommendation</DisplayName>
<DataType>string</DataType>
<UserInputType>Readonly</UserInputType>
</ClaimType>
<!-- Action token returned by the Trigger Action Event API (used for reporting result) -->
<ClaimType Id="action_token">
<DisplayName>Action Token</DisplayName>
<DataType>string</DataType>
<UserInputType>Readonly</UserInputType>
</ClaimType>
</ClaimsSchema>
</BuildingBlocks>- In the
BuildingBlockssection, add a reference to your custom UI:
<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>- In the
ClaimsProviderssection, configure a claims provider that includes the following technical profiles: one (SelfAsserted-LocalAccountSignin-Email) that outputs the session token and claimed user ID, and three REST technical profiles that handle obtaining an access token, triggering the action with an inline recommendation, and reporting the action result—all directly from the B2C custom policy.
<ClaimsProviders>
<ClaimsProvider>
<DisplayName>Sign in using Fraud Prevention</DisplayName>
<TechnicalProfiles>Get bearer token (client credentials) — calls the Mosaic token endpoint to obtain an access token using your app's Client ID and Client Secret (stored as B2C policy keys).
<TechnicalProfile Id="REST-GetTransmitBearerToken">
<DisplayName>Get Transmit Access Token</DisplayName>
<Protocol Name="Proprietary"
Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<!-- Set ServiceUrl depending on your region -->
<Item Key="ServiceUrl">https://api.transmitsecurity.io/oidc/token</Item>
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">Form</Item>
<Item Key="ClaimsFormat">Form</Item>
</Metadata>
<InputClaims>
<!-- Store these values as B2C policy keys: B2C_1A_TransmitClientId, B2C_1A_TransmitClientSecret -->
<InputClaim ClaimTypeReferenceId="grant_type" DefaultValue="client_credentials" AlwaysUseDefaultValue="true" />
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="{Policy:B2C_1A_TransmitClientId}" AlwaysUseDefaultValue="true" />
<InputClaim ClaimTypeReferenceId="client_secret" DefaultValue="{Policy:B2C_1A_TransmitClientSecret}" AlwaysUseDefaultValue="true" />
</InputClaims>
<OutputClaims>
<!-- Maps the access_token response field to our bearerToken claim -->
<OutputClaim ClaimTypeReferenceId="bearerToken" PartnerClaimType="access_token" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>Trigger action and get recommendation — sends the session token, action type, correlation ID, and claimed user ID to the Trigger Action API and retrieves the inline recommendation in a single call.
<TechnicalProfile Id="REST-ClientTriggerAction">
<DisplayName>Fraud Prevention checks 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>
<!-- get_recommendation=true returns the recommendation inline, avoiding a separate API call -->
<!-- Set ServiceUrl depending on your region-->
<Item Key="ServiceUrl">https://api.transmitsecurity.io/risk/v1/action/trigger-action?get_recommendation=true</Item>
<Item Key="AuthenticationType">Bearer</Item>
<Item Key="UseClaimAsBearerToken">bearerToken</Item>
<Item Key="SendClaimsIn">Body</Item>
<Item Key="ClaimsFormat">JsonCollections</Item>
<Item Key="ResolveJsonPathsInJsonTokens">true</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="bearerToken" />
<!-- Session token from the client-side SDK -->
<InputClaim ClaimTypeReferenceId="session_token" PartnerClaimType="session_token" />
<InputClaim ClaimTypeReferenceId="action_type" PartnerClaimType="action_type"
DefaultValue="login" AlwaysUseDefaultValue="true" />
<!-- Ties Fraud Prevention action to B2C correlation ID for tracing -->
<InputClaim ClaimTypeReferenceId="correlation_id" PartnerClaimType="correlation_id"
DefaultValue="{Context:CorrelationId}" AlwaysUseDefaultValue="true" />
<!-- The email the user entered, passed pre-auth for risk context -->
<InputClaim ClaimTypeReferenceId="claimed_user_id" PartnerClaimType="claimed_user_id" />
</InputClaims>
<OutputClaims>
<!-- Outputs the risk recommendation value returned by Mosaic -->
<OutputClaim ClaimTypeReferenceId="recommendation" PartnerClaimType="recommendation.recommendation.type" />
<!-- Action token retained for reporting result later -->
<OutputClaim ClaimTypeReferenceId="action_token" PartnerClaimType="action_token" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>Report action result — is called after authentication completes to report the outcome back to Mosaic, enabling accurate user profiling and model improvement.
<TechnicalProfile Id="REST-ReportActionResult">
<DisplayName>Report Transmit DRS Action Result</DisplayName>
<Protocol Name="Proprietary"
Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<!-- Set ServiceUrl depending on your region-->
<Item Key="ServiceUrl">https://api.transmitsecurity.io/risk/v1/action/result</Item>
<Item Key="AuthenticationType">Bearer</Item>
<Item Key="UseClaimAsBearerToken">bearerToken</Item>
<Item Key="SendClaimsIn">Body</Item>
<Item Key="ClaimsFormat">JsonCollections</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="bearerToken" />
<!-- action_token from the Trigger Action Event API response -->
<InputClaim ClaimTypeReferenceId="action_token" PartnerClaimType="action_token" />
<!-- Result: success | failure | incomplete -->
<InputClaim ClaimTypeReferenceId="result" PartnerClaimType="result"
DefaultValue="success" AlwaysUseDefaultValue="true" />
<!-- Authenticated user ID — set after login is confirmed -->
<InputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="user_id" />
</InputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>Sign-in profile — updates SelfAsserted-LocalAccountSignin-Email to output the session token and claimed user ID (the email entered by the user) from the sign-in form, and chain the REST profiles as validation steps.
<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 session token value provided by the frontend-->
<OutputClaim ClaimTypeReferenceId="session_token" />
<!-- Use signInName (email) as the claimed user ID for pre-auth risk context -->
<OutputClaim ClaimTypeReferenceId="claimed_user_id" DefaultValue="{OIDC:LoginHint}" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="REST-GetTransmitBearerToken" />
<ValidationTechnicalProfile ReferenceId="REST-ClientTriggerAction" />
<ValidationTechnicalProfile ReferenceId="login-NonInteractive" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>- In the
UserJourneyssection, create a new user journey (SignInDRSin 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'.
<UserJourneys>
<UserJourney Id="SignInDRS">
<OrchestrationSteps>
<!-- Step that identifies the user by email and stores the session token -->
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.selfasserted">
<ClaimsProviderSelections>
<ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Conditional step for ALLOW or TRUST -->
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>recommendation</Value>
<Value>ALLOW</Value>
<Value>TRUST</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<!-- Define the ClaimsExchange or other actions for ALLOW or TRUST -->
</OrchestrationStep>
<!-- Conditional step for CHALLENGE -->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>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="4" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>recommendation</Value>
<Value>DENY</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<!-- Define the ClaimsExchange or other actions for DENY -->
</OrchestrationStep>
<!-- Report action result back to Mosaic -->
<OrchestrationStep Order="5" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="ReportResultExchange" TechnicalProfileReferenceId="REST-ReportActionResult" />
</ClaimsExchanges>
</OrchestrationStep>
</UserJourney>
</UserJourneys>Save the policy file as
DRSTrustFrameworkExtensions.xml.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 Fraud Prevention.
<BasePolicy>
<TenantId>YOUR AZURE TENANT</TenantId>
<PolicyId>B2C_1A_DRSTrustFrameworkExtensions</PolicyId>
</BasePolicy>- In the
RelyingPartysection, configure your Fraud Prevention-enhanced user journey (SignInDRSin our example).
<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>
- Save the policy file as
DRSSignIn.xml.
Using the directory with your Azure AD B2C tenant, upload the custom policy:
- Sign in to the Azure portal.
- In the portal toolbar, select Directories + subscriptions.
- On the Portal settings | Directories + subscriptions page, in the Directory name list, find the Azure AD B2C directory and then select Switch.
- Under Policies, select Identity Experience Framework.
- Select Upload Custom Policy, and then upload the updated custom policy files.
Using the directory with your Azure AD B2C tenant, test your custom policy:
- In the Azure AD B2C tenant, and under Policies, select Identity Experience Framework.
- Under Custom policies, select the Sign in policy.
- For Application, select the web application you registered.
- Select Run now.
- Complete the user flow.