Files
cronmaster/howto/SSO.md
2025-11-11 11:33:51 +00:00

8.8 KiB

SSO with OIDC

Cr*nMaster 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)

Configuration

1. Configure your OIDC Provider

  • Client Type: Public
  • Grant Type: Authorization Code with PKCE
  • Scopes: openid, profile, email
  • Redirect URI: https://YOUR_APP_HOST/api/oidc/callback
  • Post-logout URI: https://YOUR_APP_HOST/login

2. Get these values from your provider

  • Client ID
  • OIDC Issuer URL (usually ends with .well-known/openid-configuration)

3. Set environment variables

services:
  cronmaster:
    environment:
      - SSO_MODE=oidc
      - OIDC_ISSUER=https://YOUR_SSO_HOST/issuer/path
      - OIDC_CLIENT_ID=your_client_id
      - APP_URL=https://your-cronmaster-domain.com # Required for OIDC, defaults to http://localhost:<port>

      # Optional:
      - OIDC_CLIENT_SECRET=your_client_secret # For confidential client mode (uses client secret instead of PKCE)
      - OIDC_LOGOUT_URL=https://provider.com/logout # Custom logout URL (bypasses discovery)
      - OIDC_GROUPS_SCOPE=groups # Scope for groups claim, set to "no" or "false" to disable
      - INTERNAL_API_URL=http://localhost:3000 # Use if getting 403 errors after login (reverse proxy issues)
      - DEBUGGER=true # Enable detailed OIDC flow logging
      - HTTPS=true # Set if running in production with HTTPS (affects secure cookie flag)

Note: When OIDC_CLIENT_SECRET is set, Cr*nMaster switches to confidential client mode using client authentication instead of PKCE.

Combining Password and SSO Authentication

You can enable both password-based authentication (AUTH_PASSWORD) and OIDC at the same time:

environment:
  - AUTH_PASSWORD=your_password
  - SSO_MODE=oidc
  - OIDC_ISSUER=https://your-sso-provider.com
  - OIDC_CLIENT_ID=your_client_id
  - APP_URL=https://your-cronmaster-domain.com

When both are enabled, the login page will display:

  • Password input field
  • SSO button ("Sign in with SSO")

Users can choose either method to authenticate.

Environment Variables Reference

Required for OIDC

