Security & Authentication
FHIR ANT includes a full authentication and authorization system. In Dev Mode (the default for testing), authentication is bypassed. When Dev Mode is off, all non-public endpoints require a valid JWT token.
Dev Mode
When enabled, all requests are accepted without authentication. A synthetic admin user is injected into every request so that audit trails and scope enforcement still function internally.
Dev Mode is for testing only. A warning banner appears in the app when it's active.
Authentication Flow
Registration & Login
# Register a new user (first user becomes admin)
curl -X POST http://server:8080/auth/register \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"mysecurepassword"}'
# Login
curl -X POST http://server:8080/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"mysecurepassword"}'
# Returns: {"token": "eyJ...", "refresh_token": "eyJ...", ...}
Using Tokens
# Use the access token for authenticated requests
curl -H "Authorization: Bearer eyJ..." http://server:8080/Patient
Token Refresh
curl -X POST http://server:8080/auth/token \
-H "Content-Type: application/json" \
-d '{"grant_type":"refresh_token","refresh_token":"eyJ..."}'
OAuth 2.0 Authorization Code Flow (PKCE)
FHIR ANT supports the full OAuth 2.0 authorization code flow with PKCE, suitable for SMART on FHIR applications:
GET /auth/authorize— Authorization endpointPOST /auth/authorize— Submit authorization (JSON or form-encoded)POST /auth/token— Exchange code for tokens (withcode_verifier)
Token Revocation (RFC 7009)
# Revoke a token
curl -X POST http://server:8080/auth/revoke \
-H "Content-Type: application/json" \
-d '{"token":"eyJ..."}'
# Logout (revokes access token from Authorization header)
curl -X POST http://server:8080/auth/logout \
-H "Authorization: Bearer eyJ..."
SMART on FHIR Scopes
FHIR ANT enforces SMART v2 scope syntax: context/resourceType.permissions
| Context | Example | Description |
|---|---|---|
user | user/Patient.rs | User-level read+search on Patient |
patient | patient/Observation.cruds | Patient-level full access |
system | system/*.* | System-level full access (admin) |
Permissions: c (create), r (read), u (update), d (delete), s (search)
Default Scopes by Role
| Role | Default Scopes |
|---|---|
admin | system/*.* |
clinician | user/*.* |
readonly | user/*.rs |
Password Policy
Follows NIST 800-63B guidelines:
- Minimum 12 characters, maximum 128
- No complexity rules (length > complexity)
- Blocked common passwords (e.g., "password123456")
- Case-insensitive blocklist matching
Account Lockout
- Account locks after 5 failed login attempts
- Auto-unlock after 15 minutes
- Admin can manually unlock via
POST /admin/unlock/<userId> - Returns HTTP 423 (Locked) when account is locked
Database Encryption
The SQLite database is encrypted with SQLCipher. The encryption key is generated on first launch and stored in the device's secure keystore (Android Keystore / iOS Keychain). Data at rest is always encrypted.
Audit Trail
All requests are logged with timestamp, method, path, status code, duration, client IP, and authenticated user (if present).