Custom Claims in Entra ID: Integrating Application Access Control with SSO

June 11, 2025

Mehdi Mohseni
Introduction
Whenever the topic of access management in software arises, a recurring question is how the backend can provide access information to the frontend. The complexity increases when you need a feature switch or access control system that is managed inside your application and you want to control user access to various features based on their permissions or purchased capabilities.
In short, while this information lives in your own database, your backend is not responsible for authentication. In most modern architectures, you rely on an external SSO provider such as Entra ID or similar services for authentication.
In the simplest software design, these two systems can be entirely decoupled:
The user authenticates via SSO and receives a token. Afterwards, the frontend calls an API, and the backend responds with what the user is and isnât allowed to access.

Now letâs examine the challenges of this architecture:
- Both the backend and the frontend need to request permissions from the authorization service. This request must be repeated on almost every API call, because otherwise, the user could easily manipulate the client-side permissions.
- Whether we like it or not, the same authorization request is repeated over and over again. This inevitably impacts the performance and speed of the application.
- If we consider the token as the single point of truth for security in our software design, this architecture actually violates that principle. Thatâs because authentication and authorization are handled by two separate systems working side by side.

A Better Approach: Embedding Permissions as Custom Claims in the Token
Now that we understand the limitations of the conventional approach, the question is: whatâs the right solution?
In my view, the most effective method is for the SSO provider to integrate with your authorization (permissions) service during the authentication pipeline, at the moment the JWT is being issued. The SSO provider should query the authorization service to retrieve the user’s permissions, then embed these as custom claims within the JWT. This way, all necessary access rights are delivered as part of the authentication token, eliminating the need for separate permission checks on every API call.
Benefits:
- The backend and frontend can trust the token as the single source of truth for both authentication and authorization.
- This reduces unnecessary API calls, improves performance, and ensures consistency.
- Users cannot easily manipulate their permissions, since they are cryptographically signed into the token itself.

