Skip to content

Commit 6474487

Browse files
feat(medcat-trainer)!: Consolidate OIDC env vars (#307)
BREAKING CHANGE: Previous env vars have been renamed and will no longer work.
1 parent a6850ef commit 6474487

File tree

10 files changed

+108
-84
lines changed

10 files changed

+108
-84
lines changed

medcat-trainer/OIDC_AUTHENTICATION_GUIDE.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ MedCAT Trainer uses a **two-client architecture** for OIDC:
3939
"KEYCLOAK_URL": "https://auth.cogstack.example.site",
4040
"KEYCLOAK_REALM": "cogstack",
4141
"KEYCLOAK_CLIENT_ID": "cogstack-medcattrainer-frontend",
42-
"LOGOUT_REDIRECT_URI": "https://launchpad.cogstack.example.site/"
42+
"KEYCLOAK_LOGOUT_REDIRECT_URI": "https://launchpad.cogstack.example.site/"
4343
}
4444
```
4545

@@ -57,11 +57,11 @@ MedCAT Trainer uses a **two-client architecture** for OIDC:
5757
**Configuration:**
5858
```python
5959
# Backend settings
60-
OIDC_HOST = "https://auth.cogstack.example.site"
61-
OIDC_REALM = "cogstack"
62-
OIDC_FRONTEND_CLIENT_ID = "cogstack-medcattrainer-frontend"
63-
OIDC_BACKEND_CLIENT_ID = "cogstack-medcattrainer-backend"
64-
OIDC_BACKEND_CLIENT_SECRET = "***secret***"
60+
KEYCLOAK_INTERNAL_SERVICE_URL = "https://auth.cogstack.example.site"
61+
KEYCLOAK_REALM = "cogstack"
62+
KEYCLOAK_FRONTEND_CLIENT_ID = "cogstack-medcattrainer-frontend"
63+
KEYCLOAK_BACKEND_CLIENT_ID = "cogstack-medcattrainer-backend"
64+
KEYCLOAK_BACKEND_CLIENT_SECRET = "***secret***"
6565
```
6666
---
6767
## Key Files

medcat-trainer/docker-compose-dev.yml

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,15 @@ services:
2424
- ./envs/env
2525
environment:
2626
- MCT_VERSION=latest
27-
# Frontend OIDC Settings (Public Client)
28-
- VITE_USE_OIDC=1
29-
- VITE_KEYCLOAK_URL=http://keycloak.cogstack.localhost
30-
- VITE_KEYCLOAK_REALM=cogstack-realm
31-
- VITE_KEYCLOAK_CLIENT_ID=cogstack-medcattrainer-frontend
32-
- VITE_LOGOUT_REDIRECT_URI=http://launch.cogstack.localhost/
33-
# Backend OIDC Settings (Confidential Client)
27+
# OIDC Settings
3428
- USE_OIDC=1
35-
- OIDC_HOST=http://keycloak:8080
36-
- OIDC_REALM=cogstack-realm
37-
- OIDC_FRONTEND_CLIENT_ID=cogstack-medcattrainer-frontend
38-
- OIDC_BACKEND_CLIENT_ID=cogstack-medcattrainer-backend
39-
- OIDC_BACKEND_CLIENT_SECRET=***
29+
- KEYCLOAK_URL=http://keycloak.cogstack.localhost
30+
- KEYCLOAK_REALM=cogstack-realm
31+
- KEYCLOAK_FRONTEND_CLIENT_ID=cogstack-medcattrainer-frontend
32+
- KEYCLOAK_LOGOUT_REDIRECT_URI=http://launch.cogstack.localhost/
33+
- KEYCLOAK_INTERNAL_SERVICE_URL=http://keycloak:8080
34+
- KEYCLOAK_BACKEND_CLIENT_ID=cogstack-medcattrainer-backend
35+
- KEYCLOAK_BACKEND_CLIENT_SECRET=***
4036
command: /usr/bin/supervisord -c /etc/supervisord.conf
4137

4238
nginx:

medcat-trainer/docs/installation.md

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -77,31 +77,27 @@ You'll need to `docker stop` the running containers if you have already run the
7777

7878
You can enable OIDC (OpenID Connect) authentication for the MedCAT Trainer. To do so, you must configure the following environment variables:
7979

80-
#### Frontend (Runtime Config)
81-
82-
| Variable | Example | Description |
83-
|----------|---------|-------------|
84-
| `VITE_USE_OIDC` | `1` | Enable OIDC (1=enabled, 0=traditional auth) |
85-
| `VITE_KEYCLOAK_URL` | `https://cogstack-auth.sites.er.kcl.ac.uk` | Keycloak base URL |
86-
| `VITE_KEYCLOAK_REALM` | `cogstack` | Keycloak realm name |
87-
| `VITE_KEYCLOAK_CLIENT_ID` | `cogstack-medcattrainer-frontend` | Public client ID |
88-
| `VITE_LOGOUT_REDIRECT_URI` | `https://cogstack-launchpad.sites.er.kcl.ac.uk/` | Where to go after logout |
89-
90-
#### Backend (Django Settings)
91-
92-
| Variable | Example | Description |
93-
|----------|---------|-------------|
94-
| `USE_OIDC` | `1` | Enable OIDC validation |
95-
| `OIDC_HOST` | `https://cogstack-auth.sites.er.kcl.ac.uk` | Keycloak base URL (for backend) |
96-
| `OIDC_REALM` | `cogstack` | Realm name |
97-
| `OIDC_FRONTEND_CLIENT_ID` | `cogstack-medcattrainer-frontend` | Frontend client ID (for token validation) |
98-
| `OIDC_BACKEND_CLIENT_ID` | `cogstack-medcattrainer-backend` | Backend client ID |
99-
| `OIDC_BACKEND_CLIENT_SECRET` | `***secret***` | Backend client secret |
100-
80+
| Variable | Example | Description |
81+
|-----------------------------------|-------------------------------------------|----------------------------------------------------|
82+
| `USE_OIDC` | `1` | Enable OIDC (1=enabled, 0=traditional auth) |
83+
| `KEYCLOAK_URL` | `https://auth.example.org` | Keycloak base URL |
84+
| `KEYCLOAK_REALM` | `cogstack` | Keycloak realm name |
85+
| `KEYCLOAK_LOGOUT_REDIRECT_URI` | `https://cogstack-launchpad.example.org/` | Where to go after logout |
86+
| `KEYCLOAK_INTERNAL_SERVICE_URL` | `http://keycloak.8080` | Keycloak internal service URL |
87+
| `KEYCLOAK_FRONTEND_CLIENT_ID` | `cogstack-medcattrainer-frontend` | Keycloak Frontend client ID (for token validation) |
88+
| `KEYCLOAK_BACKEND_CLIENT_ID` | `cogstack-medcattrainer-backend` | Keycloak Backend client ID |
89+
| `KEYCLOAK_BACKEND_CLIENT_SECRET` | `***secret***` | Keycloak Backend client secret |
90+
91+
#### Advanced Optional OIDC Settings
92+
93+
| Variable | Default | Description |
94+
|-----------------------------------|---------|-----------------------------------------------------------------------------------|
95+
| `KEYCLOAK_TOKEN_MIN_VALIDITY` | `30` | The interval in seconds between each refresh attempt |
96+
| `KEYCLOAK_TOKEN_REFRESH_INTERVAL` | `20` | Minimum time in seconds the token should remain valid before triggering a refresh |
10197

