{"record":{"id":"487be0b80d75b035","repo":"Budibase/budibase","slug":"a-new-verification-key-is-required-when-changing-t","errorCode":null,"errorMessage":"A new verification key is required when changing the embed SSO algorithm","messagePattern":"A new verification key is required when changing the embed SSO algorithm","errorType":"http","errorClass":"HTTPError","httpStatus":400,"severity":"error","filePath":"packages/server/src/sdk/workspace/embedSSO/index.ts","lineNumber":44,"sourceCode":"  }\n  return encryption.decrypt(value.slice(SECRET_ENCODING_PREFIX.length))\n}\n\n/**\n * Encrypt the secret before storage. When the incoming key is the password\n * replacement sentinel, the existing (already encrypted) key is preserved so\n * the masked value sent to the builder round-trips without overwriting it.\n */\nexport function encodeConfigForStorage(\n  incoming: EmbedSSOConfig,\n  existing?: EmbedSSOConfig\n): EmbedSSOConfig {\n  let key = incoming.key\n  if (key === PASSWORD_REPLACEMENT) {\n    // the stored key is tied to its algorithm (shared secret vs EC/RSA public\n    // key), so it cannot be reused if the algorithm has changed\n    if (existing && existing.algorithm !== incoming.algorithm) {\n      throw new HTTPError(\n        \"A new verification key is required when changing the embed SSO algorithm\",\n        400\n      )\n    }\n    key = existing?.key || \"\"\n  } else {\n    key = encodeSecret(key)\n  }\n  return { ...incoming, key }\n}\n\n/**\n * Mask the secret so the builder can display and edit the config without ever\n * receiving the real key.\n */\nexport function maskConfigForBuilder(config: EmbedSSOConfig): EmbedSSOConfig {\n  return { ...config, key: config.key ? PASSWORD_REPLACEMENT : \"\" }\n}","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/embedSSO/index.ts#L26-L62","documentation":"This HTTP 400 error is thrown by encodeConfigForStorage when saving an embed SSO configuration whose key field is the PASSWORD_REPLACEMENT sentinel (meaning 'keep the stored key') while the algorithm differs from the previously stored configuration. Verification keys are tied to their algorithm family (HMAC shared secret vs EC/RSA public key), so silently reusing the old key under a new algorithm would produce tokens that can never verify. The library forces the caller to supply an explicit new key when the algorithm changes.","triggerScenarios":"Calling updateWorkspacePackage (via the workspace embed SSO save path) with an EmbedSSOConfig whose key is PASSWORD_REPLACEMENT while incoming.algorithm !== existing.algorithm, e.g. the builder UI round-trips the masked key but the user changed HS256 to RS256 in the same save.","commonSituations":"A user edits an embed SSO config in the builder and switches the signing algorithm while leaving the masked key field untouched; automation or API scripts that echo back the masked key placeholder from a GET and only change the algorithm field.","solutions":["Supply a fresh verification key in the request instead of the PASSWORD_REPLACEMENT placeholder when the algorithm is being changed","If the algorithm change was accidental, re-send the original algorithm along with the masked key so it round-trips","If you truly intend to keep the same key material, first fetch the existing config, decode the real key server-side, and submit it as an explicit new key","Split the update into two calls: one that only changes non-key fields with the masked key and same algorithm, then a second that sets the new algorithm together with the new key"],"exampleFix":"// before\nawait updateEmbedSSO({ ...existing, algorithm: \"RS256\", key: \"••••••••\" }) // masked placeholder, algorithm changed -> 400\n// after\nawait updateEmbedSSO({ ...existing, algorithm: \"RS256\", key: newRs256PublicKeyPem })","handlingStrategy":"validation","validationCode":"const changingAlgorithm = existing && incoming.algorithm !== existing.algorithm\nif (changingAlgorithm && (!incoming.key || incoming.key === PASSWORD_REPLACEMENT)) {\n  throw new Error(\"Provide a new verification key when changing the embed SSO algorithm\")\n}","typeGuard":null,"tryCatchPattern":"try {\n  await saveEmbedSSO(config)\n} catch (err) {\n  if (err instanceof HTTPError && err.status === 400 && /new verification key/.test(err.message)) {\n    promptUserForNewKey()\n  } else throw err\n}","preventionTips":["Always submit a fresh key whenever the algorithm field changes","Never round-trip the PASSWORD_REPLACEMENT sentinel together with an algorithm change","When editing configs programmatically, load the existing config and only send masked keys when the algorithm is unchanged"],"tags":["sso","jwt","validation","http-400"],"backgroundTag":"sso-key-algorithm-mismatch","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}