Client Credentials Grant
The Client Credentials grant is designed for machine-to-machine (M2M) authentication where no user interaction is required. This flow is used when applications need to access their own resources or when acting on behalf of themselves rather than a user.
Overview of Client Credentials Grant
The Client Credentials flow is the simplest OAuth 2.0 grant type, designed for confidential clients that can securely store credentials. Unlike user-centric flows, there is no user authentication or consent step—the application authenticates itself directly with the authorization server.
Use Cases:
- Backend Services: Server-to-server communication between microservices
- Batch Jobs: Automated processes that run on a schedule (ETL pipelines, data synchronization)
- CLI Tools: Command-line applications used by developers or administrators
- Daemon Applications: Background services that don't interact with users
- System Integrations: Applications that need to access FHIR resources for administrative purposes
Why Use Client Credentials: Client Credentials is appropriate when the application itself is the resource owner, not acting on behalf of a specific user. The tokens issued are tied to the application's identity and permissions, not to any individual user.
Key Benefits:
- Simple Authentication Flow: Single request to obtain tokens, no redirects or user interaction required
- High Security: Requires secure storage of client credentials on the backend server
- Suitable for Automation: Perfect for scheduled jobs and automated workflows
- No User Context: Access resources based on application permissions, not user permissions
- Efficient: Minimal overhead compared to user-interactive flows
Client Credentials Flow
Flow Steps in Detail
Step 1: Token Request
The application directly requests an access token from the token endpoint using its client credentials:
POST /w/{tenant}/oauth/api/v1/token HTTP/1.1
Host: api.haste.health
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&
client_id=backend-service-id&
client_secret=your-client-secret&
scope=system/*.read system/*.write
Parameters:
| Parameter | Required | Description |
|---|---|---|
grant_type | Yes | Must be client_credentials |
client_id | Yes* | Your application's client identifier (*if not using Basic Auth header) |
client_secret | Yes* | Your application's client secret (*if not using Basic Auth header) |
scope | Optional | Space-delimited list of requested scopes (defaults to pre-configured scopes) |
Scope Format for System Access:
system/*.read- Read access to all resource typessystem/*.write- Write access to all resource typessystem/Patient.read- Read access to Patient resources onlysystem/Observation.write- Write access to Observation resources only
Step 2: Token Response
The authorization server validates the client credentials and returns an access token:
Success Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "system/*.read system/*.write"
}
Response Fields:
| Field | Description |
|---|---|
access_token | JWT token to use for API requests |
token_type | Always Bearer for OAuth 2.0 |
expires_in | Token lifetime in seconds (typically 3600 = 1 hour) |
scope | Granted scopes (may differ from requested if some were denied) |
Note: Client Credentials flow does not return:
refresh_token- Not needed; request new token when current expires
Step 3: Access Protected Resources
Use the access token to call FHIR APIs:
Request:
GET /w/{tenant}/{project}/api/v1/fhir/r4/Patient HTTP/1.1
Host: api.haste.health
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
Step 4: Token Expiration and Renewal
Since Client Credentials tokens don't include refresh tokens, request a new access token before the current one expires:
Error Handling
Token Request Errors
| Error Code | Description | Resolution |
|---|---|---|
invalid_request | Missing or invalid required parameter | Check grant_type, client_id, and client_secret |
invalid_client | Client authentication failed | Verify client_id and client_secret are correct |
invalid_scope | Requested scope is invalid or not authorized | Request only authorized scopes for your client |
unauthorized_client | Client not authorized for client credentials grant | Contact administrator to enable this grant type |
unsupported_grant_type | Grant type not supported | Use grant_type=client_credentials |
server_error | Authorization server error | Retry with exponential backoff |
Example Error Response
{
"error": "invalid_client",
"error_description": "Client authentication failed. Invalid client_secret provided.",
"error_uri": "https://api.haste.health/errors/invalid_client"
}
API Request Errors
| Status Code | Description | Resolution |
|---|---|---|
401 Unauthorized | Token is missing, invalid, or expired | Request a new access token |
403 Forbidden | Token is valid but lacks required scope | Check granted scopes, request additional permissions |
429 Too Many Requests | Rate limit exceeded | Implement exponential backoff and retry |
500 Server Error | Internal server error | Retry with backoff, contact support if persistent |
Comparison with Other Flows
| Feature | Client Credentials | Authorization Code + PKCE | Refresh Token |
|---|---|---|---|
| Use Case | Machine-to-machine | User authentication | Token renewal |
| User Interaction | None | Required | None |
| Client Type | Confidential only | Public & confidential | Any |
| Client Secret | Required | Not required (PKCE) | Not required |
| Refresh Token | Not issued | Issued | Used to get new tokens |
| User Context | No | Yes | Yes (from original auth) |
| Complexity | Low | High | Low |
| Security | High (if secrets protected) | Highest | High |
| Best For | Backend services, batch jobs | SPAs, mobile apps | Long sessions |