---
title: Microsoft Identity
---

Cratis' Arc provides a way to easily work with providing an object that represents properties the application finds important for describing
the logged in user. The purpose of this is to provide details about the logged in user on the ingress level of an application and letting it
provide the details on the request going in. Having it on the ingress level lets you expose the details to all microservices behind the ingress.

The values provided by the provider are values that are typically application specific and goes beyond what is already found in the token representing the user.
This is optimized for working with Microsoft Azure well known HTTP headers passed on by the different app services, such as Azure ContainerApps or WebApps.
Internally, it is based on the following HTTP headers to be present.

| Header | Description |
| --- | --- |
| x-ms-client-principal | The unsigned principal payload holding the details, base64 encoded [Microsoft Client Principal Data definition](https://learn.microsoft.com/en-us/azure/static-web-apps/user-information?tabs=csharp#client-principal-data) |
| x-ms-client-principal-id | The unique identifier from the identity provider for the identity |
| x-ms-client-principal-name | The name of the identity, typically resolved from claims within the token |

> Important note: Since local development is not configured with the identity provider, but you still need a way to test that both the backend and the frontend
> deals with the identity in the correct way. This can be achieved by creating the correct token and injecting it as request headers using
> a browser extension. Read more about [generating principal tokens for local development](/arc/backend/csharp/development/generating-principal/).

The token in the `x-ms-client-principal` should be a base64 encoded [Microsoft Client Principal Data definition](https://learn.microsoft.com/en-us/azure/static-web-apps/user-information?tabs=csharp#client-principal-data).

:::caution
Use these headers only behind ingress that authenticates callers, strips caller-supplied identity headers, writes trusted replacements, and prevents direct backend access. Base64 is not a signature. Generated principals are local test fixtures, not credentials to accept on an internet-facing service. A cookie produced by identity enrichment is likewise not an authentication ticket.
:::

## Authentication / Authorization

To get the Microsoft Client Principal supported in your backend, the Arc offers an `AuthenticationHandler` that supports the HTTP headers and
does the right thing to put ASP.NET Core and every `HttpContext` in the right state.

You can add this by calling the `AddMicrosoftIdentityPlatformIdentityAuthentication()` method on your services.

This is a registration fragment for an ASP.NET Core application using `Cratis.Arc` (not `ArcApplication`):

```csharp
using Cratis.Arc;
using Cratis.Arc.Identity;

var builder = WebApplication.CreateBuilder(args);
builder.AddCratisArc();
builder.Services.AddMicrosoftIdentityPlatformIdentityAuthentication();
builder.Services.AddAuthorization();
```

The above code will then also call the `.AddAuthentication()` with the default scheme name (**MicrosoftIdentityPlatform**) and register
the appropriate `AuthenticationHandler` for that scheme.

You can override the scheme name on the extension method by passing your own string as an argument.

For it to be appropriately setup, you'll need to enable the default authentication and authorization on your app, like below:

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

This establishes the host authentication mechanism; apply [authorization requirements](/arc/backend/csharp/asp-net-core/authorization/) to private operations. Identity details do not automatically protect every endpoint.

## Knowing which identity provider signed the caller in

The `x-ms-client-principal` payload carries an `identityProvider` field describing the provider the ingress
authenticated the caller with. Without it, everything downstream sees is a set of claims that look the same whether
they came from Entra ID or GitHub — so the application has no way to tell one federation apart from another.

Arc keeps that value on the reconstructed principal as a claim, so both normal request authorization and the
`/.cratis/me` identity resolution can read it:

```csharp
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 |

The claim type is **reserved for Arc**. The `x-ms-client-principal` header is base64, not a signature, so any caller
that can reach the application can put whatever it likes in the serialized payload — including a claim of this very
type. Arc therefore removes every claim of the reserved type from the deserialized payload, ignoring casing, _before_
writing its own value.

:::caution
**What the strip guarantees is single provenance, not authenticity.** When a nonblank provider value is supplied, the claim carries exactly one value,
and that value always comes from one place — the `identityProvider` field of the forwarded principal — so a claim of
the reserved type passed through by the ingress or by the identity provider can never displace it. It does **not**
make the value trustworthy. The same unsigned header carries that field too, and Arc does not check who sent the
header, so a caller that can reach the application authors the whole document; the strip only decides which of its
fields wins. **Trust this claim exactly as far as you trust the `x-ms-client-principal` header itself — that is,
only insofar as your ingress is the only thing that can set it.** Terminate the header at the ingress and reject or
overwrite an inbound one; nothing inside Arc can do that for you.
:::

Read the claim with `FindFirst` or `FindAll`, as in the example above, and **never normalize the claim type yourself**.
Those lookups compare the claim type the same way the strip does, so what they return is exactly what Arc wrote.
Enumerating `User.Claims` and folding the type with `ToUpperInvariant()` or `Trim()` widens the match beyond what was
removed — a forged type differing only by Unicode case folding (`urn:cratiſ:arc:identity:provider`, U+017F) or by
trailing whitespace then matches, and because forwarded claims are added before Arc's own, a `FirstOrDefault()` over
that widened set returns the forgery.

:::note
The value is the exact `identityProvider` field the ingress forwarded, verbatim and untrimmed — Arc neither
interprets nor normalizes it. What it _means_ is therefore the ingress's choice, not Arc's. Cratis AuthProxy
forwards the canonical provider key, the same value it publishes as its own `urn:cratis:identity:provider-key`
claim, so in a canonical AuthProxy deployment the two carry identical values. Another ingress may forward an
authentication scheme name or a provider display name that changes when the provider is renamed. **Arc guarantees
neither**, so use this claim for telling federations apart, for diagnostics, and for provider-aware behavior. If you
need a durable provider key, read the claim the ingress publishes for that purpose — `urn:cratis:identity:provider-key`
in an AuthProxy deployment — rather than this one. Arc copies such ingress-authored claims through untouched.
:::

## Identity Details

For information about providing additional identity details for logged-in users, including authorization checks and custom identity information, see the [Identity documentation](/arc/backend/csharp/identity/).

Use Arc's identity-details provider to add application information to the principal supplied by Microsoft Identity. Those details help the UI; authorization still checks the authenticated principal.
