Changelog
Composable Endpoints

Recipes

Common composition patterns for cookie web clients, Identity bearer, JWT-only, and custom paths.

Recipe matrix

RecipeManagement groupSign-in groupPasskeys
Cookie web client (default facade)/identitycookie /identity/account
Identity bearer/identitybearer /identityoptional
JWT-onlye.g. /accountJWT /authoptional
Custom pathsany prefixmatching DIany prefix

Which recipe for which client?

ClientRecommended sign-inWhy
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 tokensJWTShort-lived Bearer access token; refresh stays in an HttpOnly cookie + CSRF
Native / mobile appsIdentity bearerAccess and refresh tokens in the API response — no cookie jar or CSRF for refresh
Mixed web + nativeCookie or JWT for web, Identity bearer for nativeMap 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.

Advanced composition

Compose modules yourself when you need bearer Identity, 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>();

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 when the client stores Identity bearer tokens (ASP.NET Core Identity API token format) instead of cookies.

  • Map MapBearerAuthEndpoints instead of cookie
  • CSRF is skipped for bearer-authenticated unsafe methods (see Requirements)

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)
  • MapJwtAuthEndpoints on /auth (or your choice)
  • Still call UseRefreshToken() and migrate
  • For passwordless + Simple JWT, register JwtPasskeySignInCompleter (not password /create after passkey)

Custom paths

Prefixes are arbitrary as long as DI and middleware match. The Demo host uses paths such as /auth/cookie, /auth/passkey, and /auth/jwt via facade options.