MapIdentityApi alternative
MapIdentityApi<TUser>() is ASP.NET Core's Identity API. One call maps register, login, refresh, email confirmation, password reset, and account management onto your Identity user store. AuthEndpoints uses that same store. This page lists where MapIdentityApi stops in .NET 10, and which AuthEndpoints call covers each gap.
Register, login, password reset, and two-factor authentication, route by route: Stock Identity endpoints vs AuthEndpoints.
What MapIdentityApi maps
In .NET 10, MapIdentityApi<TUser>() maps this set. Paths sit at the app root unless you wrap the call in MapGroup.
| Method | Path |
|---|---|
POST | /register |
POST | /login |
POST | /refresh |
GET | /confirmEmail |
POST | /resendConfirmationEmail |
POST | /forgotPassword |
POST | /resetPassword |
POST | /manage/2fa |
GET | /manage/info |
POST | /manage/info |
builder.Services
.AddIdentityApiEndpoints<AppUser>()
.AddEntityFrameworkStores<AppDbContext>();
app.MapGroup("/identity").MapIdentityApi<AppUser>();
POST /login uses the Identity bearer scheme unless the query string sets useCookies or useSessionCookies, which issue the application cookie instead. Request bodies are the Identity types (RegisterRequest, LoginRequest, and the rest). There is no GET /manage/2fa. The method's documentation comment mentions logging out. The method does not map a logout route.
Logout
MapIdentityApi has no logout route. The Identity API authorization article tells you to add your own POST /logout that calls SignInManager.SignOutAsync(). That signs out the application scheme, plus the external and two-factor user-id schemes when they are registered. It does not sign out the two-factor remember-client cookie. It does not revoke bearer tokens. The bearer handler treats sign-out as a no-op, so the access token and the refresh token stay valid until they expire.
AuthEndpoints maps a logout route on each sign-in stack. Each stack ends a different thing. Client steps: Sign users out.
| Stack | Route | What logout ends |
|---|---|---|
| Cookie (facade default) | POST /identity/logout | Application, external, two-factor user-id, remember-client, and ReAuth cookies. Requires a signed-in user and a CSRF token. |
| Identity bearer | POST /identity/logout | Those same cookies. Bearer access and refresh tokens stay valid until they expire. Requires a signed-in user. No CSRF token. |
| JWT | POST /auth/logout | The refresh-token family for the refresh cookie, and the cookie itself. The access JWT stays valid until it expires (15 minutes by default). CSRF token required. A signed-in user is not required. |
Cookie logout:
const { csrfToken } = await fetch('/identity/csrfToken', { credentials: 'include' }).then(r => r.json());
await fetch('/identity/logout', {
method: 'POST',
credentials: 'include',
headers: { 'RequestVerificationToken': csrfToken }
});
GET /identity/csrfToken is part of the cookie module. MapIdentityApi does not map it.
Choose which endpoints to map
MapIdentityApi maps the full table above in one call. MapGroup("/identity") prefixes every route with /identity. The builder it returns can add conventions to that group. It has no parameter that omits /register, and no parameter that renames /login.
AuthEndpoints splits the same jobs into modules. Map the ones you need:
| Module | What it adds |
|---|---|
MapIdentityManagementApi | Register, confirm email, resend, forgot/reset password, manage info, set password, 2FA, ReAuth |
MapPasswordEndpoints | Register, forgot/reset password, and login for cookie or Identity bearer. PasswordLoginMode.None omits login. |
MapConfirmEmailEndpoints | GET /confirmEmail and POST /resendConfirmationEmail, once per endpoint name |
MapAccountManagementEndpoints | Manage info, set password, 2FA, auth methods, and POST /confirmIdentity |
MapCookieAuthEndpoints | Cookie POST /login, POST /logout, GET /csrfToken |
MapCookieSessionEndpoints | Cookie POST /logout and GET /csrfToken |
MapBearerAuthEndpoints | Identity bearer POST /login, POST /refresh, POST /logout |
MapBearerSessionEndpoints | Identity bearer POST /refresh and POST /logout |
MapJwtAuthEndpoints | JWT POST /create, POST /refresh, GET /verify, POST /logout, GET /csrfToken |
MapJwtPasswordEndpoints | JWT POST /create |
MapJwtSessionEndpoints | JWT refresh, verify, logout, and CSRF token |
MapPasskeyEndpoints | Passkey register, login, and credential management |
MapPasskeySignInEndpoints | Passkey register and login, plus confirm-email on the parent group when that name is new |
MapPasskeyManagementEndpoints | Signed-in passkey list, add, rename, and delete |
The 3.x facade (MapAuthEndpoints) is one composition: management plus either cookie or Identity bearer sign-in under IdentityPath (default /identity). Passkeys are on by default under PasskeyPath (default /account). JWT is off until Jwt.Enabled is true.
This maps management and cookie sign-in under /api/account, with passkeys and JWT left off:
builder.Services.AddAuthEndpoints<AppUser, AppDbContext>(o =>
{
o.IdentityPath = "/api/account";
o.Passkeys.Enabled = false;
});
builder.Services.AddTransient<IEmailSender<AppUser>, MyEmailSender>();
var app = builder.Build();
app.UseAuthEndpoints();
app.MapAuthEndpoints<AppUser>();
POST /api/account/login on that host is cookie login. The relative names stay /login, /register, and /logout. AuthEndpoints does not rename those segments. MapIdentityManagementApi always includes /register. MapCookieAuthEndpoints always includes /login. The smaller maps omit a piece: MapPasswordEndpoints with PasswordLoginMode.None skips /login, and MapCookieSessionEndpoints maps logout and the CSRF token. An email-code host uses those pieces. See Compose a custom auth stack.
To skip cookie login and map JWT instead, compose the modules. Register the matching services first, call modelBuilder.UseRefreshToken() on the DbContext, and migrate. The route map is:
app.MapGroup("/account").MapIdentityManagementApi<AppUser>();
app.MapGroup("/auth").MapJwtAuthEndpoints<AppUser>();
Full host setup: Compose a custom auth stack. Prefix reference: Configuration options.
JWT, opaque bearer tokens, and cookies
MapIdentityApi login without useCookies or useSessionCookies returns Identity bearer tokens (accessToken, refreshToken, expiresIn, tokenType). Those values are data-protection tickets (BearerTokenProtector and RefreshTokenProtector), not JWTs. Microsoft's Identity API article states that the tokens are not standard JSON Web Tokens. Another service validates them with the Identity bearer handler and the same data-protection keys. JwtBearer and a signing key do not validate them. ?useCookies=true or ?useSessionCookies=true on that same /login issues the application cookie and does not return a token body.
AuthEndpoints has three password sign-in stacks. Identity bearer is the same ticket format as MapIdentityApi. JWT is a separate opt-in.
| Stack | What login returns | Refresh |
|---|---|---|
| Cookie (facade default) | Application cookie. Empty body. | The browser sends the cookie. |
| Identity bearer | Opaque accessToken and refreshToken in the body | POST /identity/refresh with { "refreshToken" } |
| JWT | JWT accessToken in the body, refresh token in the AuthEndpoints.Jwt.RefreshToken cookie | POST /auth/refresh with that cookie and a CSRF token. The stored token is a hash. Each refresh rotates it. Reuse of an old token revokes the family. |
On the 3.x cookie facade, POST /identity/login always uses the application cookie. useCookies is ignored there. A persistent cookie is ?useSessionCookies=false. The flag table for Identity bearer login matches MapIdentityApi. The password facade uses /auth and the same cookie rule. Details: Pick your setup and Stock Identity endpoints vs AuthEndpoints.
Turn JWT on beside the cookie facade. You still call modelBuilder.UseRefreshToken() and add a migration:
builder.Services.AddAuthEndpoints<AppUser, AppDbContext>(o =>
{
o.Passkeys.ServerDomain = "example.com"; // required in Production while passkeys stay on
o.Jwt.Enabled = true;
o.Jwt.Configure = jwt =>
{
jwt.Issuer = "https://example.com";
jwt.Audience = "https://example.com";
jwt.SigningOptions.SymmetricKey = builder.Configuration["Jwt:SymmetricKey"];
};
});
MapAuthEndpoints then also maps /auth/create, /auth/refresh, /auth/verify, /auth/logout, and /auth/csrfToken. Signing keys, algorithms, and lifetimes: JWT module.
Passkeys
MapIdentityApi does not map passkey routes. The .NET 10 Blazor Identity template has passkey pages, and its MapAdditionalIdentityEndpoints posts to /Account/PasskeyCreationOptions and /Account/PasskeyRequestOptions. Those routes belong to that template. They are not MapIdentityApi, and they are not a passkey register, login, and credential API for a separate client.
AuthEndpoints maps that API. The 3.x facade turns it on by default under {PasskeyPath}/passkeys (/account/passkeys). In Production set Passkeys.ServerDomain, or set Passkeys.Enabled to false to skip passkey services and routes. The password facade maps passkeys only when Passkeys.ServerDomain is set, under /auth/passkeys. See Pick your setup.
builder.Services.AddAuthEndpoints<AppUser, AppDbContext>(o =>
{
o.Passkeys.ServerDomain = "example.com";
});
Mapped routes include POST /account/passkeys/register, POST /account/passkeys/login, GET /account/passkeys/, and add, rename, and delete. Passkey sign-in skips two-factor authentication. With no session argument, the completer follows Identity bearer query flags (useCookies or useSessionCookies), not cookie-facade LoginCookie. Pass AuthEndpointsSession.Jwt when passkey sign-in should issue a JWT. Route table and ceremony: Passkeys module. Client steps: Register users and Sign users in.
When MapIdentityApi is enough
Use MapIdentityApi when its ten routes, and a logout handler you write, cover the app. Use AuthEndpoints when you need a logout route, a prefix with only some modules, a JWT access token, or passkey endpoints.