10298
You can either use the Gateway Auth stack available in cogstack-ops or deploy your own Keycloak instance.
103-
If you deploy your own Keycloak instance, make sure to configure the network accordingly.
10499

100+
#### Roles
105101
Currently, there are two roles that can be assigned to users:
106102

107103
| Keycloak Role | Django Permission | Capabilities |

medcat-trainer/webapp/api/core/settings.py

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -200,40 +200,64 @@
200200
}
201201

202202
if USE_OIDC:
203-
log.info("Using OIDC authentication")
203+
print("Using OIDC authentication")
204204
REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"] = [
205205
'oidc_auth.authentication.JSONWebTokenAuthentication',
206206
'oidc_auth.authentication.BearerTokenAuthentication',
207207
]
208208

209-
OIDC_HOST = os.environ.get('OIDC_HOST', '')
210-
OIDC_REALM = os.environ.get('OIDC_REALM', default='cogstack-realm')
211-
OIDC_BACKEND_CLIENT_ID = os.environ.get('OIDC_BACKEND_CLIENT_ID', default='cogstack-medcattrainer-backend')
212-
OIDC_BACKEND_CLIENT_SECRET = os.environ.get('OIDC_BACKEND_CLIENT_SECRET', default='')
213-
OIDC_FRONTEND_CLIENT_ID = os.environ.get('OIDC_FRONTEND_CLIENT_ID', default='cogstack-medcattrainer-frontend')
209+
# Load OIDC configuration from environment
210+
KEYCLOAK_INTERNAL_SERVICE_URL = os.environ.get('KEYCLOAK_INTERNAL_SERVICE_URL')
211+
KEYCLOAK_REALM = os.environ.get('KEYCLOAK_REALM')
212+
KEYCLOAK_BACKEND_CLIENT_ID = os.environ.get('KEYCLOAK_BACKEND_CLIENT_ID')
213+
KEYCLOAK_BACKEND_CLIENT_SECRET = os.environ.get('KEYCLOAK_BACKEND_CLIENT_SECRET')
214+
KEYCLOAK_FRONTEND_CLIENT_ID = os.environ.get('KEYCLOAK_FRONTEND_CLIENT_ID')
215+
216+
# Validate required OIDC configuration
217+
missing_vars = []
218+
if not KEYCLOAK_INTERNAL_SERVICE_URL:
219+
missing_vars.append('KEYCLOAK_INTERNAL_SERVICE_URL')
220+
if not KEYCLOAK_REALM:
221+
missing_vars.append('KEYCLOAK_REALM')
222+
if not KEYCLOAK_BACKEND_CLIENT_ID:
223+
missing_vars.append('KEYCLOAK_BACKEND_CLIENT_ID')
224+
if not KEYCLOAK_BACKEND_CLIENT_SECRET:
225+
missing_vars.append('KEYCLOAK_BACKEND_CLIENT_SECRET')
226+
if not KEYCLOAK_FRONTEND_CLIENT_ID:
227+
missing_vars.append('KEYCLOAK_FRONTEND_CLIENT_ID')
228+
229+
if missing_vars:
230+
error_msg = (
231+
f"OIDC is enabled (USE_OIDC=1) but the following required "
232+
f"environment variables are missing or empty: {', '.join(missing_vars)}\n"
233+
f"Please set these variables in your environment configuration."
234+
)
235+
log.error(error_msg)
236+
sys.exit(error_msg)
237+
214238
OIDC_AUTH = {
215-
'OIDC_ENDPOINT': f"{OIDC_HOST}/realms/{OIDC_REALM}",
239+
'OIDC_ENDPOINT': f"{KEYCLOAK_INTERNAL_SERVICE_URL}/realms/{KEYCLOAK_REALM}",
216240
'OIDC_CLAIMS_OPTIONS': {
217241
'aud': {
218242
'values': [
219243
'account',
220-
OIDC_BACKEND_CLIENT_ID,
221-
OIDC_FRONTEND_CLIENT_ID
244+
KEYCLOAK_BACKEND_CLIENT_ID,
245+
KEYCLOAK_FRONTEND_CLIENT_ID
222246
],
223247
'essential': True,
224248
},
225249
'iss': {
226250
'values': [
227-
f"{OIDC_HOST}/realms/{OIDC_REALM}"
251+
f"{KEYCLOAK_INTERNAL_SERVICE_URL}/realms/{KEYCLOAK_REALM}"
228252
],
229253
'essential': True,
230254
},
231255
},
232-
'USERINFO_ENDPOINT': f"{OIDC_HOST}/realms/{OIDC_REALM}/protocol/openid-connect/userinfo",
256+
'USERINFO_ENDPOINT': f"{KEYCLOAK_INTERNAL_SERVICE_URL}/realms/{KEYCLOAK_REALM}/protocol/openid-connect/userinfo",
233257
'OIDC_CREATE_USER': True,
234258
'OIDC_RESOLVE_USER_FUNCTION': 'api.oidc_utils.get_user_by_email',
235-
'OIDC_CLIENT_ID': OIDC_BACKEND_CLIENT_ID,
236-
'OIDC_CLIENT_SECRET': OIDC_BACKEND_CLIENT_SECRET,
259+
'OIDC_CLIENT_ID': KEYCLOAK_BACKEND_CLIENT_ID,
260+
'OIDC_CLIENT_SECRET': KEYCLOAK_BACKEND_CLIENT_SECRET,
237261
}
238262

239263

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
{
2-
"USE_OIDC": "${VITE_USE_OIDC}",
3-
"KEYCLOAK_URL": "${VITE_KEYCLOAK_URL}",
4-
"KEYCLOAK_REALM": "${VITE_KEYCLOAK_REALM}",
5-
"KEYCLOAK_CLIENT_ID": "${VITE_KEYCLOAK_CLIENT_ID}",
6-
"LOGOUT_REDIRECT_URI": "${VITE_LOGOUT_REDIRECT_URI}"
2+
"USE_OIDC": "${USE_OIDC}",
3+
"KEYCLOAK_URL": "${KEYCLOAK_URL}",
4+
"KEYCLOAK_REALM": "${KEYCLOAK_REALM}",
5+
"KEYCLOAK_CLIENT_ID": "${KEYCLOAK_FRONTEND_CLIENT_ID}",
6+
"KEYCLOAK_LOGOUT_REDIRECT_URI": "${KEYCLOAK_LOGOUT_REDIRECT_URI}",
7+
"KEYCLOAK_TOKEN_MIN_VALIDITY": "${KEYCLOAK_TOKEN_MIN_VALIDITY}",
8+
"KEYCLOAK_TOKEN_REFRESH_INTERVAL": "${KEYCLOAK_TOKEN_REFRESH_INTERVAL}"
79
}

