Authentication
OctoFHIR implements SMART on FHIR authorization based on OAuth 2.0 and OpenID Connect.
Discovery Endpoints
Section titled “Discovery Endpoints”| Endpoint | Description |
|---|---|
GET /.well-known/smart-configuration | SMART configuration metadata |
GET /.well-known/jwks.json | Server’s public keys for JWT verification |
OAuth Endpoints
Section titled “OAuth Endpoints”| Endpoint | Description |
|---|---|
GET /auth/authorize | Authorization endpoint (interactive user login & consent) |
POST /auth/authorize | Authorization form submission (login/consent actions) |
POST /auth/token | Token endpoint (exchange codes, refresh tokens) |
POST /auth/revoke | Token revocation (RFC 7009) |
POST /auth/introspect | Token introspection (RFC 7662) |
GET /auth/userinfo | OpenID Connect UserInfo endpoint |
POST /auth/logout | Browser-based logout (revokes token, clears cookie) |
Supported Grant Types
Section titled “Supported Grant Types”authorization_code- Standard OAuth flow with PKCEclient_credentials- Machine-to-machine (system scope)refresh_token- Refresh access tokens
Client Types
Section titled “Client Types”| Type | Authentication | PKCE Requirement | Use Case |
|---|---|---|---|
| Public | PKCE only | Required (RFC 8252) | Browser apps, mobile apps |
| Confidential (symmetric) | client_secret | Recommended (RFC 9207) | Server-side apps |
| Confidential (asymmetric) | private_key_jwt | Recommended (RFC 9207) | Bulk Data, secure backends |
PKCE Requirements
Section titled “PKCE Requirements”OctoFHIR enforces PKCE requirements according to OAuth 2.0 security best practices:
- Public clients (
confidential: false): PKCE is REQUIRED. Authorization requests withoutcode_challengeandcode_challenge_methodwill be rejected. - Confidential clients (
confidential: true): PKCE is RECOMMENDED but optional. A warning is logged if PKCE is not used. - Only
S256challenge method is supported (SHA-256). Theplainmethod is forbidden per SMART on FHIR security requirements.
SMART Scopes
Section titled “SMART Scopes”Launch Scopes
Section titled “Launch Scopes”launch- EHR launch (receive context from EHR)launch/patient- Standalone launch with patient pickerlaunch/encounter- Standalone launch with encounter picker
Resource Scopes
Section titled “Resource Scopes”Format: {context}/{resourceType}.{permissions}
- Context:
patient,user,system - Permissions:
c(create),r(read),u(update),d(delete),s(search)
Examples:
patient/Patient.rs # Read/search Patient in patient contextuser/Observation.cruds # Full access to Observations for logged-in usersystem/*.rs # Read/search all resources (backend service)Identity Scopes
Section titled “Identity Scopes”openid- Enable OpenID Connect (required for/userinfo)fhirUser- Include FHIR resource reference in token/userinfo
Configuration
Section titled “Configuration”All auth settings are in octofhir.toml under [auth]:
[auth]enabled = trueissuer = "http://localhost:8888"
[auth.oauth]access_token_lifetime = "1h"refresh_token_lifetime = "90d"grant_types = ["authorization_code", "client_credentials", "refresh_token"]
[auth.smart]launch_ehr_enabled = truelaunch_standalone_enabled = truepublic_clients_allowed = trueopenid_enabled = truesupported_scopes = ["openid", "fhirUser", "patient/*.cruds", "..."]
[auth.signing]algorithm = "RS384" # RS256, RS384, or ES384key_rotation_days = 90See octofhir.toml for the full configuration reference including:
[auth.policy]- Access policy engine settings[auth.federation]- External IdP integration[auth.rate_limiting]- Request rate limits[auth.audit]- Audit logging options
Example: Authorization Code Flow
Section titled “Example: Authorization Code Flow”# 1. Redirect user to authorizeopen "http://localhost:8888/auth/authorize?\client_id=my-app&\redirect_uri=http://localhost:3000/callback&\response_type=code&\scope=openid%20fhirUser%20patient/Patient.rs&\state=random-state&\code_challenge=BASE64URL_CHALLENGE&\code_challenge_method=S256"
# 2. Exchange code for tokenscurl -X POST http://localhost:8888/auth/token \ -d "grant_type=authorization_code" \ -d "code=AUTHORIZATION_CODE" \ -d "redirect_uri=http://localhost:3000/callback" \ -d "client_id=my-app" \ -d "code_verifier=ORIGINAL_VERIFIER"
# 3. Use access tokencurl -H "Authorization: Bearer ACCESS_TOKEN" \ http://localhost:8888/PatientExample: Client Credentials Flow
Section titled “Example: Client Credentials Flow”curl -X POST http://localhost:8888/auth/token \ -u "client_id:client_secret" \ -d "grant_type=client_credentials" \ -d "scope=system/Patient.rs"Interactive Authorization Flow
Section titled “Interactive Authorization Flow”The /auth/authorize endpoint provides a server-rendered user interface for authentication and consent. This enables standard OAuth 2.0 authorization code flow with a user-friendly login experience.
Flow Steps
Section titled “Flow Steps”- Initial Request - Client redirects user to
/auth/authorizewith OAuth parameters - Authentication - User sees a login form and enters credentials
- Consent - After successful login, user sees requested scopes and can approve/deny
- Authorization Code - User is redirected back to client with authorization code
- Token Exchange - Client exchanges code for access token at
/auth/token
Features
Section titled “Features”- Server-rendered UI with modern glassmorphism design matching OctoFHIR’s aesthetics
- Session management via secure HTTP-only cookies
- Consent tracking - remembers user’s previous consent decisions
- PKCE validation - enforced based on client type (public vs confidential)
- Error handling - user-friendly error pages with detailed diagnostics
Authorization Request Parameters
Section titled “Authorization Request Parameters”| Parameter | Required | Description |
|---|---|---|
response_type | Yes | Must be code |
client_id | Yes | Client identifier |
redirect_uri | Yes | Must match registered redirect URI |
scope | Yes | Requested scopes (space-separated) |
state | Yes | CSRF protection (min 122 bits entropy) |
code_challenge | Conditional | Required for public clients |
code_challenge_method | Conditional | Must be S256 if provided |
aud | Yes (SMART) | FHIR server base URL |
launch | Optional | EHR launch context identifier |
nonce | Optional | OpenID Connect replay protection |
Session Cookie
Section titled “Session Cookie”The authorization flow uses a session cookie (oauth_session) to track the user’s authentication state:
- Name:
oauth_session - Path:
/auth - Lifetime: 10 minutes
- Attributes:
HttpOnly,SameSite=Lax,Secure(in production)
User Consent Storage
Section titled “User Consent Storage”User consent decisions are stored in the database (octofhir_auth.user_consents table). When a user approves access for a client with specific scopes, subsequent authorization requests with the same scopes skip the consent screen for better UX.