# Client Credentials Flow Template OAuth 2.0 Client Credentials Grant for server-to-server authentication using client_id and client_secret. ## When to Use - Service accounts - Background processes - System-to-system integrations - When no specific user context is needed - External Client Apps (ECAs) with client credentials enabled ## Prerequisites 1. Connected App or External Client App configured 2. Client Credentials flow enabled 3. Execution user assigned via Permission Set (for ECAs) ## Mermaid Template ```mermaid %%{init: {'theme': 'base', 'themeVariables': { 'actorBkg': '#ddd6fe', 'actorTextColor': '#1f2937', 'actorBorder': '#6d28d9', 'signalColor': '#334155', 'signalTextColor': '#1f2937', 'noteBkgColor': '#f8fafc', 'noteTextColor': '#1f2937', 'noteBorderColor': '#334155' }}}%% sequenceDiagram autonumber box rgba(221,214,254,0.3) CLIENT APPLICATION participant C as 🖥️ Service
(Backend Server) end box rgba(167,243,208,0.3) SALESFORCE participant SF as ☁️ Salesforce
Authorization Server end Note over C,SF: Client Credentials Flow (RFC 6749 Section 4.4) C->>C: 1. Retrieve Client Credentials Note over C: client_id = CONSUMER_KEY
client_secret = CONSUMER_SECRET C->>SF: 2. POST /services/oauth2/token Note over C,SF: grant_type=client_credentials
client_id=CONSUMER_KEY
client_secret=CONSUMER_SECRET SF->>SF: 3. Validate Client Credentials Note over SF: Verify client_id exists
Verify client_secret matches SF->>SF: 4. Determine Execution User Note over SF: For ECA: Use assigned
Permission Set user

For Connected App: Use
"Run As" user SF->>SF: 5. Generate Access Token Note over SF: Token runs in context
of execution user SF->>C: 6. Return Access Token Note over SF,C: {
"access_token": "...",
"instance_url": "https://...",
"token_type": "Bearer",
"issued_at": "..."
} Note over C: ⚠️ No refresh_token returned
⚠️ No user context (runs as service user) C->>SF: 7. Make API Calls Note over C,SF: Authorization: Bearer ACCESS_TOKEN SF->>SF: 8. Execute as Service User Note over SF: All operations run with
execution user's permissions SF->>C: 9. API Response ``` ## ASCII Fallback Template ```text ┌───────────────────────┐ ┌────────────────────┐ │ Service/Backend │ │ Salesforce │ │ (client_id/secret) │ │ (Auth Server) │ └───────────┬───────────┘ └─────────┬──────────┘ │ │ │ 1. POST /token │ │ grant_type= │ │ client_credentials │ │ client_id=KEY │ │ client_secret=SECRET │ │──────────────────────────>│ │ │ │ 2. Validate │ │ credentials │ │ │ │ 3. Determine │ │ execution │ │ user │ │ │ │ 4. Generate │ │ access token │ │ │ │ 5. Access Token │ │ (NO refresh token!) │ │ (NO user context!) │ │<──────────────────────────│ │ │ │ 6. API Request │ │ (Bearer token) │ │──────────────────────────>│ │ │ │ 7. API Response │ │ (runs as svc user) │ │<──────────────────────────│ ``` ## Token Request ```bash curl -X POST https://login.salesforce.com/services/oauth2/token \ -d "grant_type=client_credentials" \ -d "client_id=YOUR_CONSUMER_KEY" \ -d "client_secret=YOUR_CONSUMER_SECRET" ``` ## Response Example ```json { "access_token": "00D5g000001ABC...!ARcAQNlBrLGj...", "instance_url": "https://mycompany.my.salesforce.com", "token_type": "Bearer", "issued_at": "1702123456789" } ``` ## Code Examples ### Python ```python import requests response = requests.post( 'https://login.salesforce.com/services/oauth2/token', data={ 'grant_type': 'client_credentials', 'client_id': 'YOUR_CONSUMER_KEY', 'client_secret': 'YOUR_CONSUMER_SECRET' } ) token_data = response.json() access_token = token_data['access_token'] instance_url = token_data['instance_url'] # Make API call headers = {'Authorization': f'Bearer {access_token}'} api_response = requests.get( f'{instance_url}/services/data/v66.0/sobjects/Account', headers=headers ) ``` ### Node.js ```javascript const axios = require('axios'); const response = await axios.post( 'https://login.salesforce.com/services/oauth2/token', new URLSearchParams({ grant_type: 'client_credentials', client_id: 'YOUR_CONSUMER_KEY', client_secret: 'YOUR_CONSUMER_SECRET' }) ); const { access_token, instance_url } = response.data; // Make API call const apiResponse = await axios.get( `${instance_url}/services/data/v66.0/sobjects/Account`, { headers: { Authorization: `Bearer ${access_token}` } } ); ``` ## Connected App vs External Client App | Feature | Connected App | External Client App (ECA) | |---------|--------------|--------------------------| | Secret Management | Manual rotation | Automatic rotation supported | | User Assignment | "Run As" user | Permission Set assignment | | Configuration | Setup → App Manager | Setup → External Client Apps | | Recommended | Legacy integrations | New integrations (2024+) | ## Key Characteristics | Aspect | Value | |--------|-------| | User Interaction | None required | | Refresh Token | **Not returned** - re-authenticate | | User Context | Runs as execution/service user | | Scopes | Limited to service account permissions | | Best For | System integrations, batch jobs | ## Enabling Client Credentials ### For Connected App 1. Setup → App Manager → Edit Connected App 2. Enable OAuth Settings 3. Enable "Client Credentials Flow" 4. Set "Run As" user ### For External Client App 1. Setup → External Client Apps → New 2. Configure OAuth Settings 3. Enable `isClientCredentialsEnabled` 4. Assign Permission Set with user ## Security Considerations 1. **Protect client_secret** - Never expose in client-side code 2. **Use dedicated service user** with minimal permissions 3. **Rotate secrets regularly** (especially for Connected Apps) 4. **Monitor API usage** - Set up event monitoring 5. **Restrict IP ranges** if possible ## Limitations - No refresh tokens (must re-authenticate) - No user context (cannot impersonate users) - Limited to service user's permissions - Cannot use for user-specific operations ## Customization Points Replace these placeholders: - `CONSUMER_KEY` → Your Connected App's Consumer Key - `CONSUMER_SECRET` → Your Connected App's Consumer Secret - `login.salesforce.com` → Or `test.salesforce.com` for sandbox