Add passkey (WebAuthn) authentication with self-registration

Enable users to sign up and sign in using passkeys (biometrics/security
keys) without needing a manually-created Authentik account. The existing
SSO login remains as an alternative.

Backend:
- Add WebAuthn registration/authentication endpoints via py-webauthn
- Issue HS256 JWTs for passkey users, with Redis-backed challenge storage
- Dual JWT verification in auth middleware (issuer-based routing: passkey
  HS256 vs Authentik RS256)
- PasskeyCredential model + migration making user.password nullable
- UserRepository with full CRUD for users and credentials

Frontend:
- AuthUser type abstraction unifying OIDC and passkey users
- Passkey service using @simplewebauthn/browser for WebAuthn ceremonies
- LoginModal redesigned with Sign In / Sign Up tabs
- Type migration from oidc-client-ts User to AuthUser across all services
  and components
This commit is contained in:
Viktor Barzin 2026-02-07 00:34:47 +00:00
parent 95c0ddc4c6
commit a8b7eace48
No known key found for this signature in database
GPG key ID: 0EB088298288D958
26 changed files with 1229 additions and 129 deletions

View file

@ -1,4 +1,5 @@
from datetime import timedelta
import os
# Authentik OIDC Configuration
@ -14,3 +15,14 @@ OIDC_CACHE_TTL = timedelta(
DEV_TIER_ORIGINS = ["https://localhost/"]
PROD_TIER_ORIGINS = ["https://wrongmove.viktorbarzin.me/"]
# WebAuthn / Passkey Configuration
WEBAUTHN_RP_ID = os.getenv("WEBAUTHN_RP_ID", "localhost")
WEBAUTHN_RP_NAME = os.getenv("WEBAUTHN_RP_NAME", "Wrongmove")
WEBAUTHN_ORIGIN = os.getenv("WEBAUTHN_ORIGIN", "https://localhost")
# JWT Configuration (for passkey-issued tokens)
JWT_SECRET = os.getenv("JWT_SECRET", "change-me-in-production")
JWT_ALGORITHM = os.getenv("JWT_ALGORITHM", "HS256")
JWT_EXPIRATION_HOURS = int(os.getenv("JWT_EXPIRATION_HOURS", "24"))
JWT_ISSUER = os.getenv("JWT_ISSUER", "wrongmove")