Changelog
Getting Started

Stock Identity vs AuthEndpoints

Side-by-side register, login, password reset, and 2FA for MapIdentityApi and the AuthEndpoints facade.

Both stacks sit on ASP.NET Core Identity and your EF user store. Stock here means Microsoft's Identity API endpoints (AddIdentityApiEndpoints / MapIdentityApi) or the same routes written by hand. AuthEndpoints means the cookie facade: AddAuthEndpoints / UseAuthEndpoints / MapAuthEndpoints.

Request bodies stay Identity-shaped (RegisterRequest, LoginRequest, ForgotPasswordRequest, ResetPasswordRequest, TwoFactorRequest). AuthEndpoints maps the routes and adds cookie login, CSRF where required, rate limits, and ReAuth. It does not invent alternate DTOs.

AuthEndpoints tables use the facade default prefix /identity (IdentityPath). Stock MapIdentityApi has no built-in prefix unless the host wraps it in MapGroup.

AuthEndpoints-only on this surface: GET /identity/csrfToken, cookie POST /identity/logout, and ReAuth (/manage/authMethods, /confirmIdentity). Stock MapIdentityApi does not map logout.

Client steps for these flows: Examples. Product positioning against OpenIddict: Compare.

Host setup

Stock (MapIdentityApi)

builder.Services
    .AddIdentityApiEndpoints<AppUser>()
    .AddEntityFrameworkStores<AppDbContext>();

var app = builder.Build();

// Optional prefix — without MapGroup, routes are at the app root (/register, /login, …).
app.MapGroup("/identity").MapIdentityApi<AppUser>();

You still wire authentication, authorization, antiforgery, CORS, and rate limiting yourself. Login is bearer-first unless the client passes cookie query flags.

builder.Services.AddAuthEndpoints<AppUser, AppDbContext>(o =>
{
    o.Passkeys.Enabled = false; // thin cookie path; omit or set ServerDomain when passkeys stay on
    o.Jwt.Enabled = false;
});
builder.Services.AddTransient<IEmailSender<AppUser>, MyEmailSender>();

var app = builder.Build();

app.UseAuthEndpoints(); // authentication, authorization, rate limiting, antiforgery
app.MapAuthEndpoints<AppUser>();

Passkeys and JWT are off. When you leave passkeys enabled (facade default), set Passkeys.ServerDomain in Production. See Quick start.

CSRF on the AuthEndpoints cookie stack applies to cookie logout, manage POSTs, ReAuth, and authenticated resendConfirmationEmail. It does not apply to register, login, forgotPassword, or resetPassword.

Register

Stock Identity APIAuthEndpoints facade
RoutePOST /register (or POST /identity/register with MapGroup)POST /identity/register
BodyIdentity register (email, password)Same Identity body
CSRFHost policy (endpoint does not require antiforgery by default)Not required (RequireAntiforgery is not on register)
Duplicate email200 (no enumeration)200 (no enumeration)
Confirmed-account policyHost Identity optionsRequireConfirmedAccount default true
After 200Not signed inNot signed in (200 ≠ session)
ConfirmGET …/confirmEmail?userId&codeGET /identity/confirmEmail?userId&code, then login. Optional ConfirmEmailRedirectUri302 with status / flow
Rate limitHost-ownedBuilt-in account rate limits

Client flow: register → check email → confirm → login. Steps: Register a confirmed account.

Login

Stock Identity APIAuthEndpoints facade
RoutePOST /login (or under your MapGroup)POST /identity/login
HandlerIdentity LoginLoginCookie
BodyIdentity LoginRequestSame (email, password, optional 2FA fields)
Default successIdentity bearer tokens (AccessTokenResponse)Application cookie only (no bearer): empty body + Set-Cookie (TypedResults.Empty). Session when the flag is omitted
Cookie query flagsuseCookies / useSessionCookies select cookie vs bearerOnly useSessionCookies (useCookies ignored). Persistent: ?useSessionCookies=false
CSRF on loginNot requiredNot required
LogoutNot in MapIdentityApiPOST /identity/logout (auth + CSRF)
CSRF helperHost-ownedGET /identity/csrfToken for logout, manage, ReAuth, resend
LockoutIdentity lockoutLockout + login rate limit

Full flag tables: Cookie auth. Bearer-style Identity login stays available via compose (MapBearerAuthEndpoints) or SignIn = IdentityBearer. It is not the cookie-facade default.

Password reset

Stock Identity APIAuthEndpoints facade
ForgotPOST …/forgotPassword { email } → always 200POST /identity/forgotPassword → always 200
CSRF / session on forgotNot requiredNot required
MailYour IEmailSender<TUser>Same (Production rejects Identity's no-op sender)
Reset codeFrom mailBase64Url token from the sender; send that value as resetCode
ResetPOST …/resetPassword { email, resetCode, newPassword }Same Identity body on /identity/resetPassword
CSRF / session on resetNot requiredNot required
ThenPOST …/loginPOST /identity/login (LoginCookie)

Client steps: Reset a forgotten password.

Two-factor authentication

Stock Identity APIAuthEndpoints facade
StatusNo GET — use POST /identity/manage/2fa (response includes sharedKey / flags)GET /identity/manage/2faisTwoFactorEnabled only
Shared keyOn stock POST response (TwoFactorResponse.sharedKey)Mint/read via POST /identity/manage/2fa + {} (not GET); CSRF + ReAuth
Enable / disableAuth’d POST /manage/2fa (TwoFactorRequest)Auth’d POST /identity/manage/2fa (same Identity body); CSRF + ReAuth
CSRF on POSTNot required by MapIdentityApiRequired (RequestVerificationToken)
Step-upNot built inReAuth required (confirmIdentity, then cookie or X-AuthEndpoints-Reauth)
enable + resetSharedKeyAvoid togetherDo not send together (validation problem)
Login with 2FAtwoFactorCode / twoFactorRecoveryCode on LoginSame fields on LoginCookie

Client steps: Enable and disable two-factor authentication and Complete step-up (ReAuth).

After these four flows

AuthEndpoints also maps manage info, ReAuth routes, and optional passkeys/JWT. Stock MapIdentityApi does not include passkeys, ReAuth, cookie logout, or csrfToken. Compose only the modules you need when the facade is too much. See Composable endpoints and Modules.