{"record":{"id":"7e55695cb6caa9c0","repo":"neondatabase/neon","slug":"failed-to-verify-authorization-token","errorCode":null,"errorMessage":"failed to verify authorization token","messagePattern":"failed to verify authorization token","errorType":"http","errorClass":null,"httpStatus":401,"severity":"error","filePath":"compute_tools/src/http/middleware/authorize.rs","lineNumber":184,"sourceCode":"                    continue;\n                }\n            };\n\n            match jsonwebtoken::decode::<ComputeClaims>(token, &decoding_key, validation) {\n                Ok(data) => return Ok(data),\n                Err(e) => {\n                    warn!(\n                        \"failed to decode authorization token using {}: {}\",\n                        jwk.common.key_id.as_ref().unwrap(),\n                        e\n                    );\n\n                    continue;\n                }\n            }\n        }\n\n        Err(anyhow!(\"failed to verify authorization token\"))\n    }\n}\n","sourceCodeStart":166,"sourceCodeEnd":187,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/compute_tools/src/http/middleware/authorize.rs#L166-L187","documentation":"compute_ctl's HTTP authorization middleware (tower_http AsyncAuthorizeRequest) tried to verify the incoming Bearer JWT against every key in its JWKS and every attempt failed - either DecodingKey::from_jwk failed for each key or jsonwebtoken::decode rejected the token for each (bad signature, wrong algorithm, expired exp, malformed claims). The final error carries no detail; the per-key reasons only appear as warn! logs ('failed to decode authorization token using {kid}'). The middleware maps it to 401 Unauthorized for the request.","triggerScenarios":"jsonwebtoken::decode::<ComputeClaims> fails for all JWKS keys: expired token (validate_exp = true), token signed by a key absent from the JWKS, algorithm mismatch (validation is EdDSA unless the JWKS contains an RS256 key - Hadron deployments), or a structurally invalid token string.","commonSituations":"Long-lived tokens going stale; sending a staging token to a production compute (or vice versa); JWKS rotation after compute start (keys fetched once at startup); clock skew making exp appear passed; Authorization header with stray quotes or whitespace.","solutions":["Obtain a fresh token from the control plane and retry - expiry is validated and is the most common cause","Confirm the token was issued by the same environment whose JWKS this compute was started with; restart compute_ctl to refetch rotated keys","Check warn-level logs for per-key decode errors to distinguish signature vs algorithm vs expiry","Verify the header is exactly `Bearer <jwt>` with no extra characters, and the token has the 3 dot-separated JWT parts"],"exampleFix":"// before\nlet resp = client.get(url).bearer_auth(&token).send().await?;\n\n// after\nlet mut resp = client.get(url).bearer_auth(&token).send().await?;\nif resp.status() == StatusCode::UNAUTHORIZED {\n    token = refresh_token().await?; // fresh, unexpired JWT\n    resp = client.get(url).bearer_auth(&token).send().await?;\n}","handlingStrategy":"validation","validationCode":"// client side: refuse to send an already-expired token\nlet payload_b64 = token.split('.').nth(1)?;\nlet bytes = base64::engine::general_purpose::URL_SAFE_NO_PAD\n    .decode(payload_b64)\n    .ok()?;\nlet claims: serde_json::Value = serde_json::from_slice(&bytes).ok()?;\nif claims[\"exp\"].as_i64().unwrap_or(0) <= chrono::Utc::now().timestamp() {\n    token = refresh_token().await?; // expired - get a new one before the request\n}","typeGuard":null,"tryCatchPattern":"// server side: this error already maps to 401\nmatch Authorize::verify(&jwks, bearer.token(), &validation) {\n    Ok(data) => data,\n    Err(_) => {\n        return Err(JsonResponse::error(\n            StatusCode::UNAUTHORIZED,\n            \"failed to verify authorization token\",\n        ))\n    }\n}","preventionTips":["Refresh tokens well before exp instead of reusing long-lived JWTs","Send exactly 'Bearer <jwt>' - no quotes or whitespace - in the Authorization header","Issue tokens from the same environment/keys as the compute's JWKS","On 401, re-authenticate once with a fresh token instead of retrying the same one"],"tags":["jwt","authorization","http-middleware","security","rust"],"backgroundTag":"jwt-verification-failed","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}