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 (cookie facade)/identitycookie /identity/account
Identity bearer (bearer facade)/identitybearer /identity/account
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.

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>();

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 MapBearerAuthEndpoints instead 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)
  • 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. 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.