WebAuthn & Passkey Authentication#
Passkey/WebAuthn support in KlickerUZH is in assessment phase as of 2026-07-31 and has not yet been implemented. It is tracked under the Participant Privacy & Auth readiness map, which governs a planned redesign of participant identity, authentication, and personal-data retention. The viability assessment issue must be resolved before any implementation slice depends on passkeys .
The central open question is whether to adopt Auth.js's built-in WebAuthn provider (which locks to SimpleWebAuthn v9) or integrate SimpleWebAuthn directly at a current release .
Current Authentication Architecture#
The apps/auth application (Next.js 16, port 3010) is the sole authentication entry point for all of KlickerUZH . All auth requests are handled by a single dynamic route handler that selects between two NextAuth configurations at request time based on context.
Dual-context routing. The getAuthContext() helper (in apps/auth/src/lib/helpers.ts) determines whether a request belongs to the lecturer or participant context using a cascading priority:
- Explicit
?participant=truequery parameter callbackUrlhost matched against student or lecturer allowed-host lists- Ephemeral redirect cookies (
klicker_student_redirect_to/klicker_lecturer_redirect_to) - Default fallback:
lecturer
Lecturer config (getLecturerConfig()) uses EduID (OAuth/OIDC via Swiss national federation) plus a CredentialsProvider for delegated login (username + bcrypt password). It uses PrismaAdapter for user persistence and sets a MANAGER_COOKIE_NAME session cookie .
Participant config (getParticipantConfig()) uses EduID only, sets a PARTICIPANT_COOKIE_NAME session cookie, and links authenticated identities to Participant records via createOrLinkParticipant(). When passkeys are added, a new provider would be wired in here or in a new context branch.
Both configs share JWT session strategy with custom encode/decode helpers and derive a shared cookie domain from NEXTAUTH_URL .
Allowed hosts are configured via environment variables :
| Variable | Purpose | Dev default |
|---|---|---|
AUTH_STUDENT_ALLOWED_HOSTS | Participant redirect targets | assessment.klicker.com, localhost:3001 |
AUTH_LECTURER_ALLOWED_HOSTS | Lecturer redirect targets | manage.klicker.com, localhost:3002 |
AUTH_PWA_HOSTS | PWA redirect targets | pwa.klicker.com, localhost:3000 |
SimpleWebAuthn Dependency Status#
SimpleWebAuthn is not installed in the monorepo β not directly, not transitively. It appears in pnpm-lock.yaml lines 2893β2899 only as an optional unmet peer of @auth/core@0.41.2, declaring @simplewebauthn/browser ^9.0.1 and @simplewebauthn/server ^9.0.2 . The packages are never resolved to disk.
The apps/auth/package.json lists @auth/prisma-adapter 2.11.2 and next-auth 4.24.14 as direct dependencies, but neither @simplewebauthn/browser nor @simplewebauthn/server appear anywhere.
Implementation fork. Two paths exist once the viability assessment concludes :
- Auth.js WebAuthn provider β uses Auth.js's built-in passkey support, accepts the v9 pin already declared by
@auth/core. Tightly integrated but version-constrained. - Standalone SimpleWebAuthn β install
@simplewebauthn/browserand@simplewebauthn/serverdirectly at any current version. Requires manual API routes and credential storage, but decoupled from Auth.js's pin.
Compatibility targets for both paths: Next.js 16 and Node 24 (pinned by Volta in package.json ).
Local HTTPS Setup (WebAuthn Prerequisite)#
WebAuthn requires a secure context (HTTPS or localhost). The development environment achieves this through a Traefik reverse proxy with mkcert-signed TLS certificates.
Step 1 β Generate certificates. Run util/_create_ssl_certificates.sh (optimized for macOS; requires mkcert + nss):
brew install mkcert nss
./util/_create_ssl_certificates.sh
The script installs a local CA, then issues a wildcard cert for klicker.com and *.klicker.com, writing klicker.com+1.pem and klicker.com+1-key.pem into util/traefik/ssl/ .
Step 2 β Start Traefik. The docker-compose.yml mounts util/traefik/ssl/ read-only into the Traefik container and configures HTTPS on port 443 . The TLS certificate block in util/traefik/rules_docker.yaml points Traefik to those cert files.
Step 3 β /etc/hosts. Add entries mapping *.klicker.com to 127.0.0.1 so browsers resolve the custom domain. The auth service is reachable at https://auth.klicker.com (routed to host.docker.internal:3010) .
Development origins (from .env.development ):
| App | URL |
|---|---|
| Auth | https://auth.klicker.com |
| Manage (lecturer) | https://manage.klicker.com |
| PWA (participant) | https://pwa.klicker.com |
| Assessment | https://assessment.klicker.com |
Open question. The devcontainer uses workspace-namespaced *.klicker.<workspace>.localhost domains with mkcert certificates. Whether those satisfy WebAuthn's relying-party ID rules (which tie credentials to the effective domain) is an explicit open item in the viability assessment .