AuthEndpoints is an ASP.NET Core library that gives you ready-made auth API endpoints on top of ASP.NET Core Identity. It is a good fit for a single-backend API serving a SPA or frontend app with first-party email/password, cookies, JWT, and/or passkeys.
It provides a number of features that make it easy to build first-party auth fast, keep defaults safer for production, and compose only what you need, including:
- Ready-made endpoints - instead of wiring registration, login, password reset, 2FA, and session/token flows yourself, map a small set of composable endpoints (or use the opinionated facade) and ship
- Opinionated quick start -
AddAuthEndpoints/UseAuthEndpoints/MapAuthEndpointsfor cookie Identity + passkeys with secure defaults - Account lifecycle - register, email confirm/resend, forgot/reset password, manage info and 2FA, step-up ReAuth
- Sign-in stacks you choose - cookie sessions, Identity bearer tokens, or simple JWT (create / refresh / verify / logout)
- Passkeys (WebAuthn) - passwordless register and login endpoints
- Built-in hardening - rate limiting, antiforgery for cookie flows, lockout-aware login, hashed JWT refresh tokens with reuse detection
dotnet add package AuthEndpoints --version 3.0.0-rc.2
Requirements: .NET 10, ASP.NET Core Identity, and EF Core for the user store.
Your frontend is a separate SPA that calls these routes on the ASP.NET Core API.
// Program.cs
builder.Services.AddDbContext<AppDbContext>(/* your provider */);
builder.Services.AddAuthEndpoints<AppUser, AppDbContext>(o =>
{
o.Passkeys.ServerDomain = "example.com"; // required in Production
});
// Required in Production (Identity's no-op sender is rejected).
builder.Services.AddTransient<IEmailSender<AppUser>, MyEmailSender>();
var app = builder.Build();
app.UseAuthEndpoints(); // authentication, authorization, rate limiting, antiforgery
app.MapAuthEndpoints<AppUser>(); // /identity (management + cookie) + /account (passkeys)
app.Run();| Area | Path prefix | Main routes |
|---|---|---|
| Management + cookie | /identity |
register, login, logout, csrfToken, confirm/forgot/reset, manage/*, confirmIdentity |
| Passkeys | /account |
passkeys register/login options + complete |
| JWT (if enabled) | /auth |
create, refresh, verify, logout, csrfToken |
Cookie stack
- Send cookies with credentials (
credentials: "include"/ AxioswithCredentials). - Call
GET /identity/csrfToken, then send the token asRequestVerificationTokenon unsafe methods (POST/PUT/PATCH/DELETE).
JWT stack (when enabled)
POST /auth/createreturns an access token; send it asAuthorization: Bearer ….- Refresh token is an HttpOnly cookie;
POST /auth/refreshandPOST /auth/logoutneed CSRF (GET /auth/csrfToken+RequestVerificationToken).
builder.Services.AddAuthEndpoints<AppUser, AppDbContext>(o =>
{
o.Passkeys.ServerDomain = "example.com";
o.Jwt.Enabled = true;
o.Jwt.Path = "/auth";
o.Jwt.Configure = jwt =>
{
jwt.Issuer = "https://example.com";
jwt.Audience = "https://example.com";
jwt.SigningOptions.SymmetricKey = builder.Configuration["Jwt:SymmetricKey"];
};
});Mapped with Identity management under /identity:
GET /identity/manage/authMethodsPOST /identity/confirmIdentitywith exactly one of:password,twoFactorCode,twoFactorRecoveryCode,credentialJson- Cookie clients receive an
AuthEndpoints.ReAuthcookie; API clients also getreauthTokenfor theX-AuthEndpoints-Reauthheader
POST /account/passkeys/register/optionswith{ "email": "..." }- Browser
navigator.credentials.create(...) POST /account/passkeys/registerwith{ "email": "...", "credentialJson": "..." }
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();
builder.Services.AddJwtEndpoints<AppUser, AppDbContext>(o =>
{
o.Issuer = "https://example.com";
o.Audience = "https://example.com";
o.SigningOptions.SymmetricKey = builder.Configuration["Jwt:SymmetricKey"];
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.UseRateLimiter();
app.UseAntiforgery();
// Cookie SPA
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>();- Use HTTPS
- Register a real
IEmailSender<TUser>(Identity's no-op sender is rejected in Production) - Set
Passkeys.ServerDomainwhen passkeys are enabled - For JWT: non-default issuer/audience and a symmetric key of at least 256 bits (32 UTF-8 bytes) in Production
- Recreate the
AuthEndpointsRefreshTokenstable if upgrading from plaintext refresh-token storage (tokens are stored hashed with family reuse detection)