8.7 KiB
Keycloak Setup — BD FHIR National
Realm: hris
Keycloak URL: https://auth.dghs.gov.bd
Audience: DGHS Identity and Access Management team
Overview
This document covers the Keycloak configuration required for BD FHIR National
deployment. It assumes the hris realm and mci-api role already exist
(pre-existing national HRIS configuration). Only the additions for FHIR
deployment are documented here.
Part 1 — Create fhir-admin realm role
The fhir-admin role grants access to:
DELETE /admin/terminology/cache— terminology cache flushGET /admin/terminology/cache/stats— cache statistics
This role is not assigned to vendor clients. It is assigned only to the ICD-11 version upgrade pipeline service account and DGHS system administrators.
Steps (Keycloak Admin Console)
- Log in to
https://auth.dghs.gov.bd/admin/master/console - Select realm: hris
- Navigate to: Realm roles → Create role
- Fill in:
- Role name:
fhir-admin - Description:
BD FHIR server administrative operations — cache management and system configuration
- Role name:
- Click Save
Steps (Keycloak Admin REST API — for automation)
# Get admin token
ADMIN_TOKEN=$(curl -s -X POST \
"https://auth.dghs.gov.bd/realms/master/protocol/openid-connect/token" \
-d "grant_type=password" \
-d "client_id=admin-cli" \
-d "username=${KEYCLOAK_ADMIN_USER}" \
-d "password=${KEYCLOAK_ADMIN_PASSWORD}" \
| jq -r '.access_token')
# Create fhir-admin role
curl -s -X POST \
"https://auth.dghs.gov.bd/admin/realms/hris/roles" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "fhir-admin",
"description": "BD FHIR server administrative operations"
}'
Part 2 — Create fhir-admin service account client
The version upgrade pipeline authenticates with a dedicated client. This client must never be shared with vendor systems.
Steps (Admin Console)
- Navigate to: Clients → Create client
- Client type: OpenID Connect
- Client ID:
fhir-admin-pipeline - Click Next
- Client authentication: ON (confidential client)
- Service accounts roles: ON
- Standard flow: OFF (machine-to-machine only)
- Click Save
Assign fhir-admin role to service account
- Navigate to: Clients →
fhir-admin-pipeline→ Service accounts roles - Click Assign role
- Filter by: Filter by realm roles
- Select:
fhir-admin - Click Assign
Retrieve client secret
- Navigate to: Clients →
fhir-admin-pipeline→ Credentials - Copy Client secret — store in your secrets vault
- This secret is used in
ops/version-upgrade-integration.md
Part 3 — Configure vendor clients
Each vendor organisation requires one Keycloak client. This section documents the template for creating a vendor client. Repeat for each vendor.
Naming convention
fhir-vendor-{organisation-id}
Where {organisation-id} is the DGHS facility code, e.g.:
fhir-vendor-DGHS-FAC-001for Dhaka Medical College Hospitalfhir-vendor-DGHS-FAC-002for Square Hospital
Steps (Admin Console)
- Navigate to: Clients → Create client
- Client type: OpenID Connect
- Client ID:
fhir-vendor-{organisation-id} - Click Next
- Client authentication: ON
- Service accounts roles: ON
- Standard flow: OFF
- Click Save
Assign mci-api role
- Navigate to: Clients →
fhir-vendor-{org-id}→ Service accounts roles - Click Assign role
- Select:
mci-api - Click Assign
Add sending_facility user attribute
The sending_facility claim is a custom token mapper that injects the vendor's
DGHS facility code into every token issued to this client. The
KeycloakJwtInterceptor reads this claim for audit logging.
Without this mapper, audit logs will show client_id as the facility
identifier instead of the DGHS facility code. This degrades audit quality
and generates WARN logs in HAPI on every submission.
Create user attribute on service account
- Navigate to: Clients →
fhir-vendor-{org-id}→ Service accounts - Click the service account user link (e.g.,
service-account-fhir-vendor-xxx) - Navigate to: Attributes tab
- Click Add attribute
- Key:
sending_facility - Value:
{DGHS facility code}(e.g.,DGHS-FAC-001) - Click Save
Create token mapper
- Navigate to: Clients →
fhir-vendor-{org-id}→ Client scopes - Click the dedicated scope link (e.g.,
fhir-vendor-xxx-dedicated) - Navigate to: Mappers → Add mapper → By configuration
- Select: User Attribute
- Fill in:
- Name:
sending-facility-mapper - User Attribute:
sending_facility - Token Claim Name:
sending_facility - Claim JSON Type: String
- Add to access token: ON
- Add to ID token: OFF
- Add to userinfo: OFF
- Name:
- Click Save
Verify token contains sending_facility
# Get vendor token
TOKEN=$(curl -s -X POST \
"https://auth.dghs.gov.bd/realms/hris/protocol/openid-connect/token" \
-d "grant_type=client_credentials" \
-d "client_id=fhir-vendor-{org-id}" \
-d "client_secret={secret}" \
| jq -r '.access_token')
# Decode and check claims (base64 decode middle segment)
echo $TOKEN | cut -d. -f2 | base64 -d 2>/dev/null | jq '{
iss,
sub,
azp,
exp,
sending_facility,
realm_access: .realm_access.roles
}'
# Expected output:
# {
# "iss": "https://auth.dghs.gov.bd/realms/hris",
# "sub": "...",
# "azp": "fhir-vendor-{org-id}",
# "exp": ...,
# "sending_facility": "DGHS-FAC-001",
# "realm_access": ["mci-api", "offline_access"]
# }
Part 4 — Token validation verification
After creating a client, verify the full token validation chain works before onboarding the vendor.
Test 1 — Valid token accepted
TOKEN=$(curl -s -X POST \
"https://auth.dghs.gov.bd/realms/hris/protocol/openid-connect/token" \
-d "grant_type=client_credentials" \
-d "client_id=fhir-vendor-{org-id}" \
-d "client_secret={secret}" \
| jq -r '.access_token')
curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer ${TOKEN}" \
https://fhir.dghs.gov.bd/fhir/Patient
# Expected: 200 (empty bundle) or 404 — NOT 401
Test 2 — Missing token rejected
curl -s -o /dev/null -w "%{http_code}" \
https://fhir.dghs.gov.bd/fhir/Patient
# Expected: 401
Test 3 — Expired token rejected
# Use a deliberately expired token (exp in the past)
# Easiest: wait for a token to expire (default Keycloak token lifetime: 5 minutes)
# Then attempt a request with the expired token.
# Expected: 401
Test 4 — Wrong realm rejected
# Get a token from a different realm (if available) or forge iss claim
# Expected: 401
Test 5 — mci-api role required
# Create a test client WITHOUT mci-api role
# Get token for that client
# Attempt FHIR request
# Expected: 401
Test 6 — fhir-admin endpoint requires fhir-admin role
# Use a vendor token (mci-api only, no fhir-admin)
VENDOR_TOKEN=...
curl -s -w "\n%{http_code}" \
-X DELETE \
-H "Authorization: Bearer ${VENDOR_TOKEN}" \
https://fhir.dghs.gov.bd/admin/terminology/cache
# Expected: 403
# Use fhir-admin token
ADMIN_TOKEN=$(curl -s -X POST \
"https://auth.dghs.gov.bd/realms/hris/protocol/openid-connect/token" \
-d "grant_type=client_credentials" \
-d "client_id=fhir-admin-pipeline" \
-d "client_secret={admin_secret}" \
| jq -r '.access_token')
curl -s -w "\n%{http_code}" \
-X DELETE \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
https://fhir.dghs.gov.bd/admin/terminology/cache
# Expected: 200 with flush summary JSON
Part 5 — Token lifetime configuration
Keycloak default access token lifetime is 5 minutes. For machine-to-machine FHIR submissions, this is appropriate — vendor systems must refresh tokens before expiry. Do not increase the token lifetime to accommodate vendors who are not refreshing tokens correctly. Token refresh is the vendor's responsibility, not a server-side workaround.
Recommended settings for vendor clients:
| Setting | Value | Rationale |
|---|---|---|
| Access Token Lifespan | 5 minutes | Short-lived — minimises window for token replay |
| Refresh Token Max Reuse | 0 | One-time use refresh tokens |
| Client Session Idle | 30 minutes | Vendor batch jobs may pause between submissions |
| Client Session Max | 8 hours | Maximum session for a single batch run |
Configure at: Realm Settings → Tokens for defaults,
or per-client at: Clients → {client} → Advanced → Advanced settings.