Write your first Azure Active Directory B2C custom policy - Hello World!
In your application, you can use user flows that enable users to sign up, sign in, or manage their profile. When user flows don't cover all your business specific needs, you can use custom policies.
While you can use pre-made custom policy starter pack to write custom policies, it's important for you understand how a custom policy is built. In this article, you learn how to create your first custom policy from scratch.
Prerequisites
If you don't have one already, create an Azure AD B2C tenant that is linked to your Azure subscription.
You must have Visual Studio Code (VS Code) installed in your computer.
Note
This article is part of the Create and run your own custom policies in Azure Active Directory B2C how-to guide series. We recommend that you start this series from the first article.
Step 1 - Configure the signing and encryption keys
If you haven't already done so, create the following encryption keys. To automate the walk-through below, visit the IEF Setup App and follow the instructions:
Use the steps in Add signing and encryption keys for Identity Experience Framework applications to create the signing key.
Use the steps in Add signing and encryption keys for Identity Experience Framework applications to create the encryption key.
Step 2 - Build the custom policy file
In VS Code, create and open the file
ContosoCustomPolicy.XML
.In the
ContosoCustomPolicy.XML
file, add the following code:<?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="yourtenant.onmicrosoft.com" PolicyId="B2C_1A_ContosoCustomPolicy" PublicPolicyUri="http://yourtenant.onmicrosoft.com/B2C_1A_ContosoCustomPolicy"> <BuildingBlocks> <!-- Building Blocks Here--> </BuildingBlocks> <ClaimsProviders> <!-- Claims Providers Here--> </ClaimsProviders> <UserJourneys> <!-- User Journeys Here--> </UserJourneys> <RelyingParty> <!-- Relying Party Here that's your policy’s entry point Specify the User Journey to execute Specify the claims to include in the token that is returned when the policy runs --> </RelyingParty> </TrustFrameworkPolicy>
Replace
yourtenant
with the subdomain part of your tenant name, such ascontoso
. Learn how to Get your tenant name.The XML elements define the top-level
TrustFrameworkPolicy
element of a policy file with its policy ID and tenant name. The TrustFrameworkPolicy element contains other XML elements that you'll use in this series.To declare a claim, add the following code in
BuildingBlocks
section of theContosoCustomPolicy.XML
file:<ClaimsSchema> <ClaimType Id="objectId"> <DisplayName>unique object Id for subject of the claims being returned</DisplayName> <DataType>string</DataType> </ClaimType> <ClaimType Id="message"> <DisplayName>Will hold Hello World message</DisplayName> <DataType>string</DataType> </ClaimType> </ClaimsSchema>
A claim is like a variable. The claim's declaration also shows the claim's data type.
In the
ClaimsProviders
section of theContosoCustomPolicy.XML
file, add the following code:<ClaimsProvider> <DisplayName>Token Issuer</DisplayName> <TechnicalProfiles> <TechnicalProfile Id="JwtIssuer"> <DisplayName>JWT Issuer</DisplayName> <Protocol Name="None" /> <OutputTokenFormat>JWT</OutputTokenFormat> <Metadata> <Item Key="client_id">{service:te}</Item> <Item Key="issuer_refresh_token_user_identity_claim_type">objectId</Item> <Item Key="SendTokenResponseBodyWithJsonNumbers">true</Item> </Metadata> <CryptographicKeys> <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" /> <Key Id="issuer_refresh_token_key" StorageReferenceId="B2C_1A_TokenEncryptionKeyContainer" /> </CryptographicKeys> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider> <ClaimsProvider> <!-- The technical profile(s) defined in this section is required by the framework to be included in all custom policies. --> <DisplayName>Trustframework Policy Engine TechnicalProfiles</DisplayName> <TechnicalProfiles> <TechnicalProfile Id="TpEngine_c3bd4fe2-1775-4013-b91d-35f16d377d13"> <DisplayName>Trustframework Policy Engine Default Technical Profile</DisplayName> <Protocol Name="None" /> <Metadata> <Item Key="url">{service:te}</Item> </Metadata> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider>
We've declared a JWT Token Issuer. In the
CryptographicKeys
section, if you used different names to configure the signing and encryption keys in step 1, make sure you use the correct value for theStorageReferenceId
.In the
UserJourneys
section of theContosoCustomPolicy.XML
file, add the following code:<UserJourney Id="HelloWorldJourney"> <OrchestrationSteps> <OrchestrationStep Order="1" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" /> </OrchestrationSteps> </UserJourney>
We've added a UserJourney. The user journey specifies the business logic the end user goes through as Azure AD B2C processes a request. This user journey has only one step that issues a JTW token with the claims that you'll define in the next step.
In the
RelyingParty
section of theContosoCustomPolicy.XML
file, add the following code:<DefaultUserJourney ReferenceId="HelloWorldJourney"/> <TechnicalProfile Id="HelloWorldPolicyProfile"> <DisplayName>Hello World Policy Profile</DisplayName> <Protocol Name="OpenIdConnect" /> <OutputClaims> <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" DefaultValue="abcd-1234-efgh-5678-ijkl-etc."/> <OutputClaim ClaimTypeReferenceId="message" DefaultValue="Hello World!"/> </OutputClaims> <SubjectNamingInfo ClaimType="sub" /> </TechnicalProfile>
The RelyingParty section is the entry point to your policy. It specifies the UserJourney to execute and the claims to include in the token that is returned when the policy runs.
After you complete step 2, the ContosoCustomPolicy.XML
file should look similar to the following code:
<?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="Contosob2c2233.onmicrosoft.com" PolicyId="B2C_1A_ContosoCustomPolicy" PublicPolicyUri="http://Contosob2c2233.onmicrosoft.com/B2C_1A_ContosoCustomPolicy">
<BuildingBlocks>
<ClaimsSchema>
<ClaimType Id="objectId">
<DisplayName>unique object Id for subject of the claims being returned</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="message">
<DisplayName>Will hold Hello World message</DisplayName>
<DataType>string</DataType>
</ClaimType>
</ClaimsSchema>
</BuildingBlocks>
<ClaimsProviders><!--Claims Providers Here-->
<ClaimsProvider>
<DisplayName>Token Issuer</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="JwtIssuer">
<DisplayName>JWT Issuer</DisplayName>
<Protocol Name="None"/>
<OutputTokenFormat>JWT</OutputTokenFormat>
<Metadata>
<Item Key="client_id">{service:te}</Item>
<Item Key="issuer_refresh_token_user_identity_claim_type">objectId</Item>
<Item Key="SendTokenResponseBodyWithJsonNumbers">true</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer"/>
<Key Id="issuer_refresh_token_key" StorageReferenceId="B2C_1A_TokenEncryptionKeyContainer"/>
</CryptographicKeys>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Trustframework Policy Engine TechnicalProfiles</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="TpEngine_c3bd4fe2-1775-4013-b91d-35f16d377d13">
<DisplayName>Trustframework Policy Engine Default Technical Profile</DisplayName>
<Protocol Name="None" />
<Metadata>
<Item Key="url">{service:te}</Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="HelloWorldJourney">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
</UserJourney>
</UserJourneys>
<RelyingParty><!--
Relying Party Here that's your policy’s entry point
Specify the User Journey to execute
Specify the claims to include in the token that is returned when the policy runs
-->
<DefaultUserJourney ReferenceId="HelloWorldJourney"/>
<TechnicalProfile Id="HelloWorldPolicyProfile">
<DisplayName>Hello World Policy Profile</DisplayName>
<Protocol Name="OpenIdConnect"/>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" DefaultValue="abcd-1234-efgh-5678-ijkl-etc."/>
<OutputClaim ClaimTypeReferenceId="message" DefaultValue="Hello World!"/>
</OutputClaims>
<SubjectNamingInfo ClaimType="sub"/>
</TechnicalProfile>
</RelyingParty>
</TrustFrameworkPolicy>
Step 3 - Upload custom policy file
- Sign in to the Azure portal.
- If you have access to multiple tenants, select the Settings icon in the top menu to switch to your Azure AD B2C tenant from the Directories + subscriptions menu.
- In the Azure portal, search for and select Azure AD B2C.
- In the left menu, under Policies, select Identity Experience Framework.
- Select Upload custom policy, browse select and then upload the
ContosoCustomPolicy.XML
file.
After you upload the file, Azure AD B2C adds the prefix B2C_1A_
, so the names looks similar to B2C_1A_CONTOSOCUSTOMPOLICY.
Step 4 - Test the custom policy
- Under Custom policies, select B2C_1A_CONTOSOCUSTOMPOLICY.
- For Select application on the overview page of the custom policy, select the web application such as webapp1 that you previously registered. Make sure that the Select reply URL value is set to
https://jwt.ms
. - Select Run now button.
After the policy finishes execution, you're redirected to https://jwt.ms
, and you see a decoded JWT token. It looks similar to the following JWT token snippet:
{
"typ": "JWT",
"alg": "RS256",
"kid": "pxLOMWFg...."
}.{
...
"sub": "abcd-1234-efgh-5678-ijkl-etc.",
...
"acr": "b2c_1a_contosocustompolicy",
...
"message": "Hello World!"
}.[Signature]
Notice the message
and sub
claims, which we set as output claims in the RelyingParty
section.
Next steps
In this article, you learned and used four sections that are included in an Azure AD B2C custom policy. These sections are added as child elements the TrustFrameworkPolicy
root element:
- BuildingBlocks
- ClaimsProviders
- UserJourneys
- RelyingParty
Next, learn:
About custom policy claims overview.
How to declare a custom policy claim.
About custom policy claims data type.
About custom policy user input types.