jotty·page supports any OIDC provider (Authentik, Auth0, Keycloak, Okta, etc.) with these requirements:
- Supports PKCE (most modern providers do)
- Can be configured as a public client (no client secret needed)
- Provides standard OIDC scopes (openid, profile, email)
- Configure your OIDC Provider:
- Client Type: Public
- Grant Type: Authorization Code with PKCE
- PKCE Code Challenge Method: S256
- note S256 is the only PKCE method Jotty supports
- Scopes: openid, profile, email
- Redirect URI: https://YOUR_APP_HOST/api/oidc/callback
- Post-logout URI: https://YOUR_APP_HOST/
- Get these values from your provider:
- Client ID
- OIDC Issuer URL (usually ends with .well-known/openid-configuration)
- Set environment variables:
services:
jotty:
environment:
- AUTH_MODE=oidc
- OIDC_ISSUER=https://YOUR_SSO_HOST/issuer/path
- OIDC_CLIENT_ID=your_client_id
- APP_URL=https://your-jotty-domain.com # if not set defaults to http://localhost:<port>
# Optional security enhancements:
- OIDC_CLIENT_SECRET=your_client_secret # Enable confidential client mode (if your provider requires it)
- SSO_FALLBACK_LOCAL=yes # Allow both SSO and local login
- OIDC_ADMIN_GROUPS=admins # Map IDP groups claim to admin role
- OIDC_ADMIN_ROLES=admins # Map IDP roles claim to admin role
- OIDC_USER_GROUPS=jotty_users,app_users # Restrict access to users in these groups (admins always allowed)
- OIDC_USER_ROLES=user,member # Restrict access to users with these roles (admins always allowed)
- OIDC_GROUPS_SCOPE=groups # Scope to request for groups (set to empty string or "no" to disable for providers like Entra ID)
- OIDC_LOGOUT_URL=https://authprovider.local/realms/master/logout # Custom logout URL for global logout
# Optional for reverse proxy issues:
# - INTERNAL_API_URL=http://localhost:3000 # Use if getting 403 errors after SSO loginNote: When OIDC_CLIENT_SECRET is set, jotty·page switches to confidential client mode using client authentication instead of PKCE. This is more secure but requires provider support.
Dev verified Providers:
- Auth0 (
OIDC_ISSUER=https://YOUR_TENANT.REGION.auth0.com) - Authentik (
OIDC_ISSUER=https://YOUR_DOMAIN/application/o/APP_SLUG/)
Other providers will likely work, but I can at least guarantee these do as I have test them both locally.
Community verified Providers:
- Pocket ID(
OIDC_ISSUER: https://my-pocket-id.domain.com) - Authelia (
OIDC_ISSUER: https://my-authelia.domain.com) - Google (
OIDC_ISSUER: https://accounts.google.com) - Entra ID (Azure AD) (
OIDC_ISSUER: https://login.microsoftonline.com/{tenant-id}/v2.0)
Provider's specific notes:
- Google provider doesn't support usage of
groupswith OIDC authentication, so do NOT set theOIDC_ADMIN_GROUPSenvironment variable. - Entra ID provider allows usage of admin groups with
OIDC_ADMIN_GROUPS={Entra Group ID}variable. For that, ensure to include optionalgroupsclaim in the 'Token Configuration' pane of your 'Enterprise Registration' AND define the environment variable toOIDC_GROUPS_SCOPE="no"orOIDC_GROUPS_SCOPE="". Alternatively, useOIDC_ADMIN_ROLES=role-nameto make use of Application Groups configured in Entra.
p.s. First user to sign in via SSO when no local users exist becomes admin automatically.
If you successfully authenticate via SSO but get redirected back to the login page, and your logs show:
MIDDLEWARE - sessionCheck: Response { ... status: 403 ... }
MIDDLEWARE - session is not ok
This means the app is trying to validate your session by calling its own API through the external URL, but your reverse proxy is blocking it.
Solution: Set the INTERNAL_API_URL environment variable:
environment:
- INTERNAL_API_URL=http://localhost:3000This tells the app to use localhost for internal API calls instead of going through the reverse proxy. The default value is already http://localhost:3000, but explicitly setting it can help in some edge cases.
Why this happens: When APP_URL is set to your external domain (e.g., https://jotty.domain.com), the middleware tries to validate sessions by making a fetch request to https://jotty.domain.com/api/auth/check-session. This request goes through your reverse proxy, which may block it with a 403 Forbidden response due to security policies or misconfigurations.
The first user to register in the system is the "Super Admin", referred to as "System Owner" in the web interface.
If this user is not an SSO user, or if you need to change the super admin to a different user, you can update their
super admin status using the update-super-admin.sh script below.
Run from the server outside of the Docker container, if using Docker. You need write permissions to the
users.jsonfile. You will if running this asroot.
-
Locate your
datavolume location on the server filesystem and theusers.jsonfile. e.g. if using the exampledocker-compose.yml, undervolumesyou will see./data:/app/data:rw. Thedatavolume is under the location of your compose file, making the full path tousers.json:<compose_location>/data/users.json -
Run the
update-super-admin.shscript with the appropriate arguments to update the super admin user:wget -qO- https://raw.githubusercontent.com/fccview/jotty/main/scripts/update-super-admin.sh | bash -s -- <new super admin> <users.json location>
Alternatively download the script and run it directly.
Omit the arguments (
wget -qO- ... | bash) for help text.The old Super Admin user will be left as an Admin. The new Super Admin has the ability to delete the old user if you choose to do so.
Docker Secrets configuration
For enhanced security in production environments, you can store OIDC credentials in files instead of environment variables. This prevents secrets from appearing in docker inspect output.
Example docker-compose.yml:
services:
jotty:
environment:
- AUTH_MODE=oidc
- OIDC_ISSUER=https://YOUR_SSO_HOST/issuer/path
- OIDC_CLIENT_ID_FILE=/run/secrets/oidc_client_id
- OIDC_CLIENT_SECRET_FILE=/run/secrets/oidc_client_secret
- APP_URL=https://your-jotty-domain.com
secrets:
- oidc_client_id
- oidc_client_secret
secrets:
oidc_client_id:
file: ./secrets/oidc_client_id.txt
oidc_client_secret:
file: ./secrets/oidc_client_secret.txtCreate the secret files:
mkdir secrets
echo "your_client_id" > secrets/oidc_client_id.txt
echo "your_client_secret" > secrets/oidc_client_secret.txt
chmod 600 secrets/*Note: You can mix and match - use OIDC_CLIENT_ID directly and OIDC_CLIENT_SECRET_FILE for the secret, or vice versa. The _FILE variants take priority if both are set. Most users can skip this and use regular environment variables.