{"record":{"id":"baf55957c998d7b2","repo":"PrefectHQ/fastmcp","slug":"invalid-token-audience","errorCode":null,"errorMessage":"Invalid token audience","messagePattern":"Invalid token audience","errorType":"validation","errorClass":"JoseError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/jwt_issuer.py","lineNumber":287,"sourceCode":"                    f\"Token type mismatch: expected {expected_token_use}, \"\n                    f\"got {token_use}\"\n                )\n\n            # Validate expiration\n            exp = payload.get(\"exp\")\n            if exp is not None and exp < time.time():\n                logger.debug(\"Token expired\")\n                raise JoseError(\"Token has expired\")\n\n            # Validate issuer\n            if payload.get(\"iss\") != self.issuer:\n                logger.debug(\"Token has invalid issuer\")\n                raise JoseError(\"Invalid token issuer\")\n\n            # Validate audience\n            if payload.get(\"aud\") != self.audience:\n                logger.debug(\"Token has invalid audience\")\n                raise JoseError(\"Invalid token audience\")\n\n            logger.debug(\n                \"Token verified successfully for subject=%s\", payload.get(\"sub\")\n            )\n            return payload\n\n        except JoseError as e:\n            logger.debug(\"Token validation failed: %s\", e)\n            raise\n","sourceCodeStart":269,"sourceCodeEnd":297,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/jwt_issuer.py#L269-L297","documentation":"verify_token() validates the 'aud' (audience) claim against the audience the JWTIssuer was configured with. The audience names the intended recipient of the token; a token signed correctly but addressed to a different audience (different API/resource) is rejected with JoseError('Invalid token audience') so tokens meant for service A cannot be replayed at service B.","triggerScenarios":"Calling verify_token() on a token whose payload['aud'] != self.audience (jwt_issuer.py:287): e.g. a token minted for resource 'api://other-service' being verified by a server configured with audience 'mcp-server', or an audience case/whitespace mismatch.","commonSituations":"Resource-server audience configured with a different string than the minting code uses (typo, case, trailing slash); reusing one client's tokens against another API; multi-resource deployments where the client picked the wrong resource indicator during OAuth; audience defaulted but never set on one side.","solutions":["Set the audience on both the issuer (token minting) and the verifier (JWTIssuer) to the exact same string","Check the OAuth resource indicator / audience parameter the client requests matches this server's configured audience","Decode the token locally and diff payload['aud'] against your configured audience to spot case/slash/whitespace differences","If this server legitimately accepts multiple audiences, configure the verifier to accept the full set rather than one"],"exampleFix":"// before\nJWTIssuer(audience=\"mcp-server\")  # tokens minted with aud=\"mcp-api\"\n// after\nJWTIssuer(audience=\"mcp-api\")  # match the aud stamped at mint time","handlingStrategy":"validation","validationCode":"claims = jwt.decode(token, options={\"verify_signature\": False})\nexpected_aud = \"mcp-api\"\nif claims.get(\"aud\") != expected_aud:\n    raise ValueError(f\"Token aud {claims.get('aud')!r} != expected {expected_aud!r}\")","typeGuard":"def has_expected_audience(claims: dict, expected: str) -> bool:\n    return claims.get(\"aud\") == expected","tryCatchPattern":"try:\n    payload = issuer.verify_token(token)\nexcept JoseError as e:\n    if \"audience\" in str(e).lower():\n        log_untrusted_token_rejected(token)  # client requested wrong resource\n        raise\n    raise","preventionTips":["Define the audience string once and share it between client resource requests and server config","Verify the OAuth client requests a token for the correct resource indicator","Check case, whitespace, and scheme differences when audiences look similar"],"tags":["jwt","auth","audience-validation","configuration"],"backgroundTag":"jwt-audience-mismatch","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}