Add self-service API tokens (PATs) with UI + Bearer auth (#35)

Personal access tokens so scripts/CI can call the management API without a
browser session. Each logged-in user mints their own token; it authenticates as
the creator (carries their LDAP group permissions, re-resolved live), so the
existing permission.byGroup checks apply unchanged.

- models/api_token.js: new ApiToken model (sso_<id>_<secret> format; id is the
  lookup key, secret bcrypt-hashed + isPrivate, shown once). add()/rotate()/
  authenticate(); optional expires_at; best-effort last_used_on. No _ttl
  (persists; lifetime via expires_at).
- routes/api_token.js: self-service CRUD (list/get/update/delete/rotate),
  owner-scoped (created_by === req.user.uid, 403 otherwise).
- middleware/auth.js + models/auth.js: accept `Authorization: Bearer sso_...`
  (precedence over the auth-token session header); checkApiToken collapses
  every failure to one generic 401 (no existence/secret/expiry leak).
- views/api_tokens.ejs + routes/index.js (GET /api-tokens): self-service page
  (forceLogin, no group gate) — create (token shown once), edit, rotate, revoke.
- views/top.ejs: "API Tokens" nav entry visible to all logged-in users.
- public/js/app.js: app.apiToken client module.
- DEPLOYMENT.md + docs/deployment.md: API tokens section.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-12 17:12:35 -04:00
committed by GitHub
parent 6920a9f9f0
commit b91ef2792d
12 changed files with 546 additions and 0 deletions
+15
View File
@@ -54,6 +54,21 @@ Then `docker compose up -d --build`.
- OIDC discovery: `http://localhost:3001/.well-known/openid-configuration`
- LDAPS (legacy apps / direct binds): `ldaps://<host>:636`
### API tokens (personal access tokens)
Any logged-in user can mint a long-lived bearer token to call the management
API from scripts/CI without a browser session. Self-service; authenticates
**as the creator** (carries their LDAP group permissions, re-resolved live).
Create one under **API Tokens** in the UI (shown once), then:
```bash
curl -H "Authorization: Bearer sso_<id>_<secret>" https://sso.example.com/api/user
```
Rotate/revoke from the same page (immediate effect). Optional expiry at
creation. Tokens persist in Redis (AOF) and survive rebuilds.
### Logs
The all-in-one image runs the Node app and slapd (OpenLDAP) in one container,