Skip to content

Microsoft identity headers in Cratis Arc

Cratis Arc can reconstruct an authenticated principal from the Microsoft client-principal headers supplied by a trusted ingress. Use this integration when Azure App Service or Container Apps authentication (EasyAuth), or Cratis AuthProxy, authenticates callers before forwarding requests to your ASP.NET Core application.

The ingress is the service in front of your application that receives those requests. The setup below trusts its unsigned identity headers, so it must strip caller-supplied identity headers, write its own replacements, and prevent direct backend access. Arc’s handler reads these headers:

Header Description
x-ms-client-principal The unsigned principal payload holding the details, base64 encoded Microsoft Client Principal Data definition
x-ms-client-principal-id The unique identifier from the identity provider for the identity. A value in the RFC 8187 form UTF-8'' followed by percent-encoded UTF-8, which AuthProxy uses for an identifier that is not printable US-ASCII, is decoded
x-ms-client-principal-name The name of the identity, typically resolved from claims within the token

For local development without an identity provider, generate a principal fixture and inject it as request headers, for example with a browser extension. The x-ms-client-principal value must be the base64-encoded Microsoft Client Principal Data definition.

Register the header authentication handler with AddMicrosoftIdentityPlatformIdentityAuthentication(). It creates the request principal used by ASP.NET Core and Arc authorization; it does not decide which endpoints require that principal.

This registration fragment is for an ASP.NET Core application using Cratis.Arc, not ArcApplication. Enable header trust only after establishing the ingress boundary described above:

using Cratis.Arc;
using Cratis.Arc.Identity;
var builder = WebApplication.CreateBuilder(args);
builder.AddCratisArc(options => options.TrustForwardedIdentityHeaders = true);
builder.Services.AddMicrosoftIdentityPlatformIdentityAuthentication();
builder.Services.AddAuthorization();

The extension calls AddAuthentication() with the default scheme name MicrosoftIdentityPlatform and registers its handler. Pass a string argument to the extension if you need a different scheme name.

Without signed forwarding enabled, the handler ignores these unsigned headers until the host opts in with TrustForwardedIdentityHeaders. It returns no authentication result, so the request stays anonymous unless another scheme authenticates it. Set the option in code, as above, or in configuration:

{
"Cratis": {
"Arc": {
"TrustForwardedIdentityHeaders": true
}
}
}

This setting applies to every endpoint, including protected introspection catalogs. A host that calls AddMicrosoftIdentityPlatformIdentityAuthentication() (including through AddCratis()) while the headers are not trusted logs a warning naming the setting on every startup. The built-in header handlers also warn on the first request carrying untrusted headers.

If your ingress must sign forwarded identities, configure signed forwarded identity as well. That mode also requires TrustForwardedIdentityHeaders, but refuses unsigned forwards once enabled. It needs a compatible signer; current unmodified AuthProxy does not produce that envelope. Check the signing guide’s producer requirements before enabling it.

After building the application, add authentication and authorization middleware before activating Arc:

var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.UseCratisArc();
app.Run();

This establishes the host authentication mechanism; apply authorization requirements to private operations. Identity details do not automatically protect every endpoint.

Knowing which identity provider signed the caller in

Section titled “Knowing which identity provider signed the caller in”

The identityProvider field in x-ms-client-principal identifies the provider reported by the ingress, such as Entra ID or GitHub. Arc copies that value into a claim on the reconstructed principal. Request authorization and /.cratis/me identity resolution can read it:

using Cratis.Arc.Identity;
using Microsoft.AspNetCore.Http;
public class IdentityProviderReader(IHttpContextAccessor httpContextAccessor)
{
public string? Current =>
httpContextAccessor.HttpContext?.User.FindFirst(MicrosoftIdentityPlatformClaims.IdentityProvider)?.Value;
}
Aspect Detail
Claim type urn:cratis:arc:identity:provider — use the MicrosoftIdentityPlatformClaims.IdentityProvider constant
Value The exact identityProvider value the ingress forwarded, for example aad or github
When absent The forwarded principal carried no identityProvider field, or the field held only blank characters

Arc reserves this claim type. Before writing its value, Arc removes claims of that type from the forwarded payload, ignoring casing. This prevents a forwarded claim from displacing the value copied from identityProvider.

Read the claim with FindFirst or FindAll, as in the example. These lookups compare claim types the same way Arc does when removing forwarded copies. Do not normalize claim types yourself: applying ToUpperInvariant() or Trim() while enumerating User.Claims widens the match to types Arc did not remove. A forged type using Unicode case folding (urn:cratiſ:arc:identity:provider, U+017F) or trailing whitespace can then match. Since forwarded claims precede Arc’s own claim, FirstOrDefault() over that widened set can return the forgery.

When Cratis AuthProxy forwards a session in which a support person or an agent acts on behalf of a user, it adds an x-cratis-on-behalf-actor header and a signed x-cratis-on-behalf-assertion. The principal stays the represented user, and the actor is attached to it. Arc rejects both headers unless on-behalf forwarding is turned on and the assertion verifies. See Acting on behalf of a user for the configuration, the verification rules and how to read the actor.

Once authentication is configured, use identity details to supply application-specific information to the UI. Those details are presentation data, not an authentication ticket or additional authorization claims. Protect operations through authorization requirements on the authenticated principal.