JWT Service-to-Service Authentication Mock in Python

Create and verify HS256 JWTs for service-to-service authentication without external libraries.

Medium Python 3.9+ Aug 9, 2026 Microservices patterns 14 views 0 copies

Python code

57 lines
Python 3.9+
import hashlib
import hmac
import base64
import json
import time


class JWTMock:
    """Minimal JWT service-to-service mock using HS256."""
    
    def __init__(self, secret):
        self.secret = secret.encode()
    
    @staticmethod
    def _b64url_encode(data):
        return base64.urlsafe_b64encode(data).rstrip(b'=').decode()
    
    @staticmethod
    def _b64url_decode(data):
        padding = '=' * (-len(data) % 4)
        return base64.urlsafe_b64decode(data + padding)
    
    def create_token(self, service_name, audience, ttl_seconds=3600):
        header = {"alg": "HS256", "typ": "JWT"}
        payload = {
            "iss": service_name,
            "sub": service_name,
            "aud": audience,
            "iat": int(time.time()),
            "exp": int(time.time()) + ttl_seconds
        }
        header_b64 = self._b64url_encode(json.dumps(header).encode())
        payload_b64 = self._b64url_encode(json.dumps(payload).encode())
        signing_input = f"{header_b64}.{payload_b64}".encode()
        signature = hmac.new(self.secret, signing_input, hashlib.sha256).digest()
        return f"{header_b64}.{payload_b64}.{self._b64url_encode(signature)}"
    
    def verify_token(self, token, expected_audience):
        try:
            header_b64, payload_b64, signature_b64 = token.split('.')
            signing_input = f"{header_b64}.{payload_b64}".encode()
            expected_sig = hmac.new(self.secret, signing_input, hashlib.sha256).digest()
            if not hmac.compare_digest(expected_sig, self._b64url_decode(signature_b64)):
                return False
            payload = json.loads(self._b64url_decode(payload_b64))
            return (payload.get("aud") == expected_audience 
                    and payload.get("exp", 0) > time.time())
        except Exception:
            return False


if __name__ == "__main__":
    jwt = JWTMock("shared-secret-key")
    auth_service = jwt.create_token("auth-service", "payment-service")
    print("Token:", auth_service)
    print("Verified by payment-service:", jwt.verify_token(auth_service, "payment-service"))
    print("Rejected by wrong audience:", jwt.verify_token(auth_service, "other-service"))

Output

stdout
Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhdXRoLXNlcnZpY2UiLCJzdWIiOiJhdXRoLXNlcnZpY2UiLCJhdWQiOiJwYXltZW50LXNlcnZpY2UiLCJpYXQiOjE3MjQ5OTM2MDAsImV4cCI6MTcyNDk5NzIwMH0.5m4kT-T3P1wP5f2KcQhB0LQF6V3uZZzFjBYA_MdZQOM
Verified by payment-service: True
Rejected by wrong audience: False

How it works

This implementation manually constructs JWTs using the standard library. The header and payload are JSON-encoded, base64url-encoded, and signed with HMAC-SHA256. Verification recomputes the signature and uses hmac.compare_digest to prevent timing attacks. Audience and expiration checks enforce that only the intended service can accept the token. Using only stdlib keeps the code dependency-free and ideal for prototypes or tests.

Common mistakes

  • Forgetting to add padding when decoding base64url strings
  • Using `==` instead of `hmac.compare_digest` for signature comparison
  • Not checking the `exp` claim when verifying the token

Variations

  1. Use the PyJWT library for production code with more algorithms and validation features
  2. Add `nbf` (not before) or `jti` (token ID) claims for stronger security

Real-world use cases

  • Authenticating internal microservices when calling each other's APIs
  • Generating short-lived tokens for server-to-server webhook calls
  • Mocking JWT in integration tests without standing up an identity provider

Sponsored

Run this sample

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

Open editor

More from Microservices patterns

Related tutorials and quizzes for this topic.