Files
sriphat-dataplatform/03-apiservice/KEYCLOAK-SETUP.md

7.9 KiB

Keycloak Setup Guide for API Service Authentication

Prerequisites

  1. Keycloak must be running at http://localhost:8085
  2. PostgreSQL database must have keycloak database created
  3. API Service must have Keycloak configuration in .env

Step 1: Restart Infrastructure to Create Keycloak Database

cd 01-infra
docker compose down
docker compose --env-file ../.env.global up -d

Wait for Keycloak to initialize (check logs):

docker logs keycloak -f

Look for: Keycloak ... started

Step 2: Access Keycloak Admin Console

  1. Open browser: http://localhost:8085/admin
  2. Login credentials:
    • Username: admin
    • Password: admin_secret_pass_2026

Step 3: Create Client for API Service

3.1 Navigate to Clients

  1. In left sidebar, click Clients
  2. Click Create client button

3.2 General Settings

  • Client type: OpenID Connect
  • Client ID: apiservice
  • Click Next

3.3 Capability Config

  • Client authentication: ON (toggle to enable)
  • Authorization: OFF
  • Authentication flow:
    • Standard flow
    • Direct access grants
    • Implicit flow (uncheck)
    • Service accounts roles (uncheck)
  • Click Next

3.4 Login Settings

Fill in the following URLs:

Root URL:

http://localhost:8040

Home URL:

http://localhost:8040/apiservice/

Valid redirect URIs (add both):

http://localhost:8040/apiservice/auth/callback
https://ai.sriphat.com/apiservice/auth/callback

Valid post logout redirect URIs (add both):

http://localhost:8040/apiservice/
https://ai.sriphat.com/apiservice/

Web origins (add both):

http://localhost:8040
https://ai.sriphat.com

Click Save

3.5 Get Client Secret

  1. Go to Credentials tab
  2. Copy the Client secret value
  3. Save it - you'll need it for the .env file

Step 4: Configure API Service

4.1 Create/Update .env file in 03-apiservice

cd ../03-apiservice

Create .env file with:

# Application
APP_NAME=APIsService
ROOT_PATH=/apiservice

# Timezone
TIMEZONE=Asia/Bangkok

# PostgreSQL Database
DB_HOST=postgres
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=Secure_Hospital_Pass_2026
DB_NAME=postgres
DB_SSLMODE=prefer

# Supabase (if not using, keep dummy values)
SUPABASE_DB_HOST=localhost
SUPABASE_DB_PORT=5432
SUPABASE_DB_USER=postgres
SUPABASE_DB_PASSWORD=dummy
SUPABASE_DB_NAME=postgres
SUPABASE_DB_SSLMODE=disable
SUPABASE_API_URL=http://localhost:8000
SUPABASE_API_KEY=dummy

# Admin Authentication
ADMIN_SECRET_KEY=apiservice_admin_secret_2026
ADMIN_USERNAME=admin
ADMIN_PASSWORD=change_me_2026

# API Key Encryption
API_KEY_ENC_SECRET=dev_encryption_secret_2026

# Keycloak Authentication
KEYCLOAK_SERVER_URL=http://keycloak:8080
KEYCLOAK_REALM=master
KEYCLOAK_CLIENT_ID=apiservice
KEYCLOAK_CLIENT_SECRET=<PASTE_YOUR_CLIENT_SECRET_HERE>
KEYCLOAK_REDIRECT_URI=http://localhost:8040/apiservice/auth/callback

Important: Replace <PASTE_YOUR_CLIENT_SECRET_HERE> with the actual secret from Step 3.5

Step 5: Create Test User in Keycloak

5.1 Create User

  1. In Keycloak Admin, go to Users
  2. Click Add user
  3. Fill in:
    • Username: testuser
    • Email: test@sriphat.local
    • First name: Test
    • Last name: User
    • Email verified: ON (toggle)
  4. Click Create

5.2 Set Password

  1. Go to Credentials tab
  2. Click Set password
  3. Fill in:
    • Password: password
    • Password confirmation: password
    • Temporary: OFF (toggle off)
  4. Click Save
  5. Confirm by clicking Save password

Step 6: Rebuild and Restart API Service

cd 03-apiservice
docker compose down
docker compose --env-file ../.env up --build -d

