diff --git a/docs/identity.md b/docs/identity.md new file mode 100644 index 0000000..7fa8843 --- /dev/null +++ b/docs/identity.md @@ -0,0 +1,151 @@ +# The Guest List 💋 + +Every app in The Super Boujee Business Box ✨ authenticates through the same door. One login, every room. You don't re-authenticate because you walked from Docs to Drive — that's not a feature, that's just manners. + +The identity layer is built on **Ory Kratos** (who you are) and **Ory Hydra** (what you're allowed to do). Lightweight Go binaries, no JVM, no XML — they fit k3s like they were born for it. We chose them over Keycloak because we wanted something that didn't need 2GB of RAM to tell you who's logged in. + +--- + +## The Auth Flow + +``` +You → any app (docs.sunbeam.pt, drive.sunbeam.pt, etc.) + → 302 redirect to auth.sunbeam.pt + → Hydra says "who are you?" + → Kratos login UI (password, TOTP, WebAuthn — your pick) + → authenticate + → Hydra issues OIDC token + → 302 back to the app + → app validates via mozilla-django-oidc + → session established ✨ +``` + +That's it. Every La Suite app uses the same `mozilla-django-oidc` library. Swapping from Keycloak (the French government default) to Hydra was transparent at the app level — same OIDC, different provider. + +--- + +## The Players + +### Ory Kratos — Identity Management + +Handles the "who are you" part: + +- **Registration** — create accounts (or provision them via `sunbeam auth identity create`) +- **Login** — password, TOTP, WebAuthn, lookup secrets +- **Profile** — name, email, metadata +- **Recovery** — password reset via email (through Postfix) +- **Session** — 720 hours (30 days), cookie-scoped to parent domain + +The cookie-scoping is key: setting the session cookie on the parent domain means all `*.sunbeam.pt` subdomains share the session. Log in once, you're everywhere. + +### Identity Schemas + +Three schemas for different types of users: + +| Schema | Purpose | +|--------|---------| +| `employee` | Core team (Sienna, Amber, Lonni) — full access, all apps | +| `default` | Standard users — for when the team grows | +| `external` | External collaborators — limited scope | + +### Ory Hydra — OAuth2 / OIDC Provider + +Handles the "what can you do" part: + +- Issues OAuth2 access tokens and OIDC ID tokens +- Manages the OIDC client registry +- Token lifetimes: access/ID tokens 1h, refresh tokens 720h, auth session 720h +- OpenID Connect discovery at `auth.sunbeam.pt/.well-known/openid-configuration` + +### Hydra Maester + +The secret sauce for client management. Watches `HydraOAuth2Client` CRDs in Kubernetes and automatically: + +1. Registers the client with Hydra +2. Creates a K8s Secret with `CLIENT_ID` and `CLIENT_SECRET` +3. Keeps them in sync if the CRD changes + +No manual client registration. Declare a CRD, get a working OIDC client. + +--- + +## The OIDC Client Registry + +Every app is a registered OIDC client. Hydra Maester manages all of them: + +| App | Secret Name | Redirect URI | Notes | +|-----|-------------|-------------|-------| +| Docs | oidc-docs | `docs.DOMAIN/api/v1.0/callback/` | | +| Drive | oidc-drive | `drive.DOMAIN/api/v1.0/callback/` | | +| Meet | oidc-meet | `meet.DOMAIN/api/v1.0/callback/` | | +| Messages | oidc-messages | `mail.DOMAIN/api/v1.0/callback/` | `offline_access` scope | +| People | oidc-people | `people.DOMAIN/api/v1.0/callback/` | | +| Find | oidc-find | `find.DOMAIN/oidc/callback/` | | +| Gitea | oidc-gitea | `src.DOMAIN/user/oauth2/Sunbeam/callback` | | +| Calendars | oidc-calendars | `cal.DOMAIN/api/v1.0/callback/` | | +| Projects | oidc-projects | `projects.DOMAIN/oidc-callback` | | +| Hive | oidc-hive | *(none)* | `client_credentials` grant | +| Tuwunel | oidc-tuwunel | *(matrix namespace)* | Matrix SSO | +| Grafana | grafana-oidc | *(monitoring namespace)* | auto-assign Admin role | + +Messages gets `offline_access` to maintain sessions across browser restarts. Hive uses `client_credentials` because it's a service, not a human. Grafana auto-assigns Admin because if you're authenticated, you're on the team. + +--- + +## Self-Service Flows + +Users can manage their own security: + +| Method | What it is | +|--------|-----------| +| **Password** | Classic email + password | +| **TOTP** | Time-based one-time passwords (Google Authenticator, etc.) | +| **WebAuthn** | Hardware security keys (YubiKey, etc.) | +| **Lookup Secrets** | Backup/recovery codes | + +Recovery emails go through Postfix (same SMTP used by Messages) at `postfix.lasuite.svc:25`. + +--- + +## Vault Integration + +Secrets don't live in Git. They live in OpenBao (Vault) and sync to Kubernetes via Vault Secrets Operator. + +### OIDC Client Secrets +Hydra Maester creates K8s Secrets from HydraOAuth2Client CRDs. These contain `CLIENT_ID` and `CLIENT_SECRET` that apps consume. + +### Dynamic Database Credentials +Every app's database password rotates every 5 minutes via `VaultDynamicSecret`. The Vault Secrets Operator: +1. Requests new credentials from OpenBao's database engine +2. Creates/updates the K8s Secret +3. Triggers a rollout restart of the affected Deployment + +### Static Secrets +Django secret keys, DKIM keys, API tokens — synced from OpenBao KV v2 with 30-second refresh via `VaultStaticSecret`. + +--- + +## Admin Access + +For when you need to manage identities directly: + +```bash +# Via sunbeam CLI +sunbeam auth identity list +sunbeam auth identity create amber@sunbeam.pt --name "Amber" +sunbeam auth identity get + +# Via Kratos Admin API (auth-gated through proxy) +# Accessible at id.sunbeam.pt (requires Hydra /userinfo auth) + +# Via Hydra Admin API (auth-gated through proxy) +# Accessible at hydra.sunbeam.pt (requires Hydra /userinfo auth) +``` + +Sol☀️ also has Kratos admin access for identity operations within Matrix chat. + +--- + +## The Login UI + +Sunbeam-branded login, consent, and recovery pages served by `kratos-admin-ui`. This is what you see at `auth.sunbeam.pt` — the velvet rope. Custom logo, custom colors, same energy as everything else in the box. ✨