Files
clawmates/docs/clerk.md
T

117 lines
4.7 KiB
Markdown

# Using Clerk for authentication
Clawmates's cloud target can use [Clerk](https://clerk.com) as its identity
provider. A Clerk instance is an OIDC issuer, so the backend verifies Clerk
session JWTs directly against the instance JWKS — no extra service, no
session table writes for SSO users, and Clerk's dashboard takes over user
management, MFA, and organization invitations.
**Air-gapped installs cannot use Clerk** (it is a hosted service). The
`local` auth mode remains the appliance default; `clerk` is a cloud-only
alternative to generic `oidc`.
## 1. Clerk dashboard
1. Create an application; note the Frontend API URL
(`https://<slug>.clerk.accounts.dev`).
2. Enable **Organizations** — Clerk's `org:admin` / `org:member` roles map
to Clawmates `Owner` / `Member`.
3. Under **Sessions → Customize session token**, add the claims Clawmates
reads:
```json
{
"email": "{{user.primary_email_address}}",
"role": "{{org.role}}"
}
```
## 2. Backend configuration
```toml
[auth]
mode = "clerk"
issuer_url = "https://<slug>.clerk.accounts.dev"
```
Or via Helm: `--set auth.mode=clerk --set auth.issuerUrl=https://<slug>.clerk.accounts.dev`.
On boot the server pins the issuer, loads its JWKS (refreshing on key
rotation), and verifies every `Authorization: Bearer <session JWT>` with a
5-second clock-skew allowance. Users are JIT-provisioned on first sight,
keyed by the stable `sub` claim (`users.auth_subject`); an existing
local-auth user with the same email is linked rather than duplicated, and
the role tracks the Clerk org role on every request.
## 3. Frontend wiring (built in)
The frontend ships with the integration. Set three runtime env vars on
the frontend container — the SAME image serves local and Clerk
deployments, nothing is baked at build time:
```bash
AUTH_MODE=clerk
CLERK_PUBLISHABLE_KEY=pk_live_…
CLERK_SECRET_KEY=sk_live_…
```
What flips on: the root layout wraps the tree in `<ClerkProvider>`
(lazily imported — local mode never loads the SDK), `/login` renders
Clerk's `<SignIn />`, the middleware runs `clerkMiddleware()`, and every
server-side API call resolves its bearer through Clerk's `getToken()`
instead of the local session cookie (`src/lib/auth/bearer.ts` is the
single dispatch point). Clerk session JWTs live ~60s and refresh
transparently; the backend verifies each one against the instance JWKS.
## What is verified in CI
`crates/cm-auth/tests/jwt_auth.rs` and `crates/cm-api/tests/clerk_api.rs`
exercise the full verification path with real RSA keys against a live
local issuer publishing real discovery + JWKS documents: JIT provisioning
and role mapping, duplicate-subject suppression, expired tokens, tokens
signed by the wrong key, foreign issuers, and the end-to-end
`Authorization: Bearer` round trip into a protected endpoint.
## Deployment smoke (real instance)
`crates/cm-auth/tests/live_clerk.rs` validates the production path against
a **live Clerk instance**: it pulls real discovery + JWKS, uses Clerk's
Backend API to create a user, open a session, and mint a real session JWT,
then runs that token through `AuthService::authenticate` — proving
verification, JIT provisioning, duplicate-subject suppression, and
tamper rejection against Clerk's actual keys. It cleans up the user
afterward. Run it with your instance keys (key-gated, never in the default
suite):
```bash
CM_LIVE_CLERK=1 \
CLERK_SECRET_KEY=sk_test_… \
CLERK_PUBLISHABLE_KEY=pk_test_… \
cargo test -p cm-auth --test live_clerk
```
The frontend half is smoked by building with `AUTH_MODE=clerk` and the
same keys and confirming `/login` serves Clerk's `<SignIn />` wired to the
instance — `npx next start` then `curl /login` shows the instance domain
and `data-clerk` attributes.
## Social OAuth (Google / GitHub / Apple)
The redesigned `/login` renders Clerk's `<SignIn>` themed to the dark design
system (`src/app/login/page.tsx` → `clerkAppearance`). The backend already
validates any Clerk-issued JWT (generic OIDC), so **enabling social login is
purely a Clerk-dashboard step — no code change**:
1. **Clerk Dashboard → User & Authentication → Social Connections.**
2. Toggle on **Google** and **GitHub** (Clerk's shared dev credentials work
immediately for testing; add your own OAuth client IDs/secrets for prod).
3. **Apple** (uses the provided `AuthKey_*.p8`): enable Apple, then supply
- **Services ID** (your Apple "Sign in with Apple" identifier),
- **Apple Team ID**,
- **Key ID** + the **`.p8` private key** contents,
and add Clerk's callback URL to the Apple service's Return URLs.
Once enabled, the "Continue with …" buttons appear automatically inside the
themed `<SignIn>` — the frontend needs no redeploy. Local dev (`AUTH_MODE=local`)
keeps the email/password form and shows no social buttons.