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,6 +1,6 @@
// Generic API client with authentication
import type { User } from 'oidc-client-ts';
import type { AuthUser } from '@/auth/types';
import { ApiError } from '@/types';
export interface RequestOptions {
@ -31,12 +31,11 @@ function buildQueryString(params: Record<string, string | number | boolean | Dat
* Generic authenticated API request
*/
export async function apiRequest<T>(
user: User,
user: AuthUser,
endpoint: string,
options: RequestOptions = {}
): Promise<T> {
const { method = 'GET', params } = options;
const accessToken = user.access_token;
let url = endpoint;
if (params) {
@ -49,7 +48,7 @@ export async function apiRequest<T>(
const response = await fetch(url, {
method,
headers: {
Authorization: `Bearer ${accessToken}`,
Authorization: `Bearer ${user.accessToken}`,
'Content-Type': 'application/json',
},
});