Variable Description
SSO_MODE Set to oidc to enable SSO authentication
OIDC_ISSUER OIDC provider issuer URL (e.g., https://provider.com or https://provider.com/realm)
OIDC_CLIENT_ID Client ID from your OIDC provider
APP_URL Public URL of your Cronmaster instance (e.g., https://cron.domain.com)

Optional

Variable Default Description
OIDC_CLIENT_SECRET None Client secret for confidential client mode
OIDC_LOGOUT_URL None Custom logout URL (skips OIDC discovery for logout)
OIDC_GROUPS_SCOPE groups Scope to request groups claim. Set to no or false to disable
INTERNAL_API_URL APP_URL Internal URL for API calls (use if behind reverse proxy with 403 errors)
HTTPS false Set to true in production with HTTPS (enables __Host- cookie prefix and secure flag)
DEBUGGER false Enable detailed logging for OIDC flow debugging
NODE_TLS_REJECT_UNAUTHORIZED 1 Set to 0 to allow self-signed certificates (DEV ONLY - UNSAFE in production!)

Verified Providers

These providers have been tested:

  • Auth0 - OIDC_ISSUER=https://YOUR_TENANT.REGION.auth0.com
  • Authentik - OIDC_ISSUER=https://YOUR_DOMAIN/application/o/APP_SLUG/
  • Keycloak
  • Okta

Other standard OIDC providers should work as well.

How It Works

  1. User clicks "Sign in with SSO" button
  2. User redirected to OIDC provider's authorization endpoint with:
    • response_type=code
    • PKCE challenge (or client secret if configured)
    • state and nonce for security
    • Requested scopes: openid profile email (and groups if enabled)
  3. User authenticates with the provider
  4. Provider redirects back to /api/oidc/callback with authorization code
  5. Cr*nMaster exchanges code for ID token using:
    • PKCE verifier (or client secret)
    • Validates state matches
  6. ID token verified using provider's JWKS (public keys):
    • Issuer validation
    • Audience validation (client ID)
    • Nonce validation
    • Signature verification
  7. Secure session created with:
    • Cryptographically random session ID (32 bytes, base64url)
    • Stored in data/sessions/sessions.json
    • 30-day expiration
  8. Session cookie set:
    • Name: __Host-cronmaster-session (production with HTTPS) or cronmaster-session
    • HttpOnly, Secure (if HTTPS), SameSite=Lax
  9. Session validated on each request via middleware → /api/auth/check-session

Troubleshooting

403 Forbidden After SSO Login (Reverse Proxy)

If you successfully authenticate via SSO but get redirected back to login, and your logs show:

MIDDLEWARE - Session Check Response:
  status: 403
MIDDLEWARE - session is not ok

Solution: Set INTERNAL_API_URL:

environment:
  - INTERNAL_API_URL=http://localhost:3000

This tells the middleware to use localhost for session validation instead of going through the reverse proxy.

Why: When APP_URL is set to your external domain, the middleware tries to validate sessions by calling https://external-domain.com/api/auth/check-session, which goes through your reverse proxy and may get blocked with 403.

Login Redirect Loop

If stuck in a redirect loop:

  1. Verify APP_URL matches your public URL exactly
  2. Check redirect URI in provider matches: https://YOUR_DOMAIN/api/oidc/callback
  3. Enable DEBUGGER=true to see detailed logs
  4. Check browser console for errors
  5. Verify OIDC provider discovery URL is accessible: {OIDC_ISSUER}/.well-known/openid-configuration

Self-Signed Certificate Error

If you see this error in logs:

[OIDC Login] Error: TypeError: fetch failed
cause: Error: self-signed certificate
code: 'DEPTH_ZERO_SELF_SIGNED_CERT'

Solution for development: Set NODE_TLS_REJECT_UNAUTHORIZED=0:

environment:
  - NODE_TLS_REJECT_UNAUTHORIZED=0

⚠️ WARNING: This disables SSL certificate validation and should ONLY be used in development environments with self-signed certificates. NEVER use this in production!

Production solution: Use proper SSL certificates from a trusted CA (Let's Encrypt, etc.)

401 Unauthorized After Login

If you authenticate but immediately see login page again:

  1. Check cookies are being set (browser dev tools → Application → Cookies)
  2. Look for cronmaster-session or __Host-cronmaster-session cookie
  3. Enable DEBUGGER=true to see session validation in middleware
  4. Check data/sessions/sessions.json file exists and has your session
  5. Verify file permissions on data/ directory

Provider-Specific Errors

"Invalid client"

  • Client ID or secret is incorrect
  • Verify values in provider configuration

"Invalid redirect URI"

  • Redirect URI doesn't match exactly
  • Must include protocol: https:// not just domain.com
  • Must match APP_URL + /api/oidc/callback
  • Case-sensitive

"Invalid scope"

  • Provider doesn't support requested scope
  • Try setting OIDC_GROUPS_SCOPE=no to disable groups scope
  • Some providers (like Google) don't support groups scope

"Nonce mismatch"

  • Browser cookies being blocked or cleared
  • Check cookie settings and privacy mode
  • May need to adjust SameSite cookie settings

"JWKS fetch failed"

  • Provider's JWKS endpoint not accessible
  • Check firewall/network rules
  • Verify OIDC_ISSUER is correct

Security Notes

  • Sessions valid for 30 days, stored with cryptographically random IDs
  • PKCE used by default (no client secret in authorization URL)
  • ID tokens verified with JWKS (provider's public keys)
  • State parameter prevents CSRF
  • Nonce parameter prevents replay attacks
  • HttpOnly cookies prevent XSS
  • Secure flag on cookies in production with HTTPS
  • __Host- cookie prefix in production for additional security
  • Clock tolerance of 5 seconds for token validation

Production Recommendations:

  • Set HTTPS=true when running with HTTPS
  • Use OIDC_CLIENT_SECRET if provider supports confidential clients (more secure than PKCE)
  • Set strong, random API_KEY to protect API endpoints
  • Use INTERNAL_API_URL when behind reverse proxy
  • Regularly rotate OIDC_CLIENT_SECRET if using confidential client mode

Debugging

Enable debug mode to see detailed OIDC flow:

environment:
  - DEBUGGER=true

You'll see logs for:

  • [OIDC Login] - Authorization redirect
  • [OIDC Callback] - Token exchange and JWT verification
  • [OIDC Logout] - Logout flow
  • [Session] - Session creation/validation/deletion
  • MIDDLEWARE - Session check and URL resolution

Check both server logs and browser console for complete picture.