# 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 ```bash cd 01-infra docker compose down docker compose --env-file ../.env.global up -d ``` Wait for Keycloak to initialize (check logs): ```bash 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` ```bash cd ../03-apiservice ``` Create `.env` file with: ```bash # 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= KEYCLOAK_REDIRECT_URI=http://localhost:8040/apiservice/auth/callback ``` **Important**: Replace `` 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 ```bash cd 03-apiservice docker compose down docker compose --env-file ../.env up --build -d ``` Check logs: ```bash 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) ```bash # 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**: ```bash 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