{"record":{"id":"8f21cdbd575b5ec7","repo":"zeroclaw-labs/zeroclaw","slug":"invalid-jwt-structure-expected-3-dot-separated-pa","errorCode":null,"errorMessage":"Invalid JWT structure: expected 3 dot-separated parts","messagePattern":"Invalid JWT structure: expected 3 dot-separated parts","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-runtime/src/security/nevis.rs","lineNumber":238,"sourceCode":"                .split_whitespace()\n                .map(String::from)\n                .collect(),\n            mfa_verified: body.acr.as_deref() == Some(\"mfa\")\n                || body\n                    .amr\n                    .iter()\n                    .flatten()\n                    .any(|m| m == \"fido2\" || m == \"passkey\" || m == \"otp\" || m == \"webauthn\"),\n            session_expiry: body.exp.unwrap_or(0),\n        })\n    }\n\n    #[allow(clippy::unused_async)] // Will use async when JWKS validation is implemented\n    async fn validate_token_local(&self, token: &str) -> Result<NevisIdentity> {\n        // JWT structure check: header.payload.signature\n        let parts: Vec<&str> = token.split('.').collect();\n        if parts.len() != 3 {\n            bail!(\"Invalid JWT structure: expected 3 dot-separated parts\");\n        }\n\n        bail!(\n            \"Local JWKS token validation is not yet implemented. \\\n             Set token_validation = \\\"remote\\\" to use the Nevis introspection endpoint.\"\n        );\n    }\n\n    /// Validate a Nevis session token (cookie-based sessions).\n    pub async fn validate_session(&self, session_token: &str) -> Result<NevisIdentity> {\n        if session_token.is_empty() {\n            bail!(\"empty session token\");\n        }\n\n        let session_url = format!(\n            \"{}/auth/realms/{}/protocol/openid-connect/userinfo\",\n            self.instance_url.trim_end_matches('/'),\n            self.realm,","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/security/nevis.rs#L220-L256","documentation":"In local mode validate_token_local splits the token on '.' and requires exactly 3 parts (nevis.rs:236-239). Any string that is not header.payload.signature fails here — before the not-implemented bail. This is a shape sanity check only; no signature work happens.","triggerScenarios":"validate_token with token_validation = \"local\" and a non-JWT value: an opaque access token, an API key, or the full 'Bearer eyJ...' header with the scheme prefix still attached.","commonSituations":"Middleware forwards the whole Authorization header value instead of the token; the IdP issues opaque reference tokens that can never satisfy local JWT validation; a truncated paste loses a segment.","solutions":["Pass only the raw JWT — strip the 'Bearer ' scheme and whitespace before calling validate_token","If your Nevis instance issues opaque/reference tokens, switch to token_validation = \"remote\" (introspection handles them)","Pre-check token.split('.').count() == 3 in your middleware to fail with a clearer 401"],"exampleFix":"// before\nlet token = authorization_header; // \"Bearer eyJhbGci...\"\nlet id = provider.validate_token(&token).await?;\n\n// after\nlet token = authorization_header\n    .strip_prefix(\"Bearer \")\n    .unwrap_or(&authorization_header)\n    .trim();\nlet id = provider.validate_token(token).await?;","handlingStrategy":"validation","validationCode":"fn is_jwt_shaped(token: &str) -> bool {\n    let parts: Vec<&str> = token.split('.').collect();\n    parts.len() == 3 && parts.iter().all(|p| !p.is_empty())\n}\n\nif !is_jwt_shaped(token) { return unauthorized(\"malformed token\"); }","typeGuard":null,"tryCatchPattern":"Match err.to_string().contains(\"Invalid JWT structure\") and return 401 with a generic 'malformed token' message — log only the part count, never the token.","preventionTips":["Strip the Bearer scheme in exactly one place before token use","Know whether your IdP issues JWTs or opaque reference tokens before choosing local mode","Log token shape (part count) instead of token content when debugging"],"tags":["auth","nevis","jwt","input-validation","rust"],"backgroundTag":"malformed-jwt","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}