TL;DR
Your authorization tokens aren’t tied to specific applications (clients). This means a token generated for one app could potentially be used in another, which is a security risk. We’ll fix this by ensuring each token includes information about the client it was issued to and verifying that information on every request.
Solution Guide
- Understand Client Binding: Client binding means including details about the application requesting the token within the token itself. This usually involves adding a ‘client_id’ claim (a piece of data) to the token.
- Why it matters: Prevents tokens from being used across different applications, limiting damage if one app is compromised.
- Configure Your Authorization Server: Most authorization servers (like Keycloak, Auth0, or Okta) allow you to configure which claims are included in the tokens they issue.
- Keycloak Example: In your client settings within the Keycloak admin console, ensure that ‘Client ID’ is mapped as a claim in the ‘Mappers’ section.
- Auth0 Example: In Auth0’s Application Settings, go to API Authorizations and configure the Client ID claim under ‘Advanced Settings’.
- Modify Your Token Generation Code (if applicable): If you’re generating tokens yourself (not using a standard server), ensure your code adds the ‘client_id’ as a claim.
// Example in Python with PyJWT payload = { 'user_id': user.id, 'client_id': client.id // Add the client ID here } token = jwt.encode(payload, secret_key, algorithm='HS256') - Update Your Resource Server (API) Code: This is where you verify that the token’s ‘client_id’ matches the expected client for the requested resource.
- Token Decoding: First, decode the token to extract its claims.
- Client ID Verification: Check if the decoded token contains a ‘client_id’ claim and that it corresponds to the application making the request.
- Example Resource Server Code (Python with Flask):
from flask import Flask, request import jwt app = Flask(__name__) @app.route('/protected') def protected(): token = request.headers.get('Authorization') if not token: return 'Token missing', 401 try: payload = jwt.decode(token, secret_key, algorithms=['HS256']) client_id = payload.get('client_id') expected_client_id = 'your-app-client-id' # Replace with the expected client ID for this route if client_id != expected_client_id: return 'Invalid client', 403 return 'Protected resource accessed!', 200 except jwt.ExpiredSignatureError: return 'Token expired', 401 except jwt.DecodeError: return 'Invalid token', 401 - Testing:
- Generate a token for Client A.
- Attempt to use that token with Client B’s API endpoint. The request should be rejected (HTTP 403 Forbidden).
- Verify the correct client ID is being extracted from valid tokens.

