Skip to main content

Endpoints

Endpoint overviewโ€‹

EndpointAuthDescription
GET /.well-known/smart-configurationNoneSMART discovery document
GET /oauth2/authorizeNone (shows login)Authorization endpoint โ€” starts OAuth2 flow
POST /oauth2/tokenNoneToken exchange โ€” returns access_token + SMART extras
GET /oauth2/jwksNonePublic JWKS for JWT signature verification
POST /oauth2/revokeNoneToken revocation
GET /portalClinician loginPatient picker UI
POST /portal/launchClinician loginCreate launch token + redirect to SMART client
GET /loginNoneClinician login page
GET /actuator/healthNoneHealth 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):

ParameterRequiredDescription
response_typeYesMust be code
client_idYesRegistered app client ID (ajfhir-smart-client)
redirect_uriYesMust match registration (http://localhost:8080/callback)
scopeYesSpace-separated SMART scopes
stateYesCSRF nonce โ€” 32-byte random
launchEHR launchOpaque launch token created at /portal/launch
audYesMust equal the ISS โ€” validated by Spring Auth Server
code_challengeYesBASE64URL(SHA-256(verifier))
code_challenge_methodYesMust 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..."
}
FieldWhen presentDescription
access_tokenAlwaysJWT bearer token for FHIR API calls
token_typeAlwaysAlways Bearer
expires_inAlwaysSeconds until expiry (default 3600)
scopeAlwaysGranted scopes (may be narrower than requested)
refresh_tokenAlwaysFor proactive token refresh
patientEHR launchFHIR Patient resource ID โ€” null for standalone without patient picker
encounterIf boundFHIR Encounter resource ID โ€” null if no encounter was selected
need_patient_bannerAlwaystrue = SMART client must render a patient header
id_tokenopenid scopeRS256-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:

ParameterRequiredDescription
searchNoPatient name search filter

POST /portal/launchโ€‹

Creates a launch token and redirects the clinician's browser to the SMART client.

Form parameters:

ParameterRequiredDescription
patientIdYesFHIR Patient resource ID
encounterIdNoFHIR Encounter resource ID

Response: 302 redirect to:

http://localhost:8080/launch
?iss=http://localhost:8080/fhir
&launch=OPAQUE_TOKEN

Error responsesโ€‹

StatusCause
400 invalid_grantAuthorization code already used, expired, or PKCE verifier doesn't match
400 invalid_clientclient_id not registered or PKCE required but code_challenge missing
400 invalid_requestMissing required parameters
401Launch token not found, expired, or already used
403Scope enforcement โ€” FHIR request lacks required scope
502Auth server could not reach HAPI FHIR for patient list