{"record":{"id":"f79b70bc4deb60ef","repo":"clockworklabs/SpacetimeDB","slug":"no-kid-found","errorCode":null,"errorMessage":"No kid found","messagePattern":"No kid found","errorType":"error_code","errorClass":"TokenValidationError","httpStatus":null,"severity":"error","filePath":"crates/core/src/auth/token_validation.rs","lineNumber":332,"sourceCode":"            }\n\n            log::debug!(\"Key id {kid} not found in JWKS. Trying keys without key ids.\");\n            let mut last_error = TokenValidationError::KeyIDNotFound;\n            for key in self.keyset.keys_without_ids() {\n                match self.validate_with_key(token, key).await {\n                    Ok(claims) => return Ok(claims),\n                    Err(e) => {\n                        last_error = e;\n                        log::debug!(\"Validating with key without kid failed\");\n                    }\n                }\n            }\n            return Err(last_error);\n        }\n        log::debug!(\"No key id in header. Trying all keys.\");\n        // TODO: Consider returning an error if no kid is given?\n        // For now, lets just try all the keys.\n        let mut last_error = TokenValidationError::Other(anyhow::anyhow!(\"No kid found\"));\n        for key in &self.keyset.keys {\n            match &key.kid {\n                Some(kid) => log::debug!(\"Trying key {kid}\"),\n                None => log::debug!(\"Trying key without kid\"),\n            }\n            match self.validate_with_key(token, key).await {\n                Ok(claims) => return Ok(claims),\n                Err(e) => {\n                    last_error = e;\n                    log::debug!(\"Validating with JWKS key failed\");\n                    continue;\n                }\n            }\n        }\n        // None of the keys worked.\n        Err(last_error)\n    }\n}","sourceCodeStart":314,"sourceCodeEnd":350,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/3653d2ed498fddbd83b7062aa6bf8970e18635ec/crates/core/src/auth/token_validation.rs#L314-L350","documentation":"JwksValidator first tries the token header's kid against the keyset (a miss there yields KeyIDNotFound); 'No kid found' is the seed error of the fallback loop that tries every key, and it survives to the caller only when the keyset has no keys to try (or the loop never runs). In practice: the token header lacks a kid AND the fetched JWKS keyset is empty or unusable.","triggerScenarios":"Presenting a token whose header omits kid to a validator whose JWKS keys map is empty — e.g. the endpoint returned {\"keys\":[]}, keys are mid-rotation, or a stubbed/dev OIDC setup publishes no keys.","commonSituations":"Custom or internal OIDC providers that omit kid in signed tokens; JWKS endpoints misconfigured or returning empty sets; test harnesses with stub keysets.","solutions":["Curl the issuer's jwks_uri and confirm the keys array is non-empty; fix the provider/rotation state if it is empty","Ensure your token signer sets the kid header to a key present in the published JWKS","If you control both sides, publish at least one key and mint tokens referencing it via kid"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"# bash: confirm the JWKS actually publishes keys before wiring auth\ncurl -fsS \"$JWKS_URI\" | grep -q '\"kid\"' || echo \"JWKS has no usable keys\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure your token signer always sets the kid header referencing a published key","Alert when a JWKS keys array becomes empty (rotation bug) instead of waiting for auth failures"],"tags":["auth","jwt","jwks","oidc","key-management"],"backgroundTag":"jwt-signing-key-not-found","analyzedSha":"3653d2ed498fddbd83b7062aa6bf8970e18635ec","analyzedAt":"2026-09-06T02:33:02.036Z","contentChangedAt":"2026-09-06T02:33:02.036Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}