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:
parent
95c0ddc4c6
commit
a8b7eace48
26 changed files with 1229 additions and 129 deletions
|
|
@ -1,13 +1,14 @@
|
|||
import type { User } from 'oidc-client-ts';
|
||||
import type { AuthUser } from '@/auth/types';
|
||||
import { Button } from './ui/button';
|
||||
import { Separator } from './ui/separator';
|
||||
import { LogOut, Home, Filter } from 'lucide-react';
|
||||
import { logout } from '@/auth/authService';
|
||||
import { clearPasskeyUser } from '@/auth/passkeyService';
|
||||
import { HealthIndicator } from './HealthIndicator';
|
||||
import { TaskIndicator } from './TaskIndicator';
|
||||
|
||||
interface HeaderProps {
|
||||
user: User;
|
||||
user: AuthUser;
|
||||
activeFilterCount?: number;
|
||||
taskID?: string | null;
|
||||
isLoading?: boolean;
|
||||
|
|
@ -24,6 +25,15 @@ export function Header({
|
|||
showFilterToggle = false,
|
||||
onTaskCancelled,
|
||||
}: HeaderProps) {
|
||||
const handleLogout = async () => {
|
||||
if (user.provider === 'passkey') {
|
||||
clearPasskeyUser();
|
||||
window.location.reload();
|
||||
} else {
|
||||
await logout();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<header className="flex h-14 shrink-0 items-center gap-3 border-b bg-background px-4">
|
||||
{/* Logo / Brand */}
|
||||
|
|
@ -63,12 +73,12 @@ export function Header({
|
|||
{/* User Menu */}
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-sm text-muted-foreground hidden md:inline">
|
||||
{user.profile.email}
|
||||
{user.email}
|
||||
</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={logout}
|
||||
onClick={handleLogout}
|
||||
className="gap-2"
|
||||
>
|
||||
<LogOut className="h-4 w-4" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue