Configuration
Environment variables
| Variable | Default | Required in prod | Description |
|---|---|---|---|
SMART_CLIENT_ID | aj-fhir-immunization | ✓ | The client_id registered on the auth server |
SMART_REDIRECT_URI | http://localhost:8084/callback | ✓ | Must match the auth server registration exactly |
AUTH_SERVER_URL | http://localhost:9000 | ✓ | Auth server base URL — used for SMART discovery |
FHIR_BASE_URL | http://localhost:8080/fhir | ✓ | HAPI FHIR JPA base URL |
No other environment variables are required. All remaining properties have sensible defaults.
Full application.yml reference
immunization:
# OAuth2 client registration
client-id: ${SMART_CLIENT_ID:aj-fhir-immunization}
redirect-uri: ${SMART_REDIRECT_URI:http://localhost:8084/callback}
# SMART scopes requested on every launch
scopes: >
launch openid fhirUser
patient/Patient.rs
patient/Immunization.rs
patient/ImmunizationRecommendation.rs
# Service URLs
auth-server-url: ${AUTH_SERVER_URL:http://localhost:9000}
fhir-base-url: ${FHIR_BASE_URL:http://localhost:8080/fhir}
# Proactive token refresh: fires when less than this many seconds remain
token-refresh-buffer-seconds: 120
# SMART discovery cache TTL per ISS — avoids repeated calls to /.well-known/
discovery-cache-ttl-seconds: 600
# WHO schedule resource path (bundled JSON, used by future schedule advisor)
who-schedule-path: classpath:who-vaccine-schedule.json
Scopes
The app always requests exactly these six scopes and no others:
| Scope | Purpose |
|---|---|
launch | EHR launch context — receives the launch token from the portal |
openid | OIDC — enables id_token issuance |
fhirUser | Clinician Practitioner reference in id_token |
patient/Patient.rs | Read patient demographics for the certificate header |
patient/Immunization.rs | Read vaccination history |
patient/ImmunizationRecommendation.rs | Read forecast / due dates |
If the patient has a Consent Manager record that does not cover patient/Immunization.rs, the token exchange succeeds but HAPI FHIR returns 403 on the Immunization query — the Consent Manager's scope interceptor blocks the request. The app handles this gracefully by returning an empty vaccination list.
Caching
application.yml configures a Caffeine cache:
spring:
cache:
type: caffeine
caffeine:
spec: maximumSize=500,expireAfterWrite=600s
Five separate cache regions are used:
| Cache name | Key | TTL | What it stores |
|---|---|---|---|
immunizations | patientId | 600 s | List<VaccinationRecord> |
recommendations | patientId | 600 s | List<VaccineRecommendation> |
patient | patientId | 600 s | HAPI Patient resource |
smartDiscovery | iss | 600 s | SMART configuration document |
The dashboard, history, schedule, and certificate pages all share the same cache — navigating between views does not trigger additional HAPI FHIR requests within a 10-minute window.
Session
spring:
session:
timeout: 3600s
The HTTP session timeout matches the access token TTL. For Redis-backed sessions (multi-instance deployments), add:
spring:
session:
store-type: redis
data:
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
All session objects implement Serializable with pinned serialVersionUID.
Test profile
Activate with -Dspring.profiles.active=test or @ActiveProfiles("test"). The test profile:
- Points
auth-server-urlathttp://localhost:9999(WireMock) - Points
fhir-base-urlathttp://localhost:9998/fhir(WireMock) - Disables Thymeleaf template cache
- Uses
test-immunization-clientas the client ID
Next: Platform Registration →