Skip to content

ASP.NET Auth Credentials ​

Adds username or email and password authentication to ASP.NET Core APIs, storing users, refresh-token sessions, password reset tokens, email verification tokens, and TOTP enrolments in the application's own Entity Framework Core context.

Use this package when an API needs first-party accounts instead of an external provider such as Discord or Google. It owns the credential side of that: verifying passwords, rotating refresh sessions, locking accounts after repeated failures, running the forgot-password flow, confirming email addresses, and enrolling a second factor. Issuing and validating the access token itself is left to ASP.NET Auth, which this package builds on.

Application code depends on small service contracts rather than one aggregate, so a controller only takes the part of the surface it uses. Every credential failure surfaces as a typed exception that maps to a standardized HTTP error through ASP.NET Core, with the text resolved per language.

Categories ​

  • Configuration — session lifetime ceiling, password reset and email verification lifetimes, lockout policy, and two-factor policy.
  • Exceptions — every credential failure, with the status code and error value it becomes.
  • Localization — the auth and passwords message files those failures resolve through.
  • Extensions — service registration against the application's context and user entity.
  • Requests — request models for login, registration, password, and email verification flows.
  • Results — what a credential flow returns when it opens a session, stops at a second factor, or begins an enrolment.
  • Services — dependency-injection contracts for login, session, password, email address, and two-factor operations.
  • Utilities — the digest every stored token is matched by.
  • Types — the base context and the entities it maps.

Quick Example ​

DANGER

result.User is the database entity. Returned as written here, it serializes with the password hash, the surrogate key, and any loaded sessions, so map it to a DTO that exposes only the fields the client needs before returning it.

csharp
using AlmightyShogun.AspNet.Core;
using AlmightyShogun.AspNet.Auth;
using Microsoft.EntityFrameworkCore;
using AlmightyShogun.AspNet.Localization;
using AlmightyShogun.AspNet.Auth.Credentials;
using AlmightyShogun.AspNet.RequestValidation;

builder.Services
    .AddMessageLocalization(builder.Configuration)
    .AddHttpErrorResponseWriter()
    .AddExceptionHandling()
    .AddAuth(builder.Configuration)
    .AddAspNetValidation()
    .AddDbContext<AppDbContext>(options => ...)
    .AddAuthCredentials<AppDbContext, AppUser>(builder.Configuration);
csharp
using Microsoft.AspNetCore.Mvc;
using AlmightyShogun.AspNet.Auth;
using AlmightyShogun.AspNet.Auth.Credentials;

[ApiController]
[Route("auth")]
public sealed class AuthController(
    IAuthUserService<AppUser> authUsers
) : ControllerBase
{
    [HttpPost("login")]
    public async Task<ActionResult<AppUser>> Login(LoginRequest request)
    {
        AuthLoginResult<AppUser> result = await authUsers
            .LoginAsync(request, HttpContext);

        if (result.RequiresTwoFactor)
            return Accepted(new { challenge = result.Challenge });

        Response.SetRefreshTokenCookie(result.Session.RefreshToken, 30);

        return Ok(result.User);
    }

    [HttpPost("login/two-factor")]
    public async Task<ActionResult<AppUser>> CompleteTwoFactorLogin(
        CompleteTwoFactorLoginRequest request
    )
    {
        AuthSessionResult<AppUser> result = await authUsers
            .CompleteTwoFactorLoginAsync(request, HttpContext);

        Response.SetRefreshTokenCookie(result.RefreshToken, 30);

        return Ok(result.User);
    }
}
csharp
using Microsoft.EntityFrameworkCore;
using AlmightyShogun.AspNet.Auth.Credentials;

public sealed class AppDbContext(
    DbContextOptions<AppDbContext> options
) : AuthDbContext<AppUser>(options);
csharp
using AlmightyShogun.AspNet.Auth.Credentials;

public sealed class AppUser : AuthUser
{
    public string DisplayName { get; set; } = string.Empty;
}

All packages are released under the MIT License.