{"record":{"id":"fe5f027102027057","repo":"PrefectHQ/fastmcp","slug":"jwt-signing-key-is-required-when-upstream-client-s","errorCode":null,"errorMessage":"jwt_signing_key is required when upstream_client_secret is not provided. The JWT signing key cannot be derived without a client secret.","messagePattern":"jwt_signing_key is required when upstream_client_secret is not provided\\. The JWT signing key cannot be derived without a client secret\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"critical","filePath":"fastmcp_slim/fastmcp/server/auth/oauth_proxy/proxy.py","lineNumber":559,"sourceCode":"        self._extra_token_params: dict[str, str] = extra_token_params or {}\n\n        # Token expiry fallback (None means use smart default based on refresh token)\n        self._fallback_access_token_expiry_seconds: int | None = (\n            fallback_access_token_expiry_seconds\n        )\n        self._fallback_refresh_token_expiry_seconds: int = (\n            fallback_refresh_token_expiry_seconds\n            if fallback_refresh_token_expiry_seconds is not None\n            else DEFAULT_REFRESH_TOKEN_EXPIRY_SECONDS\n        )\n        self._fastmcp_access_token_expiry_seconds: int | None = (\n            fastmcp_access_token_expiry_seconds\n        )\n        self._token_expiry_threshold_seconds: int = token_expiry_threshold_seconds\n\n        if jwt_signing_key is None:\n            if upstream_client_secret is None:\n                raise ValueError(\n                    \"jwt_signing_key is required when upstream_client_secret is not provided. \"\n                    \"The JWT signing key cannot be derived without a client secret.\"\n                )\n            jwt_signing_key = derive_jwt_key(\n                high_entropy_material=upstream_client_secret,\n                salt=\"fastmcp-jwt-signing-key\",\n            )\n\n        if isinstance(jwt_signing_key, str):\n            if len(jwt_signing_key) < 12:\n                logger.warning(\n                    \"jwt_signing_key is less than 12 characters; it is recommended to use a longer. \"\n                    \"string for the key derivation.\"\n                )\n            jwt_signing_key = derive_jwt_key(\n                low_entropy_material=jwt_signing_key,\n                salt=\"fastmcp-jwt-signing-key\",\n            )","sourceCodeStart":541,"sourceCodeEnd":577,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/oauth_proxy/proxy.py#L541-L577","documentation":"OAuthProxy requires a JWT signing key to sign issued tokens. If no explicit jwt_signing_key is given, it is derived deterministically from upstream_client_secret; with neither provided, derivation is impossible and __init__ raises ValueError. This fails fast at startup rather than producing unsigned or randomly-keyed tokens.","triggerScenarios":"Constructing OAuthProxy with jwt_signing_key=None and upstream_client_secret=None (or omitted).","commonSituations":"Configuring the proxy against an IdP flow that doesn't use a client secret (e.g. public-client/PKCE setups) and forgetting to supply a signing key; refactoring constructor arguments and dropping the secret; missing env var not passed into the constructor.","solutions":["Pass an explicit jwt_signing_key (e.g. a stable high-entropy secret loaded from an environment variable)","Provide upstream_client_secret so the key can be derived with derive_jwt_key","If the upstream flow has no secret, generate and persist a dedicated signing key instead of leaving both None"],"exampleFix":"// before\nproxy = OAuthProxy(upstream_authorization_endpoint=..., upstream_token_endpoint=...)  # no secret, no key\n// after\nproxy = OAuthProxy(\n    upstream_authorization_endpoint=...,\n    upstream_token_endpoint=...,\n    jwt_signing_key=os.environ[\"JWT_SIGNING_KEY\"],\n)","handlingStrategy":"validation","validationCode":"jwt_key = os.environ.get(\"FASTMCP_JWT_SIGNING_KEY\")\nclient_secret = os.environ.get(\"UPSTREAM_CLIENT_SECRET\")\nif jwt_key is None and client_secret is None:\n    raise SystemExit(\"Set FASTMCP_JWT_SIGNING_KEY or UPSTREAM_CLIENT_SECRET before starting\")","typeGuard":"def has_signing_material(key: str | None, secret: str | None) -> bool:\n    return key is not None or secret is not None","tryCatchPattern":"try:\n    proxy = OAuthProxy(..., jwt_signing_key=key, upstream_client_secret=secret)\nexcept ValueError as e:\n    if \"jwt_signing_key is required\" in str(e):\n        raise SystemExit(\"Configuration error: provide jwt_signing_key or upstream_client_secret\") from e\n    raise","preventionTips":["Load signing material from environment variables with startup-time validation","Always pass an explicit jwt_signing_key when the upstream flow uses no client secret","Fail fast in deployment config checks before the app boots"],"tags":["oauth","jwt","configuration","startup"],"backgroundTag":"missing-config-parameter","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}