Add a terms of use consent page to Azure AD B2C user journey with custom policies

Azure Active Directory_COLOR

In those modern times of compliance getting important, when providing a service to customer and consumer we need to agree them to our terms of use/service.

When using Azure AD B2C (Business to Consumer) you can easily do that with custom policies from the Identity Experience Framework.

The described solution is based on the LocalAccount templates from the Custom Policies Starter Pack GitHub repository.

Beside editing your policy with the steps below, you can download the complete files from my GitHub repository: B2C-custom-policy-with-consent

What it does:

  • Present a page in the sign-up user journey with terms of use required to consent to.
  • When accepted the current version (date or number) of the terms of use are stored in an extension attribute of the users profile.
  • If you create a new version of your terms of use and modify the version (date or number) in the custom policy users are required, on the next login, to agree to those new terms of use again.

What do I need to prepare:

How to implement:

First of all we create the required custom attribute, because I decided not to use my own extension app, I will use the default “b2c-extensions-app. Do not modify. Used by AADB2C for storing user data.” app with my custom policy.

Attributes for the build in policies are also stored here.

Create the needed attribute

Go to the Azure Portal ( switch to your B2C tenant and create the following custom attribute from the B2C management blade:

  • Name: TermsOfUseConsented
  • Type: String
  • Description:

Now let’s catch up that attribute in our custom policy. Edit TrustFrameworkBase.xml and add the following ClaimType to the SECTION III of the ClaimsSchema block.

Add the additional consent page

Let’s create the additional page to present the consent screen in the user’s journey:

In the TrustFrameworkBase.xml add the following content definition to the ContentDefinitions block:

Tell the policy where extension attributes are located
Locate the TechnicalProfile Id=”AAD-Common” and add the following after the Protocol tag:
You need to enter the GUID’s of your b2c-extension-app here, you can find them in your B2C tenant from Azure Active Directory -> App Registrations

Read the stored consent attribute from the directory

Locate the TechnicalProfile Id=”AAD-UserReadUsingEmailAddress” and add an additional output claim:

So if the user is searched and read by email address the attribute is added to the output claim.
In addition locate the TechnicalProfile Id=”AAD-UserReadUsingObjectId” and add the following output claim:
This reads the attribute every time the user is searched and read from the directory and stores it in the output claim.

Create Technical Profile to write the consent attribute to AAD
Add the following technical profile in the TechnicalProfiles block of the the ClaimsProvider block with DisplayName “Azure Active Directory” in the  TrustFrameworkBase.xml file:
This profile will be used later as an validation technical profile to store the consent attribute if the user agrees to the terms of use.
Locate the ClaimsProvider block with the DisplayName “Self Asserted” and add the following new technical profile within the TechnicalProfiles block in the TrustFrameworkBase.xml file:
This technical profile stores the attribute to the identity store of AAD and uses the validation technical profile above.

Create the custom user journey
Switch to the TrustFrameworkExtension.xml file and add the following user journey to the UserJourneys block:
This is a copy of the default user journey from the TrustFrameworkBase.xml file with the added consent page in the 2nd last step (Order=”4″)

Active the new journey as the default user journey
Switch to the SignUpOrSignIn.xml file and modify the following line to set the custom user journey as the default journey:

That’s it, you can now load all policies into your B2C tenant and give them a try. Don’t forget you need to upload them in the following order:

  • TrustFrameworkBase.xml
  • TrustFrameworkExtension.xml
  • SignUpOrSignOn.xml
  • <additional files>

If you create new version of your terms of use you need to edit both date values, first in the ClaimType of the TrustFrameworkBase and second in the Preconditions of the UserJourney in the TrustFrameWorkExtension.

Since the custom consent page did not show the consent text itself you should put this into the UI customization HTML file and reference it the custom policy.

You can do that by modify the parameter LoadUri of the ContentDefinition Id=”api.selfasserted.consent”



Author: Peter Stapf

Senior Consultant Identity and Access

20 thoughts on “Add a terms of use consent page to Azure AD B2C user journey with custom policies”

  1. This article was incredibly helpful. Thank you!

    I got this to work with the local accounts but I also tried setting this up with the SocialAndLocalAccounts, however I kept getting this error.

    Unable to upload policy. Reason : Validation failed: 1 validation error(s) found in policy “B2C_1A_SIGNUP_SIGNIN” of tenant “”.Claim type “identityProvider” is the output claim of the relying party’s technical profile, but it is not an output claim in any of the steps of user journey “SignUpOrSignIn-withConsent”.

    1. Its just like the message states, you have an outputClaim in your signupsign policy but that claim is missing in one or more of the technical profiles that are called by your user journey.

  2. Hi this article was very useful. Can you please let me know how to include hyperlink in terms and agreement and also i would like to display termsofconstent custom attribute in signup page instead of redirection to new page.

    1. Hi, I only implemented a simple solution here, which only display the Radio control to accept the terms.
      The tems of use content itself is a seperate HTML page (custom UI)

  3. Very helpful article! i have a similar requirement with slight variation, Instead of consent i would like to add a custom user a custom attribute. Up on successful login if user does not have user name in AD then redirect to self asserted page where use will have to select the username.
    Now at this time i would like to put validation on the username field. He should not be able to choose something which is already taken, Also i would like to make exception for few reserved words which can not be chosen as user name.

    1. Why not put that scenario into the registration screen instead of a custom page and mark that custom attribute as required ?
      Or is it for a solution where users are already present and should add their new “username”

      1. It is when users are already present and later they are coming to choose their username for additional functionality on website.

    1. Hello,
      it should not be that different from the Local Account scenario, because the SocialandLocal account sample ist based on the LocalAccountOnly sample from Microsoft. Beside implementing the technical profiles and claims you need to put the parts into the correct steps in your user journey

    1. Hi, could be a bit tricky as over the time when you reach V50 the claim can become very long. It could be better to use the Rest API Claims Provider and create some kind of TOS history stored in a database. Of course technically it is possible to store all versions in an string attribute by adding the new consentet version to the old one but I would use a second attribute for that.

  4. Hi Peter,
    This article helped a-lot, I explored too many documentations about the Custom Policies but wasn’t able to create consent page and modified User Journey, This blog made it quite easy for me to integrate pages accords to my need. I Just need one more help here,
    Actually for users who has accepted Terms consent the profile section still shows that value as undefined under consent and Major section on users profile page. Could You please help me with that how can we store that accepted consent value.

    Thank you.

    1. Hello,
      the consent is stored on the users attribute/claim extension_TermsOfUseConsented so you should be able to read that by modify the AAD-UserReadUsingObjectId. You can verify the correct stores by for example read useres extension attributes with PowerShell

  5. Hi Peter

    Thanks for this article

    I’m running through your instructions and I’m getting an error trying to upload the SignUpSignIn policy. It says

    “An error occurred while creating extension property “TermsOfUseConsented” in tenant “”. Error returned was 409/Request_MultipleObjectsWithSameKeyValue: An extension property exists with the name extension__TermsOfUseConsented.”

    When I delete that extension property the policy uploads successfully. However when trying to use the policy I see the following error message in Application Insights:

    “The following extension properties are not available: extension__TermsOfUseConsented”

    When I add the extension property in I then get a message similar to the first one, saying that the property already exists.

    Can you advise please?



  6. Hi Peter,
    this is a really nice docu it worked for me with the first trial, thanks a lot.

    However, what is not working is the “skip” step in the UserJourney.

    I always get the claim “extension_TermsOfUseConsented”: “2022-05-18” within my token and it matches the Value I compare in the UserJourney. But this step never gets skipped.

    So I expect the claim is not properly persisted during AAD-WriteUserConsentByObjectId-ThrowIfNotExists
    I am missing to read the claim when reading the Object from the AAD

    Any Idea?


  7. Thank you for this guide!

    I am running into a couple of problems (note I had to do some tweaks to get it to work with social accounts):

    1) the consent doesn’t seem to be persisted as I have to consent on every login
    2) the consent isn’t returned to the calling application

    Any hints what I might have done wrong?

      1. I got the returning of the consent working but still unable to persist the consent in AD. I looked at that example but found it harder to wrap my head around. I also preferred having the consent as a separate page. I think it is useful that they store the date/time of the consent. I guess I will give it a try to see if I can get that working and if so if I can then move it to its own page.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: