OAuth2 Client Credentials Grant — Machine-to-Machine API Authentication
In this tutorial, you will learn about OAuth2 Client Credentials Grant. We cover key concepts, practical examples, and best practices to help you master this topic.
The OAuth2 Client Credentials grant allows a client application to authenticate directly to the authorization server, receiving an access token without any user interaction.
What You'll Learn
How the Client Credentials grant works, when to use it, implementation with Python, and security considerations for machine-to-machine authentication.
Why It Matters
Many API integrations are server-to-server with no user present. A cron job fetches data, a microservice authenticates to another service, or a backend process calls an external API. The Client Credentials grant is designed for these scenarios.
Real-World Use
Stripe's API uses Client Credentials for backend integrations, GitHub Actions uses it for automated workflows, and Durga Antivirus Pro's backend services use Client Credentials to authenticate between microservices.
flowchart LR
A["Backend Service\n(Client)"] -->|"POST /token\ngrant_type=client_credentials"| B["Authorization Server"]
B -->|"Verify client_id + client_secret"| B
B -->|"Access Token"| A
A -->|"API call + Bearer token"| C["Resource Server"]
C -->|"Data"| A
style A fill:#dbeafe,stroke:#2563eb
style B fill:#fef3c7,stroke:#d97706
style C fill:#dcfce7,stroke:#16a34a
How It Works
- The client authenticates with its own credentials (
client_idandclient_secret) - It sends a POST request to the token endpoint with
grant_type=client_credentials - The authorization server validates the credentials and issues an access token
- The client uses the access token to call the API
Use Cases
| Scenario | Why Client Credentials |
|---|---|
| Microservice A calling Microservice B | No user context needed |
| Cron job fetching nightly reports | Runs on schedule, no user interaction |
| Backend processing pipeline | Automated data processing |
| Webhook delivery service | Server-to-server notifications |
Code Example: Client Credentials Flow
import requests
CLIENT_ID = "your-client-id"
CLIENT_SECRET = "your-client-secret"
TOKEN_URL = "https://auth.example.com/oauth/token"
API_URL = "https://api.example.com/reports"
# Step 1: Get access token
token_response = requests.post(TOKEN_URL, data={
"grant_type": "client_credentials",
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"scope": "reports:read"
})
token_data = token_response.json()
access_token = token_data["access_token"]
print(f"Got token: {access_token[:20]}... expires in {token_data['expires_in']}s")
# Step 2: Use token to call API
headers = {"Authorization": f"Bearer {access_token}"}
api_response = requests.get(API_URL, headers=headers)
print(f"API response: {api_response.status_code}")
print(api_response.json())
Expected output:
Got token: eyJhbGciOiJSUzI1... expires in 3600s
API response: 200
{"reports": [{"id": 1, "name": "Daily Summary"}]}
Code Example: Expressing Scopes
# Different scopes for different permissions
scopes = {
"reports:read": "Read-only access to reports",
"reports:write": "Create and modify reports",
"users:read": "Read user data",
"admin": "Full administrative access"
}
# Request minimal scope
response = requests.post(TOKEN_URL, data={
"grant_type": "client_credentials",
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"scope": "reports:read users:read"
})
print(f"Granted scopes: {response.json()['scope']}")
Common Mistakes
1. Using Client Credentials for User Actions
Client Credentials identifies the application, not a user. Do not use it for actions that need user context or audit trail.
2. Hardcoding Client Secrets in Source Code
Client secrets are sensitive credentials. Use environment variables or a secret manager.
3. Not Scoping Tokens Appropriately
Requesting admin scope when the service only needs reports:read violates the principle of Least Privilege.
4. Storing Access Tokens Without Expiry Check
The token will expire. Check expires_in and refresh proactively, or handle 401 responses.
5. Ignoring Token Endpoint Security
The token endpoint must be protected with HTTPS and Rate Limiting. A compromised client secret can be used to obtain tokens until rotated.
Practice Questions
- What grant type should you use for a server-to-server API integration?
- How does the client authenticate to the token endpoint?
- What is the purpose of scopes in Client Credentials?
- Why should Client Credentials not be used for user-specific actions?
- How do you refresh a Client Credentials token?
Answers:
- The Client Credentials grant (
grant_type=client_credentials). - Using
client_idandclient_secretsent in the POST body or via HTTP Basic Auth header. - Scopes limit what the token can do (e.g.,
reports:read), following the principle of least privilege. - Client Credentials identifies the application. There is no user context. User-specific actions need Authorization Code or ROPC grant.
- Client Credentials tokens are typically not refreshed — because no user is involved, simply request a new token when the old one expires.
Challenge: Build a microservice that authenticates to another service using Client Credentials, handles token expiry by Caching the token and requesting a new one before expiration, and implements retry logic on 401 responses.
FAQ
Mini Project
Build a Python service that authenticates to a mock API using Client Credentials, caches the token, automatically refreshes before expiration, and handles 401 responses by requesting a new token.
What's Next
Next, learn about the Resource Owner Password Credentials Grant for trusted first-party applications.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro