Passkey login
Logos supports in-page passkey (WebAuthn) login — no redirect to a hosted login page. The "Sign in with a passkey" button on the login screen runs the WebAuthn ceremony directly in the app and signs the user in.
How it works
The implementation lives in logos-ui/src/app/core/auth/ (Angular):
passkey.ts runs the WebAuthn ceremony and the silent token exchange,
keycloak.ts manages the keycloak-js singleton (initialized with
check-sso + PKCE), and services/auth.service.ts completes the login.
The flow:
GET {issuer}/passkey/{clientId}/challenge— fetch a WebAuthn challenge.navigator.credentials.get(...)— the browser prompts for a discoverable (usernameless) passkey and signs the challenge (userVerification: "required").POST {issuer}/passkey/{clientId}/authenticate— the assertion is verified by the Keycloak passkey provider, which establishes a Keycloak SSO session.- Silent token retrieval — a hidden
prompt=noneiframe completes authorization-code + PKCE against the fresh session (silent-check-sso.html), so tokens are obtained without any visible login page. - Install into keycloak-js —
AuthService.completeLogin()pushes the silently obtained tokens into the app's keycloak-js instance, so the auth interceptor (kc.token) and itsupdateToken()refresh keep working with no redirect.
Registration (registerPasskey in passkey.ts) mirrors this with
challenge → navigator.credentials.create(...) → POST .../save.
Keycloak prerequisites
These live on the Keycloak side, not in this repo:
-
The custom passkey provider must be enabled for the
logosclient, exposing{issuer}/passkey/{logos}/{health|challenge|authenticate|save}. -
The
logosclient must allow the silent flow: the UI origin in Web Origins and{origin}/silent-check-sso.htmlin Valid Redirect URIs. -
The WebAuthn passwordless policy (resident key + user verification) must be configured, and the passkey rpId must match what the credential was registered with. On the shared TUM Keycloak passkeys are scoped to the parent domain, so the rpId must be
aet.cit.tum.de(NOTlogos.aet.cit.tum.de) — a page onlogos.aet.cit.tum.deis allowed to use the parent as rpId, and the credential is then shared across*.aet.cit.tum.deapps.The rpId is configured server-side via
KEYCLOAK_PASSKEY_RP_ID(served to the UI through/api/info); the production compose default isaet.cit.tum.de. When blank (dev),passkey.tsfalls back to the current hostname (e.g.localhost).
Notes / status
- WebAuthn needs a secure context.
localhostcounts as secure for dev; all other hosts need HTTPS (prod is behind Traefik TLS). - The silent
prompt=noneiframe depends on the Keycloak session cookie being readable from the iframe; verify against the real Keycloak (third-party-cookie behaviour varies by browser).