medcat-trainer/webapp/frontend/src/App.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export default {
104104
105105
if (this.useOidc && this.$keycloak && this.$keycloak.authenticated) {
106106
this.$keycloak.logout({
107-
redirectUri: getRuntimeConfig().LOGOUT_REDIRECT_URI
107+
redirectUri: getRuntimeConfig().KEYCLOAK_LOGOUT_REDIRECT_URI
108108
})
109109
} else {
110110
if (this.$route.name !== 'home') {

medcat-trainer/webapp/frontend/src/auth.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ export const authPlugin = {
3030
// configure axios
3131
axios.defaults.headers.common['Authorization'] = `Bearer ${keycloak.token}`
3232

33-
const refreshInterval = Number(getRuntimeConfig().KEYCLOAK_TOKEN_REFRESH_INTERVAL) || 10000
34-
const minValidity = Number(getRuntimeConfig().KEYCLOAK_TOKEN_MIN_VALIDITY) || 30
33+
const refreshIntervalSecs = Number(getRuntimeConfig().KEYCLOAK_TOKEN_REFRESH_INTERVAL)
34+
const minValiditySecs = Number(getRuntimeConfig().KEYCLOAK_TOKEN_MIN_VALIDITY)
3535

3636
setInterval(() => {
37-
keycloak.updateToken(minValidity)
37+
keycloak.updateToken(minValiditySecs)
3838
.then(refreshed => {
3939
if (refreshed) {
4040
console.log('[AuthPlugin] Token refreshed')
@@ -44,7 +44,7 @@ export const authPlugin = {
4444
.catch(err => {
4545
console.error('[AuthPlugin] Failed to refresh token', err)
4646
})
47-
}, refreshInterval)
47+
}, (refreshIntervalSecs * 1000))
4848

4949

5050
app.config.globalProperties.$keycloak = keycloak

medcat-trainer/webapp/frontend/src/runtimeConfig.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export interface RuntimeConfig {
1010
KEYCLOAK_URL: string
1111
KEYCLOAK_REALM: string
1212
KEYCLOAK_CLIENT_ID: string
13-
LOGOUT_REDIRECT_URI: string
13+
KEYCLOAK_LOGOUT_REDIRECT_URI: string
1414
KEYCLOAK_TOKEN_MIN_VALIDITY: number
1515
KEYCLOAK_TOKEN_REFRESH_INTERVAL: number
1616
}
@@ -31,7 +31,7 @@ const DEFAULT_CONFIG: RuntimeConfig = {
3131
KEYCLOAK_URL: '',
3232
KEYCLOAK_REALM: '',
3333
KEYCLOAK_CLIENT_ID: '',
34-
LOGOUT_REDIRECT_URI: '',
34+
KEYCLOAK_LOGOUT_REDIRECT_URI: '',
3535
KEYCLOAK_TOKEN_MIN_VALIDITY: 0,
3636
KEYCLOAK_TOKEN_REFRESH_INTERVAL: 0
3737
};

medcat-trainer/webapp/scripts/load_examples.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def main(port=8001,
6464

6565
use_oidc = os.environ.get('USE_OIDC')
6666
logger.info('Checking for environment variable USE_OIDC...')
67-
if use_oidc is not None and use_oidc in ('1', 'true', 't', 'y'):
67+
if use_oidc is not None and use_oidc in '1':
6868
logger.info('Found environment variable USE_OIDC is set to truthy value. Will load data using JWT')
6969
token = get_keycloak_access_token()
7070
headers = {

medcat-trainer/webapp/scripts/nginx-entrypoint.sh

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,40 @@ set -e
33

44
echo "Generating runtime config.json from template..."
55

6-
# Set VITE_USE_OIDC to 0 if not provided (traditional auth mode)
7-
export VITE_USE_OIDC="${VITE_USE_OIDC:-0}"
6+
# Set USE_OIDC to 0 if not provided (traditional auth mode)
7+
export USE_OIDC="${USE_OIDC:-0}"
88

9-
# If OIDC is enabled, require all OIDC-related variables
10-
if [ "$VITE_USE_OIDC" = "1" ]; then
9+
# If OIDC is enabled, validate required variables
10+
if [ "$USE_OIDC" = "1" ]; then
1111
echo "OIDC mode enabled - validating OIDC environment variables..."
1212

13-
if [ -z "$VITE_KEYCLOAK_URL" ]; then
14-
echo "ERROR: VITE_KEYCLOAK_URL environment variable is required when VITE_USE_OIDC=1"
13+
if [ -z "$KEYCLOAK_URL" ]; then
14+
echo "ERROR: KEYCLOAK_URL is required when USE_OIDC=1"
1515
exit 1
1616
fi
1717

18-
if [ -z "$VITE_KEYCLOAK_REALM" ]; then
19-
echo "ERROR: VITE_KEYCLOAK_REALM environment variable is required when VITE_USE_OIDC=1"
18+
if [ -z "$KEYCLOAK_REALM" ]; then
19+
echo "ERROR: KEYCLOAK_REALM is required when USE_OIDC=1"
2020
exit 1
2121
fi
2222

23-
if [ -z "$VITE_KEYCLOAK_CLIENT_ID" ]; then
24-
echo "ERROR: VITE_KEYCLOAK_CLIENT_ID environment variable is required when VITE_USE_OIDC=1"
23+
if [ -z "$KEYCLOAK_FRONTEND_CLIENT_ID" ]; then
24+
echo "ERROR: KEYCLOAK_FRONTEND_CLIENT_ID is required when USE_OIDC=1"
2525
exit 1
2626
fi
2727

28-
if [ -z "$VITE_LOGOUT_REDIRECT_URI" ]; then
29-
echo "ERROR: VITE_LOGOUT_REDIRECT_URI environment variable is required when VITE_USE_OIDC=1"
28+
if [ -z "$KEYCLOAK_LOGOUT_REDIRECT_URI" ]; then
29+
echo "ERROR: KEYCLOAK_LOGOUT_REDIRECT_URI is required when USE_OIDC=1"
3030
exit 1
3131
fi
3232

3333
else
34-
echo "Traditional auth mode enabled (VITE_USE_OIDC=0)"
35-
# Traditional auth mode - set defaults for unused variables
36-
export VITE_KEYCLOAK_URL="${VITE_KEYCLOAK_URL:-http://localhost}"
37-
export VITE_KEYCLOAK_REALM="${VITE_KEYCLOAK_REALM:-default}"
38-
export VITE_KEYCLOAK_CLIENT_ID="${VITE_KEYCLOAK_CLIENT_ID:-medcattrainer}"
39-
export VITE_LOGOUT_REDIRECT_URI="${VITE_LOGOUT_REDIRECT_URI:-/}"
34+
echo "Traditional auth mode enabled (USE_OIDC=0)"
35+
# Set Defaults
36+
export KEYCLOAK_URL="${KEYCLOAK_URL:-}"
37+
export KEYCLOAK_REALM="${KEYCLOAK_REALM:-}"
38+
export KEYCLOAK_LOGOUT_REDIRECT_URI="${KEYCLOAK_LOGOUT_REDIRECT_URI:-}"
39+
export KEYCLOAK_FRONTEND_CLIENT_ID="${KEYCLOAK_FRONTEND_CLIENT_ID:-}"
4040
fi
4141

4242
# Check if template exists
@@ -46,6 +46,12 @@ if [ ! -f "$TEMPLATE_FILE" ]; then
4646
exit 1
4747
fi
4848

49+
# Set token refresh settings with sensible defaults
50+
# KEYCLOAK_TOKEN_MIN_VALIDITY: Refresh token if it expires in less than this many seconds (default: 30s)
51+
# KEYCLOAK_TOKEN_REFRESH_INTERVAL: Check token validity every N seconds (default: 20s)
52+
export KEYCLOAK_TOKEN_MIN_VALIDITY="${KEYCLOAK_TOKEN_MIN_VALIDITY:-30}"
53+
export KEYCLOAK_TOKEN_REFRESH_INTERVAL="${KEYCLOAK_TOKEN_REFRESH_INTERVAL:-20}"
54+
4955
# Generate config.json from template
5056
envsubst < "$TEMPLATE_FILE" > /home/frontend/dist/config.json
5157

0 commit comments

Comments
 (0)