{"record":{"id":"ac96df2082209151","repo":"PrefectHQ/fastmcp","slug":"invalid-token-issuer","errorCode":null,"errorMessage":"Invalid token issuer","messagePattern":"Invalid token issuer","errorType":"validation","errorClass":"JoseError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/jwt_issuer.py","lineNumber":282,"sourceCode":"                    \"Token type mismatch: expected %s, got %s\",\n                    expected_token_use,\n                    token_use,\n                )\n                raise JoseError(\n                    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":264,"sourceCodeEnd":297,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/jwt_issuer.py#L264-L297","documentation":"verify_token() validates the 'iss' (issuer) claim against the issuer value the JWTIssuer was constructed with. A token signed with the correct key but issued by a different issuer (different deployment, tenant, or environment) is rejected with JoseError('Invalid token issuer') to prevent tokens minted by one system being accepted by another.","triggerScenarios":"Calling verify_token() on a token whose payload['iss'] != self.issuer (jwt_issuer.py:282): e.g. a token from staging used against production, a misconfigured issuer base URL, or a multi-tenant setup routing tokens to the wrong tenant verifier.","commonSituations":"Environment variable for the issuer differs between issuer and verifier deployments (trailing slash, http vs https, localhost vs domain); pointing a dev client at prod; copying tokens between environments in tests; tenant ID omitted from the configured issuer URL.","solutions":["Make the JWTIssuer's issuer configuration identical to the value stamped into tokens at mint time (exact string match)","Check env-specific config — trailing slashes, scheme (http/https), and host must match exactly","In multi-tenant setups, ensure the request is routed to the verifier configured for the token's tenant/issuer","Decode the token locally and compare payload['iss'] with your configured issuer to see the exact mismatch"],"exampleFix":"// before\nJWTIssuer(issuer=\"https://api.example.com/\")  # token has no trailing slash\n// after\nJWTIssuer(issuer=\"https://api.example.com\")  # match mint-time iss exactly","handlingStrategy":"validation","validationCode":"claims = jwt.decode(token, options={\"verify_signature\": False})\nexpected_issuer = os.environ[\"FASTMCP_JWT_ISSUER\"]\nif claims.get(\"iss\") != expected_issuer:\n    raise ValueError(f\"Token iss {claims.get('iss')!r} != configured {expected_issuer!r}\")","typeGuard":"def has_expected_issuer(claims: dict, expected: str) -> bool:\n    return claims.get(\"iss\") == expected","tryCatchPattern":"try:\n    payload = issuer.verify_token(token)\nexcept JoseError as e:\n    if \"issuer\" in str(e).lower():\n        log_untrusted_token_rejected(token)  # do not retry; investigate config\n        raise\n    raise","preventionTips":["Use one shared config source for issuer on both minting and verifying sides","Compare exact strings — beware trailing slashes and http vs https","In multi-tenant setups, derive expected iss from the request tenant"],"tags":["jwt","auth","issuer-validation","configuration"],"backgroundTag":"jwt-issuer-mismatch","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}