Endpoints
Endpoint overviewโ
| Endpoint | Auth | Description |
|---|---|---|
GET /.well-known/smart-configuration | None | SMART discovery document |
GET /oauth2/authorize | None (shows login) | Authorization endpoint โ starts OAuth2 flow |
POST /oauth2/token | None | Token exchange โ returns access_token + SMART extras |
GET /oauth2/jwks | None | Public JWKS for JWT signature verification |
POST /oauth2/revoke | None | Token revocation |
GET /portal | Clinician login | Patient picker UI |
POST /portal/launch | Clinician login | Create launch token + redirect to SMART client |
GET /login | None | Clinician login page |
GET /actuator/health | None | Health probe |
GET /h2-console/** | None (dev only) | H2 database console |
GET /.well-known/smart-configurationโ
Returns the SMART discovery document. Publicly accessible โ SMART clients fetch this before any authentication.
Response 200 OK:
{
"authorization_endpoint": "http://localhost:9000/oauth2/authorize",
"token_endpoint": "http://localhost:9000/oauth2/token",
"token_endpoint_auth_methods_supported": ["none"],
"jwks_uri": "http://localhost:9000/oauth2/jwks",
"issuer": "http://localhost:9000",
"scopes_supported": [
"launch", "launch/patient", "openid", "fhirUser", "offline_access",
"patient/Patient.rs", "patient/Condition.rs",
"patient/MedicationRequest.rs", "patient/Observation.rs",
"patient/AllergyIntolerance.rs", "patient/Encounter.rs"
],
"response_types_supported": ["code"],
"capabilities": [
"launch-ehr", "launch-standalone", "client-public",
"context-ehr-patient", "context-ehr-encounter",
"permission-patient", "permission-user",
"sso-openid-connect"
],
"code_challenge_methods_supported": ["S256"]
}
GET /oauth2/authorizeโ
Starts the OAuth2 authorization code flow. SMART clients are redirected here after the discovery step.
Query parameters (all sent by the SMART client automatically):
| Parameter | Required | Description |
|---|---|---|
response_type | Yes | Must be code |
client_id | Yes | Registered app client ID (ajfhir-smart-client) |
redirect_uri | Yes | Must match registration (http://localhost:8080/callback) |
scope | Yes | Space-separated SMART scopes |
state | Yes | CSRF nonce โ 32-byte random |
launch | EHR launch | Opaque launch token created at /portal/launch |
aud | Yes | Must equal the ISS โ validated by Spring Auth Server |
code_challenge | Yes | BASE64URL(SHA-256(verifier)) |
code_challenge_method | Yes | Must be S256 |
Response: Redirects to the login page if not authenticated, then redirects to redirect_uri?code=AUTH_CODE&state=NONCE.
POST /oauth2/tokenโ
Exchanges the authorization code for tokens. Called by the SMART client's SmartTokenService.
Request body (form-encoded):
grant_type=authorization_code
&code=AUTH_CODE
&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fcallback
&client_id=ajfhir-smart-client
&code_verifier=96_BYTE_RANDOM_VERIFIER
Response 200 OK:
{
"access_token": "eyJhbGciOiJSUzI1NiJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "launch openid fhirUser patient/Patient.rs ...",
"refresh_token": "eyJhbGciOiJSUzI1NiJ9...",
"patient": "ePatient-ABC123",
"encounter": "eEnc-789",
"need_patient_banner": true,
"id_token": "eyJhbGciOiJSUzI1NiJ9..."
}
| Field | When present | Description |
|---|---|---|
access_token | Always | JWT bearer token for FHIR API calls |
token_type | Always | Always Bearer |
expires_in | Always | Seconds until expiry (default 3600) |
scope | Always | Granted scopes (may be narrower than requested) |
refresh_token | Always | For proactive token refresh |
patient | EHR launch | FHIR Patient resource ID โ null for standalone without patient picker |
encounter | If bound | FHIR Encounter resource ID โ null if no encounter was selected |
need_patient_banner | Always | true = SMART client must render a patient header |
id_token | openid scope | RS256-signed OIDC identity token |
GET /oauth2/jwksโ
Returns the public JWKS for JWT signature verification.
Response 200 OK:
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"n": "...base64url-encoded modulus...",
"kid": "uuid-of-the-key",
"use": "sig",
"alg": "RS256"
}
]
}
Only the public key components are returned โ never the private exponent (d, p, q).
The SMART client's IdTokenValidator fetches this endpoint to verify that the signing key exists before accepting an id_token.
GET /portalโ
Clinician-facing patient picker UI. Requires clinician login.
Fetches patients from the HAPI FHIR server (smart.server.fhir-base-url) using HAPI client directly.
Query parameters:
| Parameter | Required | Description |
|---|---|---|
search | No | Patient name search filter |
POST /portal/launchโ
Creates a launch token and redirects the clinician's browser to the SMART client.
Form parameters:
| Parameter | Required | Description |
|---|---|---|
patientId | Yes | FHIR Patient resource ID |
encounterId | No | FHIR Encounter resource ID |
Response: 302 redirect to:
http://localhost:8080/launch
?iss=http://localhost:8080/fhir
&launch=OPAQUE_TOKEN
Error responsesโ
| Status | Cause |
|---|---|
400 invalid_grant | Authorization code already used, expired, or PKCE verifier doesn't match |
400 invalid_client | client_id not registered or PKCE required but code_challenge missing |
400 invalid_request | Missing required parameters |
401 | Launch token not found, expired, or already used |
403 | Scope enforcement โ FHIR request lacks required scope |
502 | Auth server could not reach HAPI FHIR for patient list |