Enriching Azure AD B2C Claims with App Roles and Groups Using Azure Function and Custom Policies

Introduction:

Azure AD B2C is a powerful customer identity solution, but it comes with some limitations compared to Microsoft Entra ID (formerly Azure Active Directory). One major limitation is the lack of native support for app roles and group claims. While Microsoft Entra ID supports these features out of the box, Azure AD B2C does not expose group membership or app role assignments in the ID token.

However, since Azure AD B2C is built on top of the Microsoft Entra ID framework, we can bridge this gap by leveraging the Microsoft Graph API. In this blog, I’ll Walk through how I built a custom solution using:

  • A backend Azure Function that queries the Microsoft Graph API,
  • A set of custom policies that call the function during sign-in, and
  • The inclusion of app role and group claims in the B2C-issued token.

The Problem: No Native Support for Groups or Roles in AD B2C

Azure AD B2C doesn’t natively issue claims for:

  • Group memberships
  • App role assignments

This is because B2C is optimized for consumer identity scenarios and does not expose the same directory APIs as standard Microsoft Entra tenants. As a result, applications that depend on RBAC (role-based access control) or group-based authorization need a workaround.

The Solution: Use Microsoft Graph to Build a Custom Solution to Manage App Roles and Group Membership

To address this limitation, I built a complete end-to-end solution that includes:

1. Web Application for Admins

The B2C App Roles Management Web App enables administrators to:

  • Assign app roles to B2C users.
  • Manage group memberships for users in the B2C directory.
  • View assigned roles and group information for each user.

This portal is built using .NET and integrates with the Microsoft Graph API using client credentials to perform directory updates. It provides a clean UI and API endpoints to manage these assignments programmatically.

Azure Function to retrieve App Roles and Group Details of the Authenticated User

I created a custom Azure Function that:

  • Authenticates using client credentials against Microsoft Graph.
  • Fetches assigned app roles for the user.
  • Retrieves the user’s group memberships.
  • Returns both values as JSON.
  • Is called inside a custom policy using B2C’s REST API integration.

Click here to get the Azure Function source How the Function Works

How the Function Works?

The Azure Function uses Microsoft Graph SDK to query both:

  • App role assignments via /appRoleAssignedTo
  • Group memberships via /memberOf

Here’s a simplified view of the logic:

var appRoles = await graphClient
    .ServicePrincipals[servicePrincipalId]
    .AppRoleAssignedTo
    .Request()
    .Filter($"principalId eq {userObjectId}")
    .GetAsync();

var groups = await graphClient
    .Users[userObjectId]
    .MemberOf
    .Request()
    .GetAsync();

It returns a response like:

{
  "roles": ["Admin", "Editor"],
  "groups": ["Marketing", "Developers"]
}

Integrating with Azure AD B2C Custom Policies

Define the REST API in TrustFrameworkExtensions.xml
      <TechnicalProfiles>
        <TechnicalProfile Id="GetUserAppRoleAssignment">
          <DisplayName>Retrieves security groups assigned to the user</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <Metadata>
            <!-- TODO: change this -->
            <Item Key="ServiceUrl">https://<your function name>.azurewebsites.net/api/ManageB2CAppRoles</Item>
            <Item Key="AuthenticationType">None</Item>
            <Item Key="SendClaimsIn">Body</Item>
            <Item Key="AllowInsecureAuthInProduction">true</Item>
            <Item Key="IncludeClaimResolvingInClaimsHandling">true</Item>
          </Metadata>
          <InputClaims>
            <InputClaim Required="true" ClaimTypeReferenceId="objectId" />
            <!-- this B2C tenant id -->
            <InputClaim ClaimTypeReferenceId="tenantId" DefaultValue="{Policy:TenantObjectId}" />
            <!-- The App we're signing in to -->
            <InputClaim ClaimTypeReferenceId="client_id" PartnerClaimType="clientId" DefaultValue="{OIDC:ClientId}" />
            <!-- specify that we want both roles and groups back -->
            <InputClaim ClaimTypeReferenceId="scope" DefaultValue="roles groups" AlwaysUseDefaultValue="true" />
          </InputClaims>
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="roles" />
            <OutputClaim ClaimTypeReferenceId="groups" />
            <OutputClaim ClaimTypeReferenceId="tenantId" DefaultValue="{Policy:TenantObjectId}" AlwaysUseDefaultValue="true" />
          </OutputClaims>
          <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
        </TechnicalProfile>

Call the REST API During Orchestration

In your B2C_1A_SIGNUPORSIGNIN_APPROLE.xml file:

 <OrchestrationStep Order="7" Type="ClaimsExchange">
          <ClaimsExchanges>
            <ClaimsExchange Id="GetUserAppRoleAssignment" TechnicalProfileReferenceId="GetUserAppRoleAssignment" />
          </ClaimsExchanges>
        </OrchestrationStep>

Return the Claims in the Token

Add the output claims to the final JWT token issuer:

<OutputClaims>
        <OutputClaim ClaimTypeReferenceId="groups" />
        <OutputClaim ClaimTypeReferenceId="roles" />
      </OutputClaims>

When you test the user flow from the portal, you will get a role, and group claims as shown in the figure below.

App roles and groups as a claim in Azure AD B2C user flows
Secure the Azure Function

You should:

  • Use Azure App Settings or Key Vault to store client secrets.
  • Add API key or JWT validation to restrict who can call the function.
  • Use Managed Identity for better security (optional enhancement).

This solution brings the enterprise-grade directory capabilities of Microsoft Entra ID into Azure AD B2C by tapping into Graph API during authentication.

This approach allows you to implement RBAC (role-based access control) and group-based personalization in your B2C-secured apps, something not possible with default B2C behavior.

If you’re building enterprise apps with Azure AD B2C and need advanced claims, I highly recommend adopting this pattern.

Summary:

Azure AD B2C does not natively support app roles and group claims like Microsoft Entra ID, but since it’s built on the same underlying framework, these features can be extended using the Microsoft Graph API and custom policies. To overcome this limitation, I developed a custom solution consisting of a web application to manage app roles and group memberships, an Azure Function to retrieve this data at runtime, and custom B2C policies to enrich the token with role and group claims. This approach enables fine-grained access control and enterprise-level authorization for applications using Azure AD B2C, bringing it closer to Microsoft Entra ID’s capabilities. The full implementation is available on GitHub for customization and reuse.

📁 Useful Resources

App Roles & Group Management UI

Azure Function to Get Role Claims

B2C Custom Policy Files

Gowtham K

Gowtham K has been awarded as MVP(Most Valuable Professional) for 9 times by Microsoft for his exceptional contribution in Microsoft technologies under the category “Developer Technologies & Security” . He has more than 12 years of experience on Microsoft technologies such as C#, ASP.NET MVC, ASP.NET WEB API, ASP.NET Core, MS SQL Server, Azure, Microsoft Entra ID, Azure AD B2C and other technologies such as JavaScript, jQuery, HTML and CSS .He is also a blogger and author of articles on various technologies. He is also a speaker and delivered talk on various technologies like ASP.NET MVC, Azure and Azure DevOps in the public events.

Leave a Reply

Your email address will not be published. Required fields are marked *