okta-setup.md 2.6 KB

Okta OIDC setup

The app authenticates via Okta using OpenID Connect (Authlib). Okta settings live only in config.toml [okta] — they are never editable from the admin panel.

1. Create the Okta app

In the Okta admin console: Applications → Create App Integration → OIDC → Web Application.

  • Sign-in redirect URI: https://YOUR-HOST/authorize (must match okta.redirect_uri exactly, including scheme and trailing path).
  • Sign-out redirect URI: https://YOUR-HOST/login (optional).
  • Grant type: Authorization Code.
  • Assignments: grant the app to the users/groups who should be able to reach the login (fine-grained allow/deny is still enforced by this app's own users list).

Note the Client ID, Client secret, and your issuer (usually https://TENANT.okta.com/oauth2/default — verify at {issuer}/.well-known/openid-configuration).

2. Configure the app

[auth]
mode = "oidc"
denied_message = "Your account is not authorised. Contact IT."

[[auth.users]]
email = "you@company.com"
role  = "admin"

[okta]
issuer        = "https://TENANT.okta.com/oauth2/default"
client_id     = "0oaXXXXXXXX"
client_secret = "XXXXXXXX"
redirect_uri  = "https://YOUR-HOST/authorize"

The app requests scopes openid email profile and identifies users by the email claim.

3. Access model

Okta decides who can authenticate; this app's [[auth.users]] list decides who is allowed in and with what role. An authenticated user not on the list sees denied_message (HTTP 403). Roles (admin/user) come from this list and are re-checked on every request, so removing or demoting someone takes effect immediately without them re-logging-in.

Gotchas

  • redirect_uri mismatch is the #1 failure. It must match Okta exactly. Behind a TLS-terminating proxy, do not rely on auto-detection — set the explicit https:// value.
  • SameSite=Lax is required (the app sets it). Strict would drop the session cookie on the cross-site callback and every login would fail with mismatching_state.
  • cookie_secure = true in production (HTTPS). Only set false for plain-http localhost.
  • Changing any [okta] key or auth.mode requires a restart (the OIDC client is registered at boot).

Testing without a tenant

Use [auth] mode = "static" with [auth.static] credentials for local development. The app logs a loud warning and refuses to bind a non-loopback interface in this mode (override with PPSQ_ALLOW_INSECURE_AUTH=1 for dev only). The OIDC callback logic is covered by tests/test_oidc.py with a monkeypatched token exchange — no tenant needed.