{"record":{"id":"c67c7b664a2dc0a0","repo":"paperclipai/paperclip","slug":"cloud-runtime-identity-protected-header-is-invalid","errorCode":null,"errorMessage":"Cloud runtime identity protected header is invalid","messagePattern":"Cloud runtime identity protected header is invalid","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/cloud-runtime-identity.ts","lineNumber":277,"sourceCode":"\nfunction verifyClaims(input: {\n  compactJws: string;\n  env: NodeJS.ProcessEnv;\n  now: Date;\n}): RuntimeIdentityClaims {\n  const parts = input.compactJws.split(\".\");\n  if (parts.length !== 3 || parts.some((part) => part.length === 0)) {\n    throw new Error(\"Cloud runtime identity assertion is not a compact JWS\");\n  }\n  const [encodedHeader, encodedPayload, encodedSignature] = parts;\n  const header = decodeJsonPart(encodedHeader, \"protected header\");\n  if (\n    header.alg !== \"EdDSA\"\n    || header.typ !== CLOUD_RUNTIME_IDENTITY_JWS_TYPE\n    || typeof header.kid !== \"string\"\n    || !header.kid\n  ) {\n    throw new Error(\"Cloud runtime identity protected header is invalid\");\n  }\n  const key = publicKeyForKid(input.env, header.kid);\n  const signature = Buffer.from(encodedSignature, \"base64url\");\n  const signingInput = Buffer.from(`${encodedHeader}.${encodedPayload}`, \"ascii\");\n  if (!verify(null, signingInput, key, signature)) {\n    throw new Error(\"Cloud runtime identity signature is invalid\");\n  }\n\n  const payload = decodeJsonPart(encodedPayload, \"payload\");\n  const nowSeconds = Math.floor(input.now.getTime() / 1000);\n  if (\n    payload.v !== 1\n    || payload.iss !== CLOUD_RUNTIME_IDENTITY_ISSUER\n    || payload.aud !== CLOUD_RUNTIME_IDENTITY_AUDIENCE\n    || typeof payload.sub !== \"string\"\n    || typeof payload.claimId !== \"string\"\n    || typeof payload.previousOrigin !== \"string\"\n    || typeof payload.canonicalOrigin !== \"string\"","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/cloud-runtime-identity.ts#L259-L295","documentation":"Thrown by verifyClaims when the decoded protected header is present but does not meet the required shape: alg must be exactly 'EdDSA', typ must equal CLOUD_RUNTIME_IDENTITY_JWS_TYPE, and kid must be a non-empty string. This prevents algorithm-confusion attacks and ensures the right key can be selected.","triggerScenarios":"decodeJsonPart succeeded on the header, but header.alg is missing or not 'EdDSA' (e.g. 'RS256', 'none'), header.typ differs from the expected type constant, or header.kid is absent/empty/not a string.","commonSituations":"Token minted by a different JWT library with default alg/typ values; alg accidentally downgraded to 'none' or HS256; kid omitted when signing; assertion generated for a different token type/audience by a sibling service.","solutions":["Re-sign the assertion with alg EdDSA and typ set to CLOUD_RUNTIME_IDENTITY_JWS_TYPE (check the constant in this file)","Always include a non-empty kid in the JOSE header matching a JWKS entry","Inspect the decoded header (base64url-decode segment 1) to see what the issuer actually put there","Use the official control-plane signing tooling instead of a generic JWT library"],"exampleFix":"// before\nconst header = { alg: \"none\", kid };\n// after\nconst header = { alg: \"EdDSA\", typ: CLOUD_RUNTIME_IDENTITY_JWS_TYPE, kid: \"k1\" };","handlingStrategy":"validation","validationCode":"function hasValidJwsHeader(token: string, expectedTyp: string): boolean {\n  try {\n    const h = JSON.parse(Buffer.from(token.split(\".\")[0], \"base64url\").toString(\"utf8\")) as Record<string, unknown>;\n    return h.alg === \"EdDSA\" && h.typ === expectedTyp && typeof h.kid === \"string\" && h.kid.length > 0;\n  } catch { return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n  verifyCloudRuntimeIdentityAssertion({ compactJws: assertion, expectedPreviousOrigin: prev });\n} catch (e) {\n  if (String((e as Error).message).includes(\"protected header is invalid\")) {\n    logger.error(\"Assertion header alg/typ/kid mismatch — re-mint with EdDSA and the protocol typ\", { error: e });\n  } else throw e;\n}","preventionTips":["Pin alg to EdDSA when signing; never allow algorithm negotiation or 'none'","Set typ to the exact CLOUD_RUNTIME_IDENTITY_JWS_TYPE constant when minting","Always include a non-empty kid that exists in the verifier JWKS","Inspect headers of tokens from new issuers before wiring them into this flow"],"tags":["jwt","header-validation","algorithm-confusion","security"],"backgroundTag":"invalid-argument-value","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}