Recipes
Recipe matrix
| Recipe | Management group | Sign-in group | Passkeys |
|---|---|---|---|
| Cookie web client (cookie facade) | /identity | cookie /identity | /account |
| Identity bearer (bearer facade) | /identity | bearer /identity | /account |
| JWT-only | e.g. /account | JWT /auth | optional |
| Custom paths | any prefix | matching DI | any prefix |
Which recipe for which client?
| Client | Recommended sign-in | Why |
|---|---|---|
| Browser / SPA (React, Next.js, Vue, Nuxt, Svelte, …) | Cookie (default facade) | Session cookie + CSRF fits first-party web apps; passkeys work well here |
| Browser wanting JWT access tokens | JWT | Short-lived Bearer access token; refresh stays in an HttpOnly cookie + CSRF |
| Native / mobile apps | Identity bearer | Access and refresh tokens in the API response — no cookie jar or CSRF for refresh |
| Mixed web + native | Cookie or JWT for web, Identity bearer for native | Map separate sign-in groups (or hosts) per client type |
Passkeys are WebAuthn-oriented (browsers and platform passkey APIs). With the default Identity completer, register/login issue an Identity bearer token, or an application cookie when ?useCookies=true / ?useSessionCookies=true. For Simple JWT after passkey, register JwtPasskeySignInCompleter (see Passkeys) — do not call password JWT /create after a successful passkey.
POST /identity/login?useCookies=true does nothing under the cookie facade. Cookie facade password login maps LoginCookie, which ignores useCookies. That query only affects bearer Login (MapAuthEndpoints with SignIn = IdentityBearer, or MapBearerAuthEndpoints) and the default passkey completer. Persistent cookie on cookie-facade password login: ?useSessionCookies=false. See Cookie auth.Advanced composition
Compose modules yourself when you need custom paths or JWT-only. Map management once in production hosts.
builder.Services
.AddIdentityApiEndpoints<AppUser>(o =>
{
o.Stores.SchemaVersion = IdentitySchemaVersions.Version3;
})
.AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders();
builder.Services.AddAntiforgery();
builder.Services.AddCookieAuthEndpoints(); // rate limits + ReAuth schemes
builder.Services.AddPasskeyEndpoints<AppUser>();
builder.Services.AddJwtEndpoints<AppUser, AppDbContext>(o =>
{
o.Issuer = "https://example.com";
o.Audience = "https://example.com";
o.SigningOptions.SymmetricKey = builder.Configuration["Jwt:SymmetricKey"];
});
// Optional: Simple JWT after passkey register/login
// builder.Services.AddPasskeySignInCompleter<AppUser, JwtPasskeySignInCompleter<AppUser>>();
// In AppDbContext.OnModelCreating:
// builder.UseRefreshToken();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.UseRateLimiter();
app.UseAntiforgery();
// Cookie web client
app.MapGroup("/identity").MapIdentityManagementApi<AppUser>();
app.MapGroup("/identity").MapCookieAuthEndpoints<AppUser>();
// Or Identity bearer
// app.MapGroup("/identity").MapIdentityManagementApi<AppUser>();
// app.MapGroup("/identity").MapBearerAuthEndpoints<AppUser>();
// Or JWT-only (no cookie login)
// app.MapGroup("/account").MapIdentityManagementApi<AppUser>();
// app.MapGroup("/auth").MapJwtAuthEndpoints<AppUser>();
app.MapGroup("/account").MapPasskeyEndpoints<AppUser>();
Cookie web client
Best default for a first-party web app on the same site (or carefully configured CORS + credentials).
- Management + cookie on
/identity - Passkeys on
/account - Client uses credentials + CSRF token
Identity bearer
Best default for native and mobile clients. Use the bearer facade when the client stores Identity bearer tokens (ASP.NET Core Identity API token format) instead of cookies.
builder.Services.AddAuthEndpoints<AppUser, AppDbContext>(AuthEndpointsSignIn.IdentityBearer, o =>
{
o.Passkeys.ServerDomain = "example.com";
});
app.UseAuthEndpoints();
app.MapAuthEndpoints<AppUser>();
- Maps
MapBearerAuthEndpointsinstead of cookie - CSRF is skipped for bearer-authenticated unsafe methods (see Requirements)
- Compose the same modules by hand if you need a custom prefix
JWT-only
Best for browser clients that want a Bearer access token with an HttpOnly refresh cookie (not a pure token refresh flow for native apps).
- Management on one prefix (e.g.
/account) MapJwtAuthEndpointson/auth(or your choice)- Still call
UseRefreshToken()and migrate - For passwordless + Simple JWT, register
JwtPasskeySignInCompleter(not password/createafter passkey)
Custom paths
Prefixes are arbitrary as long as DI and middleware match. Set facade IdentityPath, PasskeyPath, and Jwt.Path:
builder.Services.AddAuthEndpoints<AppUser, AppDbContext>(o =>
{
o.IdentityPath = "/auth/cookie"; // management + cookie login
o.PasskeyPath = "/auth/passkey"; // maps /auth/passkey/passkeys
o.Jwt.Path = "/auth/jwt"; // used when o.Jwt.Enabled = true
});
For compose hosts, use MapGroup("/your-prefix") with the matching Map* methods.