Features
MarkdownSSO / OIDC
Externa supports optional OIDC/SSO alongside Fortify password and passkey login. Configure a single standards-compliant identity provider (Keycloak, Azure AD OIDC, Google Workspace OIDC, …) in Project settings.
Related
Project settings form: Project settings. Local auth stack: Passkeys · Account settings. Env / APP_URL: Environment variables. Pre-ship gates: Security checklist.
What ships in v1
| Concern | Behavior |
|---|---|
| Protocol | OpenID Connect authorization code via issuer discovery (/.well-known/openid-configuration) |
| Package | laravel/socialite + in-app generic OIDC driver |
| Login UX | Optional secondary button on /login when OIDC is fully configured |
| Linking | Match IdP sub first; else link by verified email to an existing active user and store oidc_sub |
| JIT | Off by default. When on, create users from verified email; honor allowed_domains + default_user_role |
| Secrets | Client secret stored encrypted; edit payloads expose oidc_client_secret_configured only |
| Local auth | Password + passkey paths stay available |
Out of scope (v1)
SAML, SCIM, multi-IdP, claim→role matrices, IdP-initiated SSO, treating OIDC as an MFA substitute, forcing SSO-only (disabling password/passkey).
Setup
- In the IdP, create a confidential OIDC client.
- Set redirect URI to
{APP_URL}/auth/oidc/callback(same class of footgun as passkeys /APP_URL). - In Externa Settings → Project → SSO / OIDC:
- Enable OIDC
- Issuer URL (base; discovery is appended)
- Client ID + client secret
- Optional button label
- Optional JIT provisioning
- Ensure target users already exist (JIT off) or turn JIT on and configure allowed domains / default role.
| Route | Name | Notes |
|---|---|---|
GET /auth/oidc/redirect | auth.oidc.redirect | Starts authorize redirect (404 when disabled) |
GET /auth/oidc/callback | auth.oidc.callback | Token + userinfo → session login |
If the callback fails validation or the IdP rejects the login, you are redirected to /login with the error shown — no redirect loop back to the IdP.
For local QA, a Keycloak profile ships in compose.oidc.yaml (not for production):
docker compose -f compose.yaml -f compose.oidc.yaml --profile oidc up --build
Security notes
- Never link on unverified email — account takeover risk. Externa requires IdP
email_verified. - After first successful link, prefer
oidc_subover email. - Inactive / soft-deleted users are rejected.
- OIDC login does not satisfy
two_factor_required. SSO-only cohorts still need TOTP/passkey enrollment or the project MFA gate off. - Do not trust arbitrary
roles/groupsclaims without an allowlist (not implemented in v1).
Operator checklist
- [ ] Redirect URI matches production
APP_URL - [ ] Client secret saved (configured flag true); plaintext never returned on edit
- [ ] Link-only path verified with an existing user
- [ ] If JIT on: allowed domains + default role verified; unknown domains rejected
- [ ] Password and passkey login still work