Check logs:

docker logs apiservice -f

Look for successful startup without errors.

Step 7: Test Authentication Flow

7.1 Access Landing Page

  1. Open browser: http://localhost:8040/apiservice/
  2. You should be redirected to Keycloak login page

7.2 Login

  1. Enter credentials:
    • Username: testuser
    • Password: password
  2. Click Sign In

7.3 Verify Success

  1. You should be redirected back to the landing page
  2. You should see your name in the top-right corner: "👤 Test User"
  3. You should see a Logout button

7.4 Test Protected Pages

Try accessing:

  • /apiservice/ - Landing page (requires auth)
  • /apiservice/docs - API docs (requires auth)
  • /apiservice/data-management/finance - Finance page (requires auth)

All should work without redirecting to login again.

7.5 Test API Endpoints (Should NOT require Keycloak auth)

# API endpoints use API Key authentication, not Keycloak
curl -X GET http://localhost:8040/apiservice/api/v1/health

This should work without Keycloak authentication.

7.6 Test Logout

  1. Click Logout button
  2. You should be logged out from Keycloak
  3. Accessing any protected page should redirect to login again

Troubleshooting

Error: "access_denied" in Keycloak logs

Cause: Client not configured correctly or client secret mismatch

Solution:

  1. Verify client ID is exactly apiservice
  2. Verify client secret in .env matches Keycloak
  3. Verify redirect URIs are correct
  4. Restart API service after changing .env

Error: "SessionMiddleware must be installed"

Cause: Middleware order is incorrect

Solution: Already fixed in app/main.py - SessionMiddleware is added after WebAuthenticationMiddleware

Error: "Invalid redirect_uri"

Cause: Redirect URI not in Keycloak's allowed list

Solution:

  1. Go to Keycloak → Clients → apiservice → Settings
  2. Add the exact redirect URI to "Valid redirect URIs"
  3. Make sure it matches KEYCLOAK_REDIRECT_URI in .env

Error: Connection refused to Keycloak

Cause: Keycloak not running or wrong URL

Solution:

  1. Check Keycloak is running: docker ps | grep keycloak
  2. Verify KEYCLOAK_SERVER_URL=http://keycloak:8080 (use container name, not localhost)
  3. Verify both services are on same Docker network

Production Deployment

For production deployment:

  1. Use HTTPS:

    KEYCLOAK_REDIRECT_URI=https://ai.sriphat.com/apiservice/auth/callback
    
  2. Update Keycloak Client:

    • Add production redirect URIs
    • Remove localhost URIs
  3. Secure Cookies:

    • SessionMiddleware should use secure=True, httponly=True, samesite='lax'
  4. Use Nginx Proxy Manager:

    • Configure SSL certificates
    • Set up proper reverse proxy rules
  5. Environment Variables:

    • Use Docker secrets or external secret management
    • Never commit .env with real secrets to git

Security Notes

  1. Client Secret: Keep this secret! Never commit to git
  2. Session Secret: Use strong random value for ADMIN_SECRET_KEY
  3. HTTPS: Always use HTTPS in production
  4. Token Expiration: Configure appropriate token lifetimes in Keycloak
  5. CORS: Restrict allow_origins to known domains only

Architecture

User Browser
    ↓
    ↓ (1) Access protected page
    ↓
API Service (FastAPI)
    ↓
    ↓ (2) Check session - not authenticated
    ↓
    ↓ (3) Redirect to Keycloak login
    ↓
Keycloak (OAuth2/OIDC)
    ↓
    ↓ (4) User enters credentials
    ↓
    ↓ (5) Redirect back with auth code
    ↓
API Service
    ↓
    ↓ (6) Exchange code for tokens
    ↓
    ↓ (7) Store user info in session
    ↓
    ↓ (8) Redirect to original page
    ↓
User sees protected page with user info

API vs Web Authentication

Type Authentication Method Protected Routes
Web Pages Keycloak (OAuth2/OIDC) /, /docs, /data-management/*
API Endpoints API Key (Bearer Token) /api/v1/*
Admin Panel SQLAdmin (Basic Auth) /admin/*

This separation allows:

  • Human users to login via Keycloak for web UI
  • Applications/services to use API Keys for programmatic access
  • Admins to manage API keys via separate admin panel