Benefits of Embedding Permissions as Custom Claims in the Token
Strong Security: This approach is very secure. The token cannot be easily manipulated, and validating the token inherently means validating the userâs permissions. Since custom claims are embedded within the JWT, simply verifying the tokenâs authenticity with the provider also confirms the validity of the permissionsâno separate checks are required.
One-Time Permission Retrieval:
Although weâve essentially shifted the place in the system where permissions are retrieved, the crucial point is that this operation only needs to happen once per authentication. Thereâs no need to query for permissions repeatedly with every request; simply verifying the token suffices.
Preserving the JWT as the Single Source of Truth:
This architecture respects the principle of the JWT being the single source of truth for security. Authentication and authorization are unified within the same token, preventing fragmentation or duplication of responsibility across different system components.
Step-by-Step: Integrating a Custom API with Microsoft Entra ID
Letâs skip the preliminary discussions and get straight to the point. In this article, we will focus on integrating your own Identity and Access Management (IAM) system with Microsoft Entra ID. This integration consists of the following key steps:
- Developing a Custom API:
Youâll need to build an API that adheres to the contract defined by Entra ID, ensuring it can handle both the request and response formats required by Entra ID. - Configuring Entra ID:
Proper configuration is necessary within Entra ID to connect it to your custom API and enable the integration features. - Securing the Connection:
Finally, you must secure the communication between your API and Entra ID to ensure only authorized requests are accepted.
Letâs get started!
Step-by-Step: Integrating a Custom API with Microsoft Entra ID
You are free to use any programming language or framework to build your API. The only requirement is to comply with the contract defined by Microsoft. In this article, Iâll demonstrate the process using an Azure Function as an example, but the technology you chooseâwhether itâs .NET, Node.js, Python, or anything elseâmakes no difference as long as you meet Microsoftâs integration contract.
Letâs create a simple Azure Function with no business logic, just to ensure that the contract between our API and Entra ID is correctly implemented. If youâre not familiar with Azure Functions, you can easily scaffold a blank Azure Function project by running the following command. (Note: youâll need to install the Azure Functions Core Tools (func
) beforehand. Install the Azure Functions Core Tools)
func init DotNetTalk.CustomClaimProvider --worker-runtime dotnet
Letâs go ahead and create the required function, and briefly explain how it works. The following function actually covers both steps 1 and 3: it receives the request from Entra ID, returns the expected response, and also handles authentication and security between your API and Entra ID.
First, add the function below to your Azure Functions project. Please note that this function requires data models for the request and response formats defined by Entra ID, which should be placed in a separate project. You can download the complete code and models from the repository linked below.
https://github.com/MehdiMohseni82/entra-custom-cliam-provider-api
Now, letâs walk through this code together.
The method signature is a standard isolated-process Azure Function. Weâve also added the appropriate annotations and attributes for OpenAPI, so you can take advantage of Swagger UI for testing and documentation. The function is currently set to allow anonymous access, since in the next step weâll secure this API from within the function itself. (While this isnât always best practice, Iâve done it this way to keep the code generic so you can later adapt it for use in a regular ASP.NET API if you wish.) Of course, you can always change the access level to Function
and then secure it further via your Function App settings.
In the next few lines, we essentially check that the request actually comes from a trusted sourceâin this case, Entra ID. Weâll refer back to this section when configuring Entra ID later on. In general, youâll have an App Registration in Entra ID responsible for establishing a secure connection between Entra ID and your API. As you can see in the TenantId
and Audience
fields, you specify the information for this App Registration.
Entra ID will include a JWT in every request. All you need to do is ensure that this JWT is valid and has been issued by a trusted authority. In the final part of the function, you can implement the business logic for generating and returning claims. Youâll also be able to inspect the incoming request in detail. For example, the request lets you access the userâs Object ID, App ID, and other relevant dataâso you can base your business logic on these details.
The last part of the code is where you return the response according to the contract defined by Entra ID.
Configuring the Entra Id
Alright, now our API is up and running. I wonât go into deployment details hereâI trust youâll be able to handle deployment on your own.
At this point, weâve covered steps one and three. Now weâre moving on to the most challenging part: Configuring Entra ID so it can connect to our API. There are a few important prerequisites you need to know before getting started:
- Custom Claim Providers are only available in tenants with a Premium Plan 2 (P2) license. Other license tiers do not support this feature.
- You must be using a B2B tenant. B2C tenants do not support custom claim provider extensions.
So, before you continue, make sure your environment meets both of these requirements.
In summary, hereâs what youâll need:
- A Custom Claim Extension.
While creating this custom claim extension, you must also set up an App Registration for authentication. This App Registration will provide a secure link between Entra ID and your API.
Letâs start from here. Weâll assume you have an account with Global Admin privileges in Azure. I strongly recommend verifying your access level before you proceed.
Now, navigate to:
https://entra.microsoft.com/#home
You can access this process from various sections in the Azure Portal or the Entra admin center, but weâll proceed via the Entra ID management environment.
- From the left navigation panel, expand Applications and click on Enterprise Applications.
- The main Enterprise Applications panel will open.
- In the right-hand panel, select Custom claim extension.
- Click Create a custom extension to enter the creation workflow for a new custom extension.

Now you need to complete three main configuration sections:
Basic
In this step, you specify which event will trigger your custom authentication extension. For a custom claim provider, you should select the TokenIssuanceStart
event, then proceed to the next step: Endpoint Configuration.
Endpoint Configuration
Here, you configure how Entra ID will connect to your API.
The most important field is the Target URL, which must be an HTTPS address pointing to your API endpoint.

API Authentication
In this step, you configure the App Registration responsible for securing the connection between Entra ID and your API.
You can either select an existing App Registration or allow Entra ID to create a new one for you. I recommend letting Entra ID create a new App Registrationâthis will automatically set up the required permissions and configurations based on the endpoint you provided in the previous step.
Important:
If you choose to create a new App Registration, make sure to select âRequest permission.â This will ensure that, in the following step, youâll be prompted to grant admin consent for the extension, which is required for proper operation.

Claims
Next, youâll define your custom claims.
A simple but crucial point: these custom claims are read directly from the response payload your API returns.
Any property under the Claims
property of the TokenIssuanceStartAction
object in your APIâs response can be surfaced as a custom claim here.
For example, in the sample below, we have two custom claims: metadata
and features
. Both can be exposed as custom claims.

Review
After completing all steps, youâll reach a Review page.
Here, you can review your settings before finalizing the creation of the extension. This review step is very important, as even a small mistake can cause the system to fail.

Typical Issues
If everything goes well, after you click the Create button, Entra ID will create both a custom extension and an App Registration for youâcompleting one of the most important steps in the process.
However, if this is your first time creating a custom extension in a new tenant, thereâs a high chance youâll encounter an error. Unfortunately, identifying the cause of this error is usually almost impossible, as Entra ID provides no detailed explanation. Youâll simply see a generic error message, similar to the example below.

The reason for this error is that Entra ID requires a specific Enterprise Application to be present in your tenant in order to create a custom extension.
This Enterprise Application is a predefined application created by Microsoft and should be automatically added to your tenant when you purchase a P2 licenseâbut in practice, it often isnât.
Details of the required Enterprise Application:
- Name: Azure Active Directory Authentication Extensions
- Application ID: 99045fe1-7639-4a75-9d4a-577b6ca3810f
The Application ID is very important, because there is no user interface in the Entra ID or Azure portal to add this Enterprise Application manually. As a result, you must add it using the Microsoft Graph CLI or PowerShell.
$ New-AzureADServicePrincipal -AppId "99045fe1-7639-4a75-9d4a-577b6ca3810f"
To run the above command, youâll first need to install either the AzureAD PowerShell module or the Microsoft Graph PowerShell SDK, and sign in with the appropriate credentials. Once youâre logged in, you can execute the command to add the required Enterprise Application.
Install-Module -Name AzureAD
Import-Module AzureAD
Connect-AzureAD
Or
Install-Module Microsoft.Graph
Connect-MgGraph -Scopes "Directory.AccessAsUser.All"
If everything goes well, you should see a result similar to the following:
ObjectId AppId DisplayName
-------- ----- -----------
[ObjectId in your Tenant] 99045fe1-7639-4a75-9d4a-577b6ca3810f Azure Active Directory Authentication Extensions\
If you encountered an error in the previous step and have now completed the above process using the Microsoft Graph module, try clicking the Create button again. Hopefully, this time the process will succeed. If so, youâll be presented with a screen like this:

Whatâs happening here?
The App Registration responsible for API Authentication requires admin consent for the CustomAuthenticationExtension.Receive.Payload
permission.
This permission, which is part of the Microsoft Graph API permissions,
allows custom authentication extensions associated with the app to receive HTTP requests triggered by an authentication event. The request may include information about the user, the client and resource service principals, and other authentication details.
Now click on the Grant Permission button. You will get this popup.

Whatâs happening here?
The App Registration responsible for API Authentication requires admin consent for the CustomAuthenticationExtension.Receive.Payload
permission.
This permission, which is part of the Microsoft Graph API permissions, allows custom authentication extensions associated with the app to receive HTTP requests triggered by an authentication event. The request may include information about the user, the client and resource service principals, and other authentication details.
Using Custom Claims in Your Application
Now we have successfully created our custom claim providerâbut these claims are not being used anywhere yet.
The final step in this process is to create a new App Registration for authenticating your application. For example, suppose you have an Angular web app and you want users to log in with Entra ID and make use of these custom claims.
Letâs go through the process step by step. Iâll assume you already have a general familiarity with the Entra portal.
Navigate to App registrations and create a new App Registration for your application.

