326 lines
7.9 KiB
Markdown
326 lines
7.9 KiB
Markdown
# 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=<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
|
|
|
|
```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
|