Skip to content

Commit 17c41f7

Browse files
feat: OpenID Connect (OIDC) SSO + per-bucket access management (#21)
* feat: OpenID Connect SSO + per-bucket access management (#9) Generic OIDC client for any compliant provider (Keycloak, Authentik, Okta, Auth0, Entra ID, Google, Dex). Auto-discovers endpoints, fully validates the ID token (JWKS signature, iss/aud/exp/azp, nonce), and protects the flow with state + nonce + PKCE (S256); works as a confidential or public client. Per issue #9, only the username is synced — new users are viewers with no access until an admin grants it. - Optional group->role mapping (OIDC_ADMIN_GROUP / OIDC_GROUPS_CLAIM), verified-email and email-domain restrictions, and RP-initiated single logout — all opt-in, off by default. - Per-bucket access UI: auth-source badges (Local / SSO / LDAP / OAuth), a bucket-grant count, and a Manage-access editor (Read / Write / No Access, with search and bulk grant/revoke). - Security: federated logins (OIDC/OAuth/LDAP) go through one hardened sync path. An auth_source is tracked per user (with a backfill migration), blocking account takeover — a federated login can no longer sign in as a username already owned by a different source (notably the local admin). - Docs: .env.example, Helm values/deployment/secrets, README, CHANGELOG, docs/SSO.md, and website SSO / env-vars / user-management / changelog pages. Validated: 62 backend tests (incl. takeover, azp, group-match, nonce); live browser e2e across Keycloak, Dex, and Authentik plus the full user-management UI (18 Playwright checks); Google validated against real discovery + JWKS. * chore: release 3.6.0 Bump app version to 3.6.0 (Helm chart 1.4.0) and finalize the OIDC SSO + per-bucket access management changelog entry.
1 parent 557dc72 commit 17c41f7

21 files changed

Lines changed: 1428 additions & 93 deletions

File tree

.env.example

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,37 @@ DB_DIR=/data # Directory for SQLite databases
2828
# LDAP_ADMIN_GROUP=CN=Admins,OU=Groups,DC=example,DC=com
2929
# LDAP_DEFAULT_ROLE=viewer
3030

31-
# OAuth / OIDC (optional)
31+
# OAuth — built-in Google / GitHub (optional)
3232
# OAUTH_GOOGLE_CLIENT_ID=
3333
# OAUTH_GOOGLE_CLIENT_SECRET=
3434
# OAUTH_GITHUB_CLIENT_ID=
3535
# OAUTH_GITHUB_CLIENT_SECRET=
3636
# OAUTH_DEFAULT_ROLE=viewer
3737
# OAUTH_ALLOWED_DOMAINS=example.com
3838

39+
# OIDC — generic OpenID Connect SSO (Keycloak, Okta, Auth0, Entra ID, Authentik, …)
40+
# Enabled when OIDC_ISSUER and OIDC_CLIENT_ID are both set. Endpoints are
41+
# auto-discovered from <issuer>/.well-known/openid-configuration; the ID token is
42+
# validated (JWKS signature + iss/aud/exp) before any claim is trusted.
43+
# Only the username is synced — a new user lands as OIDC_DEFAULT_ROLE (viewer)
44+
# with no bucket access, and an admin assigns permissions.
45+
# Register this redirect URI with your provider: https://<your-host>/api/auth/oidc/callback
46+
# OIDC_ISSUER=https://login.example.com/realms/main
47+
# OIDC_CLIENT_ID=
48+
# OIDC_CLIENT_SECRET=
49+
# OIDC_PROVIDER_NAME=SSO # label on the login button
50+
# OIDC_USERNAME_CLAIM=preferred_username # claim used as the local username
51+
# OIDC_SCOPES=openid profile email
52+
# OIDC_DEFAULT_ROLE=viewer
53+
# OIDC_ALLOWED_DOMAINS=example.com # optional email-domain allowlist
54+
# Optional group→role mapping (off by default = username-only; admins assign access).
55+
# When OIDC_ADMIN_GROUP is set, membership maps to admin and re-syncs each login.
56+
# OIDC_ADMIN_GROUP=sairo-admins # group whose members become admins
57+
# OIDC_GROUPS_CLAIM=groups # claim containing the user's groups
58+
# Optional hardening:
59+
# OIDC_REQUIRE_VERIFIED_EMAIL=false # reject logins whose email isn't verified
60+
# OIDC_RP_LOGOUT=false # also end the IdP session on logout (single logout)
61+
3962
# White-labeling (optional)
4063
# APP_NAME=Sairo
4164
# PRIMARY_COLOR=#3b82f6

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ node_modules/
55
frontend/dist/
66
website/dist/
77
website/.astro/
8+
backend/static/
89

910
# Python
1011
__pycache__/

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,26 @@
22

33
All notable changes to Sairo are documented here. This project uses [Semantic Versioning](https://semver.org/).
44

5+
## [3.6.0] - 2026-06-29
6+
7+
Generic OpenID Connect SSO + a real per-bucket access UI (issue #9).
8+
9+
### Added
10+
11+
- **OpenID Connect (OIDC) SSO** — a standards-compliant client for any provider (Keycloak, Authentik, Okta, Auth0, Entra ID, Google, Dex, …). Endpoints are auto-discovered from `<issuer>/.well-known/openid-configuration`; every ID token is fully validated (JWKS signature, `iss`/`aud`/`exp`/`azp`, nonce) and the flow is protected with **state + nonce + PKCE (S256)**. Per issue #9, **only the username is synced** — new users are viewers with no access until an admin grants it. Configured entirely via `OIDC_*` env vars; works with a confidential **or** public (secretless) client. Tested end-to-end against Keycloak, Authentik, and Dex. See [docs/SSO.md](docs/SSO.md).
12+
- **Optional OIDC group→role mapping** (`OIDC_ADMIN_GROUP` / `OIDC_GROUPS_CLAIM`) — off by default; when enabled, admin-group membership maps to the admin role and re-syncs each login. Matching is exact / path-segment / DN — never a loose substring.
13+
- **Optional hardening toggles**`OIDC_REQUIRE_VERIFIED_EMAIL`, `OIDC_ALLOWED_DOMAINS`, and RP-initiated single logout (`OIDC_RP_LOGOUT`).
14+
- **Per-bucket access management UI** — Users now shows an auth-source badge (Local / SSO / LDAP / OAuth) and a bucket-grant count; "Manage access" opens a Read / Write / No-Access editor with search and bulk grant/revoke. (The backend permission API already existed; this makes it usable.)
15+
- Friendly login-page error messages for SSO failures.
16+
17+
### Changed
18+
19+
- **`auth_source` is now tracked per user** (with a backfill migration). All federated logins — OIDC, OAuth, and LDAP — go through one hardened sync path.
20+
21+
### Security
22+
23+
- Closed an **account-takeover** vector: a federated (OIDC/OAuth/LDAP) login can no longer sign in as a username already owned by a different auth source — notably the local admin.
24+
525
## [3.5.0] - 2026-06-27
626

727
Anonymous telemetry schema v2 — richer, still privacy-first.

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Works with **AWS S3**, **MinIO**, **Ceph**, **Wasabi**, **Cloudflare R2**, **Bac
4242
- **User Management** — Role-based access control (admin / viewer) with per-bucket permissions
4343
- **S3-Key Auth** — Optional `AUTH_MODE=s3`: users log in with their own S3 keys and see only the buckets their provider IAM allows — access is delegated entirely to the provider
4444
- **Two-Factor Auth** — TOTP-based 2FA with QR setup and recovery codes
45-
- **OAuth & LDAP** — Google, GitHub OAuth and LDAP authentication
45+
- **SSO — OAuth, OIDC & LDAP** — Google/GitHub OAuth, generic OpenID Connect (Keycloak, Okta, Auth0, Entra ID, Google, Authentik, Dex, …) with auto-discovery + full ID-token validation (PKCE, nonce, JWKS), and LDAP. OIDC syncs the username only; admins assign per-bucket access. **Setup guide: [docs/SSO.md](docs/SSO.md)**
4646
- **AI-Powered Analysis (MCP)** — Connect Claude, Cursor, or any MCP client to ask natural language questions about your storage. 26 tools for analytics, cost optimization, pipeline health, and more
4747
- **Petabyte-Scale Performance** — Proven on a live 269 TB / 15.5M-object deployment. Constant-time folder listing at any scale via pre-computed prefix hierarchies, 64MB SQLite page cache, 256MB memory-mapped I/O, async FTS rebuilds
4848
- **Anonymous Telemetry (opt-out)** — A privacy-first heartbeat reports aggregate counts, instance health, and activation timestamps only (never names, keys, paths, or content). Disable with `TELEMETRY=false`
@@ -122,7 +122,7 @@ Validated read-only against a live production deployment (Leaseweb S3-compatible
122122
| Upload | Direct browser→S3 multipart, up to 5 TB/object, **flat server memory** |
123123
| Crawl throughput | 16 parallel prefix workers, 10K batch inserts, adaptive delta crawl |
124124

125-
**Scaling:** Folder navigation is a constant-time index lookup, so it stays instant at any dataset size. See [Benchmarks](https://docs.sairo.dev/reference/benchmarks/) for the full methodology and server-compute microbenchmarks.
125+
**Scaling:** Folder navigation is a constant-time index lookup, so it stays instant at any dataset size. See [benchmark results](benchmark/results/LATEST-RESULTS.md) for the full methodology and server-compute microbenchmarks.
126126

127127
## Environment Variables
128128

@@ -145,7 +145,7 @@ Validated read-only against a live production deployment (Leaseweb S3-compatible
145145
| `TELEMETRY` | `true` | Anonymous usage heartbeat (aggregate counts + health only). Set `false` to disable |
146146
| `TELEMETRY_INTERVAL` | `3600` | Seconds between telemetry heartbeats |
147147

148-
> This is a summary. See the [full environment-variable reference](https://docs.sairo.dev/reference/environment-variables/) for delta-crawl tuning, uploads, LDAP/OAuth, branding, and rate limiting.
148+
> This is a summary; see **[.env.example](.env.example)** for the full annotated environment-variable reference (delta-crawl tuning, uploads, LDAP/OAuth, branding, rate limiting). For single sign-on (OIDC / OAuth / LDAP) setup with per-provider examples, see **[docs/SSO.md](docs/SSO.md)**.
149149
150150
## Tech Stack
151151

0 commit comments

Comments
 (0)