{"id":"322d45a9882deeb2","repo":"brianc/node-postgres","slug":"sasl-scram-server-final-message-server-signature","errorCode":null,"errorMessage":"SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature does not match","messagePattern":"SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature does not match","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/pg/lib/crypto/sasl.js","lineNumber":140,"sourceCode":"  const serverSignatureBytes = await crypto.hmacSha256(serverKey, authMessage)\n\n  session.message = 'SASLResponse'\n  session.serverSignature = Buffer.from(serverSignatureBytes).toString('base64')\n  session.response = clientFinalMessageWithoutProof + ',p=' + clientProof\n}\n\nfunction finalizeSession(session, serverData) {\n  if (session.message !== 'SASLResponse') {\n    throw new Error('SASL: Last message was not SASLResponse')\n  }\n  if (typeof serverData !== 'string') {\n    throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: serverData must be a string')\n  }\n\n  const { serverSignature } = parseServerFinalMessage(serverData)\n\n  if (serverSignature !== session.serverSignature) {\n    throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature does not match')\n  }\n}\n\n/**\n * printable       = %x21-2B / %x2D-7E\n *                   ;; Printable ASCII except \",\".\n *                   ;; Note that any \"printable\" is also\n *                   ;; a valid \"value\".\n */\nfunction isPrintableChars(text) {\n  if (typeof text !== 'string') {\n    throw new TypeError('SASL: text must be a string')\n  }\n  return text\n    .split('')\n    .map((_, i) => text.charCodeAt(i))\n    .every((c) => (c >= 0x21 && c <= 0x2b) || (c >= 0x2d && c <= 0x7e))\n}","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/brianc/node-postgres/blob/c5e8c9a57bff6d9160ec5dbd5c4f4c1e4c460711/packages/pg/lib/crypto/sasl.js#L122-L158","documentation":"Thrown during SCRAM session finalization (sasl.js:139-141) when the server's signature (v= attribute in the SCRAM-SERVER-FINAL-MESSAGE) does not match the signature the client computed locally. The server signature is an HMAC-SHA-256 over the auth message using the Server Key derived from the password; a mismatch means the password is wrong, or the server is not the legitimate holder of the stored verifier. This is the primary 'authentication failed' signal in SCRAM-SHA-256 and indicates incorrect credentials with high confidence.","triggerScenarios":"The password provided by the client does not match the PostgreSQL role's stored SCRAM verifier. The client computed session.serverSignature during continueSession and compares it against the v= value from the server's final message in finalizeSession.","commonSituations":"Wrong password (typo, stale credential, rotated password not updated in config). The role's password was changed on the server but the client config/env var still has the old one. Copy-paste introduced a trailing newline or space in the password. Connecting to the wrong database/role.","solutions":["Verify the password is correct: test with psql using the same credentials.","Check for trailing whitespace/newlines in the password from env vars or config files (trim() it).","Reset the role password on the server and update the client configuration to match."],"exampleFix":"// before\nconst password = fs.readFileSync('.pgpass', 'utf8'); // may have trailing \\n\n\n// after\nconst password = fs.readFileSync('.pgpass', 'utf8').trim();\n// or reset on server:\n// ALTER ROLE myuser WITH PASSWORD 'correctpass';","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await client.connect();\n} catch (err) {\n  if (/server signature does not match/i.test(err.message)) {\n    console.error('Authentication failed: wrong password or corrupted credentials.');\n    // prompt for correct credentials or rotate\n  }\n  throw err;\n}","preventionTips":["Verify credentials with psql using the same connection string before debugging code.","Trim whitespace/newlines from passwords read from files or env vars.","Use a secrets manager to avoid copy-paste errors and stale credentials.","When rotating passwords, update all client configs atomically."],"tags":["sasl","authentication","security","password","credentials"],"analyzedSha":"c5e8c9a57bff6d9160ec5dbd5c4f4c1e4c460711","analyzedAt":"2026-08-03T18:47:28.334Z","schemaVersion":2}