Guest identity
Keycloak holds every guest account: passwords, registration, sign-in sessions, and any social or second-factor login FINNS turns on. Every channel that signs guests in (the app, the booking website, future channels) is an OpenID Connect client of the same realm, which is what makes “one FINNS account” true.
- Version
- Keycloak 26.8
- Realm
- finns
- Hosting
- DigitalOcean droplet keycloak-1Docker ComposeCaddy (TLS)Its database lives in the same managed Postgres cluster as core. Operators reach the droplet over Tailscale.
- Public URL
- test-auth.finnsbeachclub.com/realms/finns
- FINNS extension
- finns-core event listener (Java jar)
How it connects
Section titled “How it connects”flowchart LR
classDef ours fill:#13686C,stroke:#0B4A4D,color:#ffffff
classDef ext fill:#F3ECDF,stroke:#B9A88B,color:#12191B
app["Guest app<br/>client trident-app"]:::ours
book["Booking website<br/>client poc-booking"]:::ext
kc["<b>Keycloak</b><br/>realm finns"]:::ext
core["Core"]:::ours
app -- "OIDC code + PKCE" --> kc
book -- "OIDC code + PKCE,<br/>silent SSO, fresh login" --> kc
kc -- "back-channel logout" --> book
kc -- "finns-core listener:<br/>user changed {userId}" --> core
core -- "client finns-core-sync:<br/>read users (view-users)" --> kc
core -- "validate JWTs (JWKS)" --> kc
Clients
Section titled “Clients”| Client | Type | Notes |
|---|---|---|
trident-app |
Public, PKCE | Core accepts only access tokens with azp=trident-app. Mobile uses offline_access. |
poc-booking |
Public, PKCE | Back-channel logout URL set, “session required” on. A real booking vendor gets its own client. |
finns-core-sync |
Confidential, service account only | One role: realm-management → view-users. Its secret lives only in core. |
Sessions
Section titled “Sessions”| Setting | Value | Effect |
|---|---|---|
| SSO session idle / max | 30 min / 10 h | How long a browser stays signed in (web app, booking site) |
| Offline session idle | ~30 days | How long the mobile app stays signed in without use |
Keeping core in sync
Section titled “Keeping core in sync”The finns-core extension listens for user events (registration, login, profile and email changes, account deletion, identity-provider links) and admin changes to users. After Keycloak commits, it sends core only the user’s id; core fetches the user itself with finns-core-sync. Core also reconciles every 10 minutes to catch anything missed. Setup is scripted (setup-realm.py) and described in core/keycloak-extension/manual-keycloak.md.
Things to decide for production
Section titled “Things to decide for production”- High availability: today one node on one droplet. Keycloak clusters well (Infinispan), but needs its own database sizing and a load balancer.
- Email delivery: verification and password-reset emails need an SMTP relay.
- Social login and passwordless: Keycloak supports Google, Apple and WebAuthn natively; the brief’s activation goals argue for the lowest-friction sign-up.
- Consent and data rights (PDP Law, GDPR): terms acceptance and account deletion flows belong here.
Internal and confidential. Share partner sections only as partner builds (pnpm build:partner).

