SkillByAIOpen interactive version →

Lesson 15 / 25

Client Credentials and Discouraged Grants

Service-to-service access, and why implicit and password grants are going away.

Machines and legacy flows

The client credentials grant serves service-to-service calls with no user involved: the service authenticates as itself (client secret, or preferably a private-key JWT or mutual TLS) and receives an access token with limited scopes. The implicit grant returned tokens directly in the redirect URL fragment, exposing them to browser history, referrers and injection; it is superseded by the authorization code flow with PKCE. The resource owner password credentials grant makes the app collect the user's password, which defeats the point of OAuth, trains users to type passwords into third-party apps and cannot support MFA or passkeys well. RFC 9700 says not to use either, and the OAuth 2.1 draft omits them. Check the current draft status before citing OAuth 2.1 as final.

Client credentials from Python

Using requests; cache the token until shortly before it expires.

import time
import requests

_cache = {'token': None, 'exp': 0}

def service_token() -> str:
    if _cache['token'] and time.time() < _cache['exp'] - 60:
        return _cache['token']
    resp = requests.post(
        'https://auth.example.com/token',
        data={'grant_type': 'client_credentials', 'scope': 'inventory:read'},
        auth=(CLIENT_ID, CLIENT_SECRET),   # HTTP Basic client authentication
        timeout=5,
    )
    resp.raise_for_status()
    body = resp.json()
    _cache.update(token=body['access_token'], exp=time.time() + body['expires_in'])
    return _cache['token']

items = requests.get('https://inventory.internal/items',
                     headers={'Authorization': f'Bearer {service_token()}'}, timeout=5)

Scope machine clients tightly

Give each service its own client ID with the smallest set of scopes, so a leaked secret affects one integration and shows up clearly in audit logs.

Quick check: Which grant fits a nightly batch job calling an internal API with no user present?

  • Client credentials
  • Implicit
  • Resource owner password credentials
  • Authorization code without PKCE
Answer

Client credentials — No user is involved, so the service authenticates as itself.