pediatric-ai-scribe-v3/OPENID_SETUP.md
ifedan-ed 75a3a47598 v4: Fix milestones display + add OpenID auth + 100MB PDF support
FIXES:
- Milestones now show correctly on encounter page (use static fallback if DB empty)
- Static data preserved as MILESTONES_DATA_STATIC for compatibility
- Database-driven milestones still work (admin can edit via CMS)

NEW FEATURES:
- OpenID Connect (OIDC) authentication support (PocketID, Keycloak, Azure AD, etc.)
- Comprehensive setup guide: OPENID_SETUP.md
- Auto-linking existing users by email on SSO login
- Multiple PDF upload support in Learning Hub (up to 10 files)
- 100 MB per file limit (was 20 MB)
- Full PDF content used for AI generation
- Embeddings use first ~8K chars for semantic search

IMPROVEMENTS:
- Updated UI to show multiple file selection with list
- Drag-and-drop supports multiple files
- Better file upload validation and error handling
- Added clarifying comments about embedding truncation
2026-04-01 17:59:51 +00:00

9.7 KiB

OpenID Connect (OIDC) / PocketID Setup Guide

This guide explains how to configure Single Sign-On (SSO) authentication using OpenID Connect providers like PocketID, Keycloak, Azure AD, Okta, or Google.

Overview

The application supports OIDC authentication alongside traditional email/password login. Once configured, users can:

  • Sign in with their SSO provider (e.g., PocketID)
  • Automatically link existing email accounts to their SSO identity
  • Admins can optionally disable local password login entirely

Prerequisites

  1. An OpenID Connect provider (e.g., PocketID instance)
  2. Admin access to this application
  3. The public URL where your app is deployed (APP_URL in .env)

Configuration Steps

1. Configure Your Identity Provider

First, register this application with your OIDC provider. You'll need:

Redirect URI / Callback URL:

https://your-domain.com/api/auth/oidc/callback

Replace your-domain.com with your actual APP_URL value.

Example: PocketID Setup

  1. Log into your PocketID admin panel
  2. Navigate to ApplicationsAdd Application
  3. Set the callback URL: https://your-domain.com/api/auth/oidc/callback
  4. Copy the Client ID and Client Secret

Example: Keycloak Setup

  1. Create a new client in your Keycloak realm
  2. Set Access Type to confidential
  3. Add Valid Redirect URI: https://your-domain.com/api/auth/oidc/callback
  4. Save and note the Client ID and Client Secret from the Credentials tab

2. Enable OIDC in Application Settings

Log into your application as an admin user, then:

  1. Navigate to Admin PanelSettings (or access /admin-settings.html)
  2. Look for the OpenID Connect (SSO) section
  3. Fill in the following fields:
Field Description Example
Enabled Toggle to enable OIDC true
Issuer URL Your provider's discovery endpoint https://id.example.com or https://keycloak.example.com/realms/myrealm
Client ID Application client ID from your provider pediatric-scribe-client
Client Secret Application client secret (keep confidential) a1b2c3d4...
Button Label Text shown on the SSO login button Sign in with PocketID
Disable Local Auth Hide email/password login (optional) false (keep disabled initially)
Allowed IPs Restrict SSO to specific IP ranges (optional) Leave blank for no restriction
  1. Click Save Settings

3. Test SSO Login

  1. Log out or open an incognito browser window
  2. Visit the login page
  3. You should see a new button: "Sign in with [Your Provider]"
  4. Click it and authenticate with your SSO provider
  5. You'll be redirected back to the application and logged in

Linking Existing Users to SSO

When a user signs in via OIDC for the first time, the system automatically links their account based on email address matching:

Scenario 1: Existing User with Matching Email

If a user already has an account with email doctor@example.com and signs in via SSO with the same email:

  1. The system finds the existing user by email
  2. Links the SSO identity (oidc_sub) to the existing account
  3. The user is logged in
  4. Future logins can use either method (email/password OR SSO)

Database update performed:

UPDATE users
SET oidc_sub = '<provider-unique-id>',
    email_verified = true
WHERE email = 'doctor@example.com';

Scenario 2: New User (No Matching Email)

If the SSO email doesn't match any existing user:

  1. A new account is automatically created
  2. The user is assigned the user role (first user becomes admin)
  3. A random password is generated (not used for SSO logins)
  4. The user is logged in

Scenario 3: Disabled User

If an existing user is disabled (disabled = true in database):

  • SSO login is blocked
  • User sees an error message
  • Admin must re-enable the account from the Admin Panel

Manual Account Linking (CLI)

If you need to manually link an existing user to an SSO identity, use the PostgreSQL database directly:

# Connect to database
docker exec -it pediatric-ai-scribe-postgres psql -U pedscribe -d pedscribe

# Link user by setting their oidc_sub
UPDATE users
SET oidc_sub = 'provider-sub-12345',
    email_verified = true
WHERE email = 'doctor@example.com';

Finding the oidc_sub value:

The oidc_sub is the unique identifier from your OIDC provider (usually a UUID or numeric ID). To find it:

  1. Have the user attempt SSO login once
  2. Check the application logs for their sub claim:
    [OIDC] User logged in: sub=abc-123-def, email=doctor@example.com
    
  3. Use that sub value in the UPDATE statement

Security Considerations

HTTPS Required in Production

OIDC requires HTTPS for security. Ensure your APP_URL uses https://:

APP_URL=https://scribe.example.com

Client Secret Protection

The client secret is stored encrypted in the database. The admin UI masks it after saving (shows ••••••••1234).

Never commit the client secret to Git or share it publicly.

IP Allowlisting (Optional)

To restrict SSO to specific networks (e.g., hospital VPN):

  1. Set Allowed IPs in admin settings to comma-separated CIDR ranges:
    10.0.0.0/8, 192.168.1.0/24
    
  2. Users outside these ranges will see an error when attempting SSO

Disable Local Password Login

Once SSO is working, you can optionally disable traditional email/password login:

  1. In Admin Settings, enable Disable Local Auth
  2. The login page will only show the SSO button
  3. Admins can still use the CLI to reset passwords if needed

Warning: Only disable local auth after confirming all users can access SSO. Keep one admin password as backup.


Troubleshooting

"SSO is not enabled" error

  • Verify Enabled is set to true in admin settings
  • Check application logs for OIDC configuration errors

"Invalid state" or "Expired" error

  • The OIDC flow timed out (5 minute window)
  • Try logging in again
  • If persistent, check server time synchronization

"No email claim" error

Your OIDC provider didn't return an email address. Ensure:

  1. The email scope is requested (default: openid email profile)
  2. Your provider is configured to release email claims
  3. The user's account has an email address set

Email Mismatch

If a user has different emails in the app vs. SSO provider:

Option 1: Update app email to match SSO

UPDATE users SET email = 'new-email@example.com' WHERE id = 123;

Option 2: Update SSO provider email to match app (Provider-specific — consult your IdP documentation)

Callback URL Not Working

Double-check the redirect URI in your OIDC provider settings matches exactly:

https://your-domain.com/api/auth/oidc/callback

Common mistakes:

  • Missing https://
  • Trailing slash (don't include it)
  • Wrong domain (must match APP_URL in .env)

Provider-Specific Examples

PocketID

Issuer URL: https://id.pockethost.io
Client ID: (from PocketID app settings)
Client Secret: (from PocketID app settings)
Redirect URI: https://your-domain.com/api/auth/oidc/callback

Keycloak

Issuer URL: https://keycloak.example.com/realms/medical
Client ID: pediatric-scribe
Client Secret: (from Credentials tab)
Redirect URI: https://your-domain.com/api/auth/oidc/callback

Azure AD / Entra ID

Issuer URL: https://login.microsoftonline.com/{tenant-id}/v2.0
Client ID: (Application ID from Azure)
Client Secret: (from Certificates & secrets)
Redirect URI: https://your-domain.com/api/auth/oidc/callback

Note: Azure requires app registration in Azure Portal first.

Okta

Issuer URL: https://{your-okta-domain}.okta.com
Client ID: (from Okta application settings)
Client Secret: (from Okta application settings)
Redirect URI: https://your-domain.com/api/auth/oidc/callback

Google (Workspace or Gmail)

Issuer URL: https://accounts.google.com
Client ID: (from Google Cloud Console)
Client Secret: (from Google Cloud Console)
Redirect URI: https://your-domain.com/api/auth/oidc/callback

Note: Google requires OAuth consent screen configuration.


Environment Variables (Alternative to UI Config)

For deployment automation, you can set OIDC config via environment variables instead of the admin UI:

# .env file
OIDC_ENABLED=true
OIDC_ISSUER=https://id.example.com
OIDC_CLIENT_ID=my-client-id
OIDC_CLIENT_SECRET=my-client-secret
OIDC_BUTTON_LABEL=Sign in with PocketID
OIDC_DISABLE_LOCAL_AUTH=false

Note: UI settings take precedence over environment variables. If set in both places, the database values are used.


HIPAA Compliance Notes

OIDC does not transmit PHI to the identity provider. Only authentication-related data (email, name) is exchanged.

For HIPAA compliance:

  • Ensure your OIDC provider has appropriate safeguards
  • Use a self-hosted provider (Keycloak, PocketID) within your secure network
  • Or use a HIPAA-compliant SaaS provider with a BAA
  • Enable audit logging for all SSO login events (automatically logged in audit_log table)

Audit Logging

All SSO login events are logged in the audit_log table:

SELECT * FROM audit_log WHERE action = 'login_oidc' ORDER BY created_at DESC;

Logged fields:

  • User ID
  • Action: login_oidc
  • IP address
  • Details: Issuer URL
  • Timestamp

Support

For issues specific to:

  • This application: Check application logs with docker logs pediatric-ai-scribe
  • Your OIDC provider: Consult provider documentation (PocketID, Keycloak, Azure, etc.)
  • Network/TLS issues: Verify APP_URL matches your reverse proxy configuration

Common log locations:

# Application logs
docker logs pediatric-ai-scribe

# PostgreSQL logs
docker logs pediatric-ai-scribe-postgres