Skip to main content

Client Credentials Flow

The Client Credentials flow is used for server-to-server authentication where the application acts on its own behalf rather than on behalf of a user. This flow is ideal for machine-to-machine (M2M) communication.

Overview

This flow is designed for backend services, daemons, and other applications that need to authenticate themselves to access resources. Unlike user-centric flows, there is no user authentication step—the application uses its own credentials to obtain an access token.

Flow Steps

  1. Token Request: The application authenticates with its client credentials
  2. Token Response: The authorization server returns an access token
  3. Access Resources: Use the access token to access protected resources

Implementation

Prerequisites

Before starting, you’ll need:
  • Your application’s client_id and client_secret
  • The token endpoint: https://login.camall.io/oauth/token
  • Required scopes for your application

Step 1: Initiate Token Request

Request an access token directly from the token endpoint using your client credentials:
CLIENT_ID="your_client_id"
CLIENT_SECRET="your_client_secret"
SCOPE="openapi"

curl -X POST https://login.camall.io/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=${CLIENT_ID}" \
  -d "client_secret=${CLIENT_SECRET}" \
  -d "scope=${SCOPE}"
Parameters:
  • grant_type=client_credentials: Specifies the client credentials flow
  • client_id: Your application’s client ID
  • client_secret: Your application’s client secret
  • scope: Requested permissions (space-separated, optional)
Alternative: Using Basic Authentication You can also send credentials using HTTP Basic Authentication:
curl -X POST https://login.camall.io/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "Authorization: Basic $(echo -n "${CLIENT_ID}:${CLIENT_SECRET}" | base64)" \
  -d "grant_type=client_credentials" \
  -d "scope=${SCOPE}"

Step 2: Exchange Credentials for Access Token

The token endpoint responds with an access token: Response:
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "openapi"
}
Response Fields:
  • access_token: The JWT token used to authenticate API requests
  • token_type: Always “Bearer” for OAuth 2.0
  • expires_in: Token lifetime in seconds (e.g., 3600 = 1 hour)
  • scope: Granted scopes (may differ from requested scopes)
Note: Unlike the Authorization Code flow, the Client Credentials flow does not return a refresh token. When the access token expires, simply request a new one using the same credentials.

Step 3: Use the Access Token

Make authenticated API requests using the access token:
curl -X GET https://api.camall.io/v1/resource \
  -H "Authorization: Bearer ACCESS_TOKEN_HERE" \
  -H "Content-Type: application/json"
Example with actual token:
ACCESS_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

curl -X GET https://api.camall.io/v1/products \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json"

Complete Example Script

Here’s a complete bash script demonstrating the entire flow:
#!/bin/bash

# Configuration
CLIENT_ID="your_client_id"
CLIENT_SECRET="your_client_secret"
SCOPE="openapi"

echo "Step 1: Requesting access token with client credentials..."
TOKEN_RESPONSE=$(curl -s -X POST https://login.camall.io/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=${CLIENT_ID}" \
  -d "client_secret=${CLIENT_SECRET}" \
  -d "scope=${SCOPE}")

echo "Token Response:"
echo "$TOKEN_RESPONSE" | jq '.'

# Extract access token and expiration
ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.access_token')
EXPIRES_IN=$(echo "$TOKEN_RESPONSE" | jq -r '.expires_in')

echo ""
echo "Step 2: Access token obtained successfully!"
echo "Token: $ACCESS_TOKEN"
echo "Expires in: $EXPIRES_IN seconds"
echo ""

# Step 3: Make an API request
echo "Step 3: Making API request with access token..."
API_RESPONSE=$(curl -s -X GET https://api.camall.io/v1/resource \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json")

echo "API Response:"
echo "$API_RESPONSE" | jq '.'

Complete Example Script (PowerShell)

For Windows users, here’s a PowerShell version:
# Configuration
$CLIENT_ID = "your_client_id"
$CLIENT_SECRET = "your_client_secret"
$SCOPE = "openapi"

Write-Host "Step 1: Requesting access token with client credentials..." -ForegroundColor Cyan

# Prepare request body
$body = @{
    grant_type = "client_credentials"
    client_id = $CLIENT_ID
    client_secret = $CLIENT_SECRET
    scope = $SCOPE
}

# Request token
$tokenResponse = Invoke-RestMethod -Uri "https://login.camall.io/oauth/token" `
    -Method Post `
    -ContentType "application/x-www-form-urlencoded" `
    -Body $body

Write-Host "`nToken Response:" -ForegroundColor Green
$tokenResponse | ConvertTo-Json

$ACCESS_TOKEN = $tokenResponse.access_token
$EXPIRES_IN = $tokenResponse.expires_in

Write-Host "`nStep 2: Access token obtained successfully!" -ForegroundColor Cyan
Write-Host "Token: $ACCESS_TOKEN"
Write-Host "Expires in: $EXPIRES_IN seconds"

# Step 3: Make an API request
Write-Host "`nStep 3: Making API request with access token..." -ForegroundColor Cyan

$headers = @{
    Authorization = "Bearer $ACCESS_TOKEN"
    "Content-Type" = "application/json"
}

$apiResponse = Invoke-RestMethod -Uri "https://api.camall.io/v1/resource" `
    -Method Get `
    -Headers $headers

Write-Host "`nAPI Response:" -ForegroundColor Green
$apiResponse | ConvertTo-Json

Security Best Practices

  1. Secure client secret: Store client secrets securely (environment variables, key vaults, secrets managers)
  2. Use HTTPS: All endpoints must use HTTPS in production
  3. Rotate credentials: Regularly rotate client IDs and secrets
  4. Limit scope: Request only the minimum scopes required for your application
  5. Monitor token usage: Track token generation and API calls for anomalies
  6. Short-lived tokens: Tokens should have limited lifetime (typically 1 hour)
  7. Network restrictions: Consider IP allowlisting for additional security
  8. Never expose secrets: Never commit secrets to version control or client-side code

HTTP Request Examples

Raw HTTP Request - Token Request (POST form)

POST /oauth/token HTTP/1.1
Host: login.camall.io
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&scope=openapi

Raw HTTP Request - Token Request (Basic Auth)

POST /oauth/token HTTP/1.1
Host: login.camall.io
Content-Type: application/x-www-form-urlencoded
Authorization: Basic BASE64_ENCODED_CREDENTIALS

grant_type=client_credentials&scope=openapi

Raw HTTP Request - API Call with Bearer Token

GET /v1/resource HTTP/1.1
Host: api.camall.io
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

Common Use Cases

The Client Credentials flow is ideal for:
  • Backend services: Microservices communicating with each other
  • Scheduled jobs: Cron jobs or scheduled tasks that need API access
  • Data processing: Batch processing or ETL operations
  • System integrations: Third-party system integrations
  • CLI tools: Command-line tools for administrative tasks
  • Monitoring services: Health checks and monitoring systems

Error Handling

Handle common errors when using the Client Credentials flow:
# Example with error handling
TOKEN_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST https://login.camall.io/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=${CLIENT_ID}" \
  -d "client_secret=${CLIENT_SECRET}" \
  -d "scope=${SCOPE}")

HTTP_CODE=$(echo "$TOKEN_RESPONSE" | tail -n1)
RESPONSE_BODY=$(echo "$TOKEN_RESPONSE" | sed '$d')

if [ "$HTTP_CODE" -eq 200 ]; then
    echo "Success! Access token obtained."
    ACCESS_TOKEN=$(echo "$RESPONSE_BODY" | jq -r '.access_token')
    echo "Token: $ACCESS_TOKEN"
else
    echo "Error: HTTP $HTTP_CODE"
    echo "$RESPONSE_BODY" | jq '.'
fi
Common Error Responses: Invalid credentials:
{
  "error": "invalid_client",
  "error_description": "Client authentication failed"
}
Invalid scope:
{
  "error": "invalid_scope",
  "error_description": "The requested scope is invalid, unknown, or malformed"
}
Server error:
{
  "error": "server_error",
  "error_description": "The authorization server encountered an unexpected condition"
}

Comparison with Other Flows

FeatureClient CredentialsAuthorization Code
User interactionNoneRequired
Use caseMachine-to-machineUser authentication
Refresh tokenNoYes
PKCE requiredNoRecommended
Client secretRequiredRequired
Redirect URINot usedRequired

Token Caching Strategy

Since tokens are short-lived, implement caching to avoid unnecessary token requests:
#!/bin/bash

# Token cache file
CACHE_FILE="/tmp/camall_token_cache.json"

# Function to check if token is still valid
is_token_valid() {
    if [ ! -f "$CACHE_FILE" ]; then
        return 1
    fi
    
    EXPIRY=$(jq -r '.expiry' "$CACHE_FILE")
    CURRENT=$(date +%s)
    
    if [ "$CURRENT" -lt "$EXPIRY" ]; then
        return 0
    else
        return 1
    fi
}

# Check cache first
if is_token_valid; then
    echo "Using cached token..."
    ACCESS_TOKEN=$(jq -r '.access_token' "$CACHE_FILE")
else
    echo "Requesting new token..."
    TOKEN_RESPONSE=$(curl -s -X POST https://login.camall.io/oauth/token \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -d "grant_type=client_credentials" \
      -d "client_id=${CLIENT_ID}" \
      -d "client_secret=${CLIENT_SECRET}" \
      -d "scope=${SCOPE}")
    
    ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.access_token')
    EXPIRES_IN=$(echo "$TOKEN_RESPONSE" | jq -r '.expires_in')
    
    # Cache with 5-minute buffer
    EXPIRY=$(($(date +%s) + EXPIRES_IN - 300))
    
    echo "{\"access_token\":\"$ACCESS_TOKEN\",\"expiry\":$EXPIRY}" > "$CACHE_FILE"
fi

echo "Access Token: $ACCESS_TOKEN"