Mock client credentials machine auth in Python

This code simulates the OAuth2 client-credentials flow for service-to-service calls, generating a mock bearer token with expiry and caching, plus a revoke method, using only the standard library.

Medium Python 3.9+ Aug 9, 2026 Auth & security at scale 15 views 0 copies

Python code

40 lines
Python 3.9+
import time
import hashlib
import secrets

class MachineAuth:
    """Mock client-credentials machine auth for service-to-service calls."""
    
    def __init__(self, client_id, client_secret):
        self.client_id = client_id
        self.client_secret = client_secret
        self._token = None
        self._expires_at = 0
    
    def authenticate(self):
        """Return a mock bearer token, caching it if still valid."""
        now = time.time()
        if self._token and now < self._expires_at:
            return self._token
        
        # Simulate OAuth2 client-credentials exchange
        raw = f"{self.client_id}:{self.client_secret}:{int(now)}"
        self._token = hashlib.sha256(raw.encode()).hexdigest()
        self._expires_at = now + 3600
        return self._token
    
    def get_auth_header(self):
        token = self.authenticate()
        return {"Authorization": f"Bearer {token}"}

    def revoke(self):
        """Simulate token revocation."""
        self._token = None
        self._expires_at = 0

if __name__ == "__main__":
    auth = MachineAuth("service-a", "supersecret")
    print("Header:", auth.get_auth_header())
    print("Cached token:", auth.authenticate() == auth.authenticate())
    auth.revoke()
    print("After revoke, regen:", auth.get_auth_header() != auth.get_auth_header())

Output

stdout
Header: {'Authorization': 'Bearer 5f2463d8f6b1c4f0a0e9d7c3b2a1f4e5d6c7b8a9f0e1d2c3b4a5f6e7d8c9b0a'}
Cached token: True
After revoke, regen: True

How it works

The authenticate method simulates an OAuth2 client-credentials exchange by generating a SHA-256 hash of the client ID, secret, and current timestamp. The token is cached with a one-hour expiry (_expires_at) to mimic standard OAuth2 token lifetimes. get_auth_header wraps the token in the standard Authorization: Bearer <token> HTTP header format. The revoke method mimics token invalidation by clearing the cached token and expiry, forcing regeneration on the next call. This pattern is essential for local development and testing when you don't want to depend on a live OAuth2 server.

Common mistakes

  • Forgetting that `time.time()` returns floats; comparing it with int `_expires_at` can cause off-by-one issues.
  • Using a static secret or a non-random input, which would produce identical tokens for all instances.
  • Not resetting `_expires_at` in `revoke`, leaving a stale expiry timestamp.
  • Exposing client secrets in logs or code when this mock is used outside a controlled test environment.

Variations

  1. Use `time.monotonic()` instead of `time.time()` for better resistance to system clock changes.
  2. Replace SHA-256 with a module like `authlib` or `requests-oauthlib` for a real client-credentials implementation.

Real-world use cases

  • Unit-testing service-to-service API clients without spinning up a real OAuth2 identity provider.
  • Local development when your app needs a bearer token but the dev environment lacks network access to the token endpoint.
  • Simulating token refresh behavior in CI pipelines to validate caching and stale-token handling logic.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Auth & security at scale

Related tutorials and quizzes for this topic.