Skip to content

ZigBase 0.5.0

Choose a tag to compare

@github-actions github-actions released this 22 Jun 01:16
· 1111 commits to main since this release
Immutable release. Only release title and notes can be modified.

Removed

  • BREAKING: legacy OAuth2 endpoints removed — GET .../oauth2-providers, POST .../oauth2-init, and POST .../auth-with-oauth2 no longer exist. OAuth2 is now exclusively the contract method: POST .../auth/oauth2/initiate, POST .../auth/oauth2/complete, and GET .../auth/oauth2/providers (discovery).

Added

  • OAuth2 as a first-class AuthMethod — exclusively at the contract endpoints POST /auth/oauth2/initiate, POST /auth/oauth2/complete, and GET /auth/oauth2/providers (discovery); all paths share one implementation and the single onAuth session seam. See docs/api.md for request/response shapes.
  • Pluggable auth-method system — the AuthMethod contract (initiate/complete + AuthCtx blessed helpers + Resolution) lets the framework own session issuance while methods plug in verification logic. Built-ins implement the same contract with no privileged path.
  • Per-collection .auth.methods config — enable and configure built-in methods per auth collection: password (backward-compat default when .methods is absent), magic_link (TTL, auto-create flag), otp (code length, TTL), webauthn (rp_id, rp_name, origin, credentials_collection). Each method has a rate_limit knob (.default | .off | .{ .custom = .{ .max, .window_s } }).
  • App-level .auth_methods — register custom AuthMethod plugin TYPES at comptime (same pattern as .storage/.mailer); a type missing create/method/deinit is a compile error.
  • Auto-mounted auth endpoints — for every enabled method, the framework auto-mounts POST /api/collections/:col/auth/:method/initiate and .../complete; the dispatch enforces enablement (404 for disabled/unknown methods) and default rate-limits.
  • magic_link built-in — enumeration-safe initiate (always 204), single-use link token emailed via the configured mailer, complete verifies+consumes and mints the session.
  • otp built-in — enumeration-safe initiate emails a 6-digit code stored in the ChallengeStore, complete verifies the code.
  • webauthn built-in — passkey login via the two-phase contract (initiate returns PublicKeyCredentialRequestOptions; complete verifies the signed assertion). Passkey registration via two authed endpoints (register/begin / register/finish). ES256 (P-256, COSE -7) and Ed25519 (COSE -8) supported; attestation fmt:"none" (v1); signCount clone detection (fail-closed); credentials stored in _webauthnCredentials.
  • ChallengeStore (_authChallenges) — TTL'd, GC'd single-use server-side challenge storage used by otp and webauthn, and accessible to custom plugins via AuthCtx.challengeStore().
  • onAuth method tagging extended — AuthEvent.method is an enum: .password, .oauth2, .magic_link, .otp, .webauthn, or .custom for custom plugins.
  • RPC client generation for auth endpoints — the generated TypeScript client exposes non-password auth-method endpoints under an auth surface (initiate/complete stubs, currently untyped).
  • zigbase.auth consumer surface for custom auth flows — issueSession (and RouteEvent.issueSession), single-use magic-link tokens (mintLinkToken / verifyLinkToken / consumeLinkToken), deliverAuthMail, and rateLimit. All session minting now funnels through one seam that always fires onAuth.

Security

  • OAuth2 server-side CSRF state is now ON by default (ZIGBASE_OAUTH_STATE_SERVER defaults to true). The initiate endpoint issues a state value and complete requires and consumes it before contacting the provider. Behavior change: OAuth2 clients must use the initiate→complete flow; bare complete calls without a valid state are rejected with 400. Set ZIGBASE_OAUTH_STATE_SERVER=false to restore the previous client-driven mode.
  • New require_verified per-collection auth option (default false). When true, any login attempt for an unverified record is rejected with 403. This gate applies to all methods — including WebAuthn/passkey and OAuth2 accounts whose provider email was unverified (those are created verified=false). Enabling it will lock out such users until they complete email verification.
  • OAuth2 no longer claims unverified provider emails — when a provider does not mark the email as verified, the new account is created with verified=false and the email field is left unpopulated. This prevents email-squatting via an OAuth2 provider that does not verify addresses.
  • WebAuthn credential binding — a passkey is now bound to the collection it was registered on; presenting it on a different collection returns 401.
  • WebAuthn require_uv option (default false). When true, the server rejects assertions that do not set the user-verification bit (UV=1), requiring biometrics or PIN at the authenticator.
  • WebAuthn COSE key curve validation — ES256 credentials must use the P-256 curve; EdDSA credentials must use Ed25519. A mismatched algorithm/curve is rejected.

Performance

  • Auth I/O off the write lock. otp and magic_link release the DB connection before the SMTP send. WebAuthn signature verification runs before acquiring the write lock (only the signCount update and challenge consume hold it). oauth2Providers uses a reader connection. The authenticated-request fast path no longer does a redundant collection lookup. No auth method holds the single writer across blocking I/O or CPU-heavy verification.

Changed

  • Auth methods now manage their own DB connections — each method holds one connection across its work; OAuth2 complete releases the writer during the provider HTTP exchange (no write-throughput stall); password complete uses a reader (argon2 is read-only). Neither method blocks writes during I/O.
  • Session issuance (password, refresh, OAuth2) routes through a single
    issueSession+emitAuth seam
    — custom routes can no longer mint a session that
    skips the onAuth hook.