SSO: OAuth, OIDC & LDAP
Sairo supports external authentication via generic OpenID Connect (OIDC), built-in Google/GitHub OAuth, and LDAP. Users authenticated through external providers are auto-provisioned on first login.
For most providers, OIDC is the recommended path — it works with any compliant identity provider and validates tokens properly.
OIDC (OpenID Connect)
Section titled “OIDC (OpenID Connect)”A standards-compliant OIDC client that works with any provider — Keycloak, Authentik, Okta, Auth0, Microsoft Entra ID, Google, Dex, Zitadel, and more. Endpoints are auto-discovered from <issuer>/.well-known/openid-configuration, and every ID token is fully validated (JWKS signature, iss / aud / exp / azp, nonce). The flow is protected with state + nonce + PKCE (S256), so a confidential or public (secretless) client both work.
The model: identity in, access assigned
Section titled “The model: identity in, access assigned”OIDC syncs the username only. A first-time user is created as a viewer with no bucket access; an admin then grants per-bucket Read/Write under Users → Manage access (see User Management). Optional group→role mapping is available but off by default.
- Register a new client/application on your identity provider.
- Add the redirect URI:
https://your-domain/api/auth/oidc/callback(http://localhost:8000/...works for local testing). - Set the environment variables:
environment: OIDC_ISSUER: "https://login.example.com/realms/main" OIDC_CLIENT_ID: "sairo" OIDC_CLIENT_SECRET: "your-client-secret" # omit for a public client OIDC_PROVIDER_NAME: "Company SSO" # label on the login buttonYou can point Sairo at an identity provider you already run — just register a new client for Sairo on it; your users, passwords, MFA, and SSO sessions are reused.
Provider examples
Section titled “Provider examples”Keycloak
OIDC_ISSUER: "https://keycloak.example.com/realms/main"OIDC_CLIENT_ID: "sairo"OIDC_CLIENT_SECRET: "..."Okta
OIDC_ISSUER: "https://your-org.okta.com"OIDC_CLIENT_ID: "..."OIDC_CLIENT_SECRET: "..."Auth0 (no preferred_username; group claims are namespaced)
OIDC_ISSUER: "https://your-tenant.us.auth0.com/"OIDC_CLIENT_ID: "..."OIDC_CLIENT_SECRET: "..."OIDC_USERNAME_CLAIM: "nickname"Microsoft Entra ID (groups are object-ID GUIDs)
OIDC_ISSUER: "https://login.microsoftonline.com/<tenant-id>/v2.0"OIDC_CLIENT_ID: "..."OIDC_CLIENT_SECRET: "..."Google (no username concept; use email)
OIDC_ISSUER: "https://accounts.google.com"OIDC_CLIENT_ID: "...apps.googleusercontent.com"OIDC_CLIENT_SECRET: "..."OIDC_USERNAME_CLAIM: "email"OIDC_ALLOWED_DOMAINS: "yourcompany.com"Optional: group → role mapping
Section titled “Optional: group → role mapping”Off by default (username-only). Set OIDC_ADMIN_GROUP (and OIDC_GROUPS_CLAIM, default groups) to map admin-group membership to the admin role — re-evaluated on every login. Matching is on the whole group value or a delimited component (path segment / DN), never a loose substring, so sairo-admins will not match sairo-admins-readonly. Plain Google OIDC does not emit groups.
Optional: single logout
Section titled “Optional: single logout”Set OIDC_RP_LOGOUT: "true" to also end the session at your identity provider when a user logs out of Sairo.
Troubleshooting
Section titled “Troubleshooting”Sign-in failures land back on the login page with a friendly message and an ?error= code: account_conflict (username already exists with a different sign-in method), oidc_invalid_token, oidc_state_mismatch / oidc_nonce_mismatch (session expired / cookies blocked), oidc_no_username (set OIDC_USERNAME_CLAIM), email_not_verified, domain_not_allowed.
Google OAuth
Section titled “Google OAuth”The built-in Google integration below is a simpler, Google-specific OAuth flow. For new deployments, prefer the generic OIDC path above (it adds full ID-token validation and works with any provider).
Google OAuth uses the OpenID Connect authorization code flow.
- Go to the Google Cloud Console
- Create an OAuth 2.0 Client ID (Web application type)
- Add the authorized redirect URI:
https://your-domain/api/auth/oauth/google/callback - Copy the Client ID and Client Secret
Configuration
Section titled “Configuration”environment: OAUTH_GOOGLE_CLIENT_ID: "your-client-id.apps.googleusercontent.com" OAUTH_GOOGLE_CLIENT_SECRET: "your-client-secret" OAUTH_ALLOWED_DOMAINS: "yourcompany.com,subsidiary.com"OAUTH_ALLOWED_DOMAINS restricts which email domains can authenticate. Comma-separated, no spaces. If not set, any Google account can log in.
- User clicks Sign in with Google on the login page
- User authenticates with Google and grants consent
- Google redirects to
/api/auth/oauth/google/callbackwith an authorization code - Sairo exchanges the code for an ID token, extracts the email and name
- If the user does not exist, a new account is created with the configured default role
- A JWT session cookie is issued
GitHub OAuth
Section titled “GitHub OAuth”- Go to GitHub Developer Settings
- Create a new OAuth App
- Set the Authorization callback URL to:
https://your-domain/api/auth/oauth/github/callback - Copy the Client ID and Client Secret
Configuration
Section titled “Configuration”environment: OAUTH_GITHUB_CLIENT_ID: "your-client-id" OAUTH_GITHUB_CLIENT_SECRET: "your-client-secret"- User clicks Sign in with GitHub on the login page
- User authenticates with GitHub
- GitHub redirects to
/api/auth/oauth/github/callbackwith an authorization code - Sairo exchanges the code for an access token, fetches the user’s profile
- If the user does not exist, a new account is created with the configured default role
- A JWT session cookie is issued
LDAP authentication binds against your directory server to verify credentials.
Configuration
Section titled “Configuration”environment: LDAP_ENABLED: "true" LDAP_SERVER: "ldap://ldap.yourcompany.com:389" LDAP_BASE_DN: "ou=people,dc=yourcompany,dc=com" LDAP_USER_FILTER: "(sAMAccountName={username})" # default; use "(uid={username})" for OpenLDAP LDAP_BIND_DN: "cn=readonly,dc=yourcompany,dc=com" LDAP_BIND_PASSWORD: "bind-password" LDAP_ADMIN_GROUP: "cn=sairo-admins,ou=groups,dc=yourcompany,dc=com"| Variable | Description |
|---|---|
LDAP_ENABLED | Set to true to enable LDAP login |
LDAP_SERVER | LDAP server URI (supports ldap:// and ldaps://) |
LDAP_BASE_DN | Base DN for user searches |
LDAP_USER_FILTER | Search filter; {username} is replaced with the login username |
LDAP_BIND_DN | DN of the service account used to search for users |
LDAP_BIND_PASSWORD | Password for the bind DN |
LDAP_ADMIN_GROUP | LDAP group DN whose members are assigned the admin role |
- User enters their LDAP username and password on the login page
- Sairo binds with the service account to search for the user DN
- Sairo attempts to bind with the found user DN and the provided password
- If the bind succeeds and the user is a member of
LDAP_ADMIN_GROUP, they get the admin role; otherwise they get the default role - If the user does not exist in Sairo, a new account is created
- A JWT session cookie is issued
Default Role for New Users
Section titled “Default Role for New Users”OAuth and LDAP have separate default role variables:
environment: OAUTH_DEFAULT_ROLE: "viewer" # for Google/GitHub OAuth users LDAP_DEFAULT_ROLE: "viewer" # for LDAP usersBoth default to viewer. New users get no bucket permissions until an admin assigns them.
2FA After External Auth
Section titled “2FA After External Auth”Two-factor authentication still applies after external authentication. If a user who authenticated via Google, GitHub, or LDAP has 2FA enabled on their Sairo account, they are prompted for a TOTP code after the external auth completes.