Skip to content

ADR 0001: Issue and validate JWTs with standard .NET libraries, not Noundry.Authnz

  • Status: Accepted, 2026-08-28

Noundry.Authnz was considered for Bookend’s authentication. Version 1.3.0 is an OAuth 2.0 / OIDC client library: sign-in with Google, Microsoft, GitHub and similar providers, cookie-based sessions and Razor tag helpers. It has no surface for issuing RS256 tokens, publishing JWKS, rotating refresh tokens, email sign-in links or API-key exchange, all of which Bookend requires, and its cookie and session model conflicts with Bookend’s stateless bearer-token design.

Bookend implements its own identity issuer as application services over the users, auth_refresh_tokens, auth_otp_tokens, api_keys and auth_events tables, using:

  • Microsoft.IdentityModel.JsonWebTokens to sign RS256 access tokens (15 minutes by default) with a keypair generated on first boot and stored encrypted in settings (auth.signing_key);
  • Microsoft.AspNetCore.Authentication.JwtBearer to validate them, with the public key published at /v1/.well-known/jwks.json;
  • Argon2id for password hashing, and SHA-256 for refresh-token, sign-in link and API-key material at rest;
  • rotating refresh tokens, revocation, emailed single-use sign-in links (Noundry.Sanquhar over the bank’s SMTP relay) and API-key to JWT exchange at /v1/auth/token.

Noundry.Authnz is not referenced anywhere in the solution.

  • Bookend owns a small but security-critical component. It is covered by an authorization-matrix contract test (every endpoint against every role) and by end-to-end sign-in tests as release gates.
  • Tokens live in memory on the client; there are no cookies and no server sessions, which is also what lets the MCP surface share the same bearer authentication.
  • Later additions built on the same issuer: authenticator-app MFA (RFC 6238 TOTP on standard .NET cryptography) and OpenID Connect federation, where the api acts as a confidential client, validates the IdP’s id_token and then issues the same token pair as password sign-in. Authorization, refresh rotation and audit are identical for every sign-in method.
  • If Noundry later ships a token-issuing package, adopting it would be a new ADR; nothing here forecloses it.