{"record":{"id":"f05d374de1beda9e","repo":"gchq/CyberChef","slug":"err-f05d37","errorCode":null,"errorMessage":"${err}","messagePattern":"\\$\\{err\\}","errorType":"exception","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/operations/JWTVerify.mjs","lineNumber":57,"sourceCode":"     * @param {string} input\n     * @param {Object[]} args\n     * @returns {string}\n     */\n    run(input, args) {\n        const [key] = args;\n        const algos = JWT_ALGORITHMS;\n        algos[algos.indexOf(\"None\")] = \"none\";\n\n        try {\n            const verified = jwt.verify(input, key, { algorithms: algos });\n\n            if (Object.prototype.hasOwnProperty.call(verified, \"name\") && verified.name === \"JsonWebTokenError\") {\n                throw new OperationError(verified.message);\n            }\n\n            return verified;\n        } catch (err) {\n            throw new OperationError(err);\n        }\n    }\n\n}\n\nexport default JWTVerify;\n","sourceCodeStart":39,"sourceCodeEnd":64,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/operations/JWTVerify.mjs#L39-L64","documentation":"Thrown by JWT Verify's outer catch when jwt.verify() throws directly - the common path in modern jsonwebtoken. The thrown Error (JsonWebTokenError, NotBeforeError, TokenExpiredError, or a key/algorithm error) is wrapped as OperationError(err) and stringified. Note: because the inner JsonWebTokenError path (error 450) throws inside the same try, that OperationError is re-caught here and double-wrapped.","triggerScenarios":"Expired token (TokenExpiredError), not-yet-valid token (NotBeforeError), invalid signature that throws, malformed key/PEM, or any verify failure the library raises by throwing. Also the double-wrap case where error 450's thrown OperationError lands here.","commonSituations":"Testing with an expired token. Clock skew causing NotBefore/TokenExpired errors. Wrong key type. The double-wrap making the surfaced message nested/less readable.","solutions":["Check the token's exp/nbf claims against a synchronised clock before verifying.","Use the correct secret/public key matching the signing algorithm.","Distinguish error types by inspecting err.name before wrapping (TokenExpiredError vs JsonWebTokenError).","Avoid the double-wrap by rethrowing OperationError unchanged in the outer catch."],"exampleFix":"// before: outer catch re-wraps every error indiscriminately\ncatch (err) { throw new OperationError(err); }\n// after: rethrow OperationError as-is, wrap others with their name\nif (err instanceof OperationError) throw err;\nthrow new OperationError(`${err.name}: ${err.message}`);","handlingStrategy":"try-catch","validationCode":"function checkClaims(token) {\n  const parts = token.split('.');\n  const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString());\n  const now = Math.floor(Date.now() / 1000);\n  if (payload.exp && now >= payload.exp) throw new Error('Token expired');\n  if (payload.nbf && now < payload.nbf) throw new Error('Token not yet valid');\n  return payload;\n}","typeGuard":"function isNonExpiredToken(token, skewSec = 0) {\n  try {\n    const p = JSON.parse(Buffer.from(token.split('.')[1], 'base64url').toString());\n    const now = Math.floor(Date.now() / 1000) + skewSec;\n    return (!p.exp || now < p.exp) && (!p.nbf || now >= p.nbf);\n  } catch { return false; }\n}","tryCatchPattern":"try {\n  return chef.JWTVerify(token, { key });\n} catch (e) {\n  const msg = String(e.message || e);\n  if (/expired/i.test(msg)) throw new Error('Token expired - refresh it');\n  if (/not yet valid|nbf/i.test(msg)) throw new Error('Token not yet valid - check clock skew');\n  throw new Error(`JWT verify failed: ${msg}`);\n}","preventionTips":["Check exp/nbf against a synchronised clock before verifying.","Use the correct secret/public key for the signing algorithm.","Beware the double-wrap: unwrap nested messages when reading errors."],"tags":["jwt","crypto","verification","bug"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}