Cirreum.Identity.EntraExternalId
2.0.3
dotnet add package Cirreum.Identity.EntraExternalId --version 2.0.3
NuGet\Install-Package Cirreum.Identity.EntraExternalId -Version 2.0.3
<PackageReference Include="Cirreum.Identity.EntraExternalId" Version="2.0.3" />
<PackageVersion Include="Cirreum.Identity.EntraExternalId" Version="2.0.3" />
<PackageReference Include="Cirreum.Identity.EntraExternalId" />
paket add Cirreum.Identity.EntraExternalId --version 2.0.3
#r "nuget: Cirreum.Identity.EntraExternalId, 2.0.3"
#:package Cirreum.Identity.EntraExternalId@2.0.3
#addin nuget:?package=Cirreum.Identity.EntraExternalId&version=2.0.3
#tool nuget:?package=Cirreum.Identity.EntraExternalId&version=2.0.3
Cirreum Identity EntraExternalId
Microsoft Entra External ID integration for Cirreum — the Infrastructure-layer library that implements the custom authentication extension (onTokenIssuanceStart) callback: validates Entra-signed tokens, provisions users, and returns custom claims for the issued token.
Overview
Cirreum.Identity.EntraExternalId is the Infrastructure-layer implementation of the Cirreum Identity provider pattern for Microsoft Entra External ID (CIAM). For each configured instance it registers an HTTP endpoint that Entra calls during sign-in to:
- Authenticate itself with a Microsoft-signed bearer token (validated against Entra's OIDC discovery metadata).
- Supply the authenticating user's context (external ID, email, correlation ID, calling service principal).
- Receive custom claims — the provisioner's role list — to be embedded in the issued token.
Installation
Apps do not reference this package directly. Install Cirreum.Runtime.Identity.EntraExternalId (or the umbrella Cirreum.Runtime.Identity), which brings this package in transitively and exposes the app-facing builder.AddIdentity() / app.MapIdentity() extensions.
Wire contract (Microsoft-defined)
The request and response shapes are fixed by Microsoft's custom claims provider contract. This package handles both sides — apps only supply an IUserProvisioner that returns ProvisionResult.Allow(roles) or Deny().
Request
Entra posts a JWT-authenticated payload describing the authentication context (calling app, user, correlation ID, etc.). The package validates and unpacks it internally.
Response on Allow
{
"data": {
"@odata.type": "microsoft.graph.onTokenIssuanceStartResponseData",
"actions": [
{
"@odata.type": "microsoft.graph.tokenIssuanceStart.provideClaimsForToken",
"claims": {
"correlationId": "<echoed>",
"customRoles": ["role1", "role2"]
}
}
]
}
}
Response on Deny or error
| Status | Trigger |
|---|---|
| 401 Unauthorized | Missing / invalid bearer token |
| 400 Bad Request | Malformed JSON, missing correlationId, or missing user ID |
| 403 Forbidden | clientServicePrincipal.appId not in allowlist, OR provisioner returned Deny |
| 500 Internal Server Error | Provisioner threw, or returned Allow with zero roles |
Configuration
{
"Cirreum": {
"Identity": {
"Providers": {
"EntraExternalId": {
"Instances": {
"primary": {
"Enabled": true,
"Route": "/auth/entra/provision",
"ClientId": "<app-registration-client-id>",
"Issuer": "https://<tenant-id>.ciamlogin.com/<tenant-id>/v2.0",
"MetadataEndpoint": "https://<tenant-id>.ciamlogin.com/<tenant-id>/v2.0/.well-known/openid-configuration",
"AllowedAppIds": "<allowed-client-app-guid>,<another-allowed-client-app-guid>"
}
}
}
}
}
}
}
Per-instance settings
| Key | Default | Notes |
|---|---|---|
Enabled |
false |
Instance is skipped during registration when false. |
Route |
— | Required. The HTTP route Entra will POST to (match the Target URL on the Custom Authentication Extension in the Azure Portal). |
ClientId |
— | Required. Application (client) ID of the custom claims provider app registration in the Entra External ID tenant. Validated as the aud claim on inbound tokens. |
Issuer |
— | Required. Must use tenant-ID subdomain format: https://<tenant-id>.ciamlogin.com/<tenant-id>/v2.0. Do NOT use the domain-name format. |
MetadataEndpoint |
— | Required. OIDC discovery URL for the tenant. |
EntraAppId |
99045fe1-7639-4a75-9d4a-577b6ca3810f |
The Microsoft service app issuing callback tokens. Validated against appid/azp. Default is correct for all Entra External ID tenants — override only if Microsoft changes it. |
AllowedAppIds |
"" |
Comma/semicolon-separated list of permitted client-app GUIDs. Empty = allowlist disabled. |
ClockSkewMinutes |
5 |
Tolerance for JWT exp / nbf. |
Instance key = Source name
The instance key (e.g. primary) is auto-populated into ProvisionContext.Source and is also the keyed-DI key under which the app registers its IUserProvisioner (via AddProvisioner<T>(key) in the Runtime Extensions layer). Do not set Source in configuration — it will fail loudly on mismatch.
Multi-instance
Configure multiple entries under Instances: to serve multiple Entra External ID tenants from one API, each with its own client ID, issuer, and provisioner class. Per-instance EntraTokenValidator keeps tenant-specific signing keys cached independently.
Azure Portal setup (summary)
- Register an application (the "custom claims provider app registration") in your Entra External ID tenant. Note its Application (client) ID — this becomes
ClientId. - In the app's manifest, set
acceptMappedClaimstotruein theapisection. (There is no Azure Portal UI toggle for this — manifest edit only.) - Create a Custom Authentication Extension of type onTokenIssuanceStart, targeting the
RouteURL from your configuration. - Attach the extension to the relevant user flow(s) and select the claims you want to issue.
- On the client app registration(s) consuming the flow, add the custom
customRolesclaim to the ID token claim configuration (and remap toroleson the client — seeCirreum.Components.WebAssembly.Authentication).
For the full setup walkthrough, see Microsoft's docs: https://learn.microsoft.com/en-us/entra/identity-platform/custom-claims-provider-configure-custom-extension
Security notes
- Token validation uses Microsoft's signing keys fetched via OIDC discovery, cached per-instance and auto-refreshed on unknown
kid. - The token's
appid/azpclaim is verified againstEntraAppIdto ensure the callback was triggered by Microsoft's Entra service (not by a forged JWT using any other valid Microsoft-signed token). AllowedAppIdsis strongly recommended in production — it prevents other applications within the same Entra tenant from triggering provisioning in your application.- Body integrity is not additionally verified beyond the bearer-token signature envelope. Microsoft's token validation is the authenticity boundary.
What's not in this package
- The app's
IUserProvisionerimplementation — apps register theirs viabuilder.AddIdentity().AddProvisioner<TProvisioner>("instance_key")in the Runtime Extensions layer. This package only resolves the keyed service at callback time. - App-facing
AddEntraExternalIdIdentity()/MapEntraExternalIdIdentity()extensions — those live inCirreum.Runtime.Identity.EntraExternalId. - OIDC webhook-style IdPs (Descope, Auth0) — those use a different protocol and live in the sibling
Cirreum.Identity.Oidcpackage.
License
MIT — see LICENSE.
Cirreum Foundation Framework
Layered simplicity for modern .NET
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Cirreum.IdentityProvider (>= 1.0.3)
- Microsoft.IdentityModel.Protocols.OpenIdConnect (>= 8.18.0)
- System.IdentityModel.Tokens.Jwt (>= 8.18.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Cirreum.Identity.EntraExternalId:
| Package | Downloads |
|---|---|
|
Cirreum.Runtime.Identity.EntraExternalId
Runtime Extensions package for Cirreum Identity EntraExternalId — app-facing AddEntraExternalIdIdentity() and MapEntraExternalIdIdentity() extensions that wire the Entra External ID identity provider from configuration. |
GitHub repositories
This package is not used by any popular GitHub repositories.