Now I have created a new App Registration. However, this App Registration doesnât have any authentication platform configured yet.
To set this up, go to the Authentication section and add a new platform.
Since weâre assuming this is an Angular web application, select Single-page application as the platform.
Configure the settings for localhost (for example, http://localhost:4200
).
Now, you should have something similar to the following.

Be sure to select the ID Token option. Custom claims will only appear in the ID Tokenânot in access tokens.
Claim It Like You Mean It: Wiring Up Custom Claims for Your App
Do you think weâre done? Not quite! Thereâs still one more step to complete in Entra ID.
Now, we need to tell Entra to include these custom claims for this specific App Registration.
When we created the App Registration, Entra automatically created an associated Enterprise Application for us.

Now, from the right-side panel, select Single Sign-On and then go to the Attributes & Claims section.
Click the Edit button to open the following page.

And then

On this page, click the Advanced Settings button, then select the Custom Claim section. From the dropdown, choose your custom claim (identified by the name and URL you assigned earlier). You should end up with something similar to the screenshot below.

Youâve now added the custom claim extension! But what does this actually mean?
Are the custom claims automatically added to your token? Nope!
Are the custom claims now available in your App Registration? Yes! đ¤đ
Letâs keep going. Now, you need to manually add each custom claim to your token. To do this, click the Add new claim button and select your custom claim from the list.

On the next page, youâll see a list of your custom claims. In the Name field, enter the name of the claim. You can leave the Namespace field blank.
From the Source attribute dropdown, select your custom claim.

In the end, you should see something like this in the Attributes & Claims section of your Enterprise Application. As you can see, you have configured your custom claim provider and added additional claims. At this point, everything is set up and you can now use this App Registration for authentication in your application.

A Few Steps to the Finish
We still have two important steps ahead. First, we need to enable the custom claims feature in the manifest of the authentication application. To do this, go to the authentication application. From the left sidebar, select the Manifest section. In the opened manifestâwhich is a JSON fileâlook for the acceptMappedClaims
entry and change its value from null
to true
.

Now, you need to allow the app registration related to authentication in your own software to use the API of the app registration that you created for Authentication in the Custom Authentication Extension. To do this, first copy the App Registration ID of the authentication application. Then go to the App Registration for the custom claim extension authentication. Here, go to the Expose an API section and first add a scope to the API. To add a scope, select Admin Only in the “Who can consent?” field, and write simple descriptions in the “Admin consent display name” and other description fields. Then click the Add Scope button. By doing this, you allow the App Registration to be used by another App Registrationâin this case, the app registration for authentication in your own software.

Next, in the Authorized client applications section, click the Add a client application button. In the client ID field, enter the client ID of the app registration related to authentication in your software, select the scope you just created, and click Add Application.

Summary: How Everything Connects
In this architecture, user authentication starts when the frontend app (such as an Angular SPA) redirects the user to Entra ID via its own App Registration. During the authentication pipeline, Entra triggers the Custom Claim Provider Extension, which itself is securely linked to a separate App Registration used exclusively for API authentication. The extension uses this registration to obtain a signed JWT, which it presents to your API (the custom claim provider endpoint). Your API validates the JWT, ensuring requests originate from the trusted extension, and then returns the required custom claims. The extension relays these claims back to Entra ID, which embeds them into the userâs ID Token. The frontend app then consumes these enriched tokens, enabling granular access control and personalized business logicâall while maintaining secure, clearly defined boundaries between each component in the system.

Further Reading & Source Code
- You can find the full API source code along with all PlantUML diagrams in this repository. https://github.com/MehdiMohseni82/entra-custom-cliam-provider-api
- For more information, refer to the official Microsoft documentation (https://learn.microsoft.com/en-us/entra/identity-platform/custom-claims-provider-overview). While some details are not explicitly covered there, it provides a solid overall explanation of the process.