fix(auth): wire dual-key JWT verification into TokenService for WebSocket auth
Extract shared `verifyWithRotation` helper and `makeSecretOrKeyProvider` into `jwt-rotation.ts` so both REST (passport-jwt strategy) and WebSocket (TokenService.verifyAccessToken) paths honour JWT_SECRET_PREVIOUS during secret rotation. Add env-validation for optional previous secrets and document the rotation policy for WebSocket sessions. Resolves GOO-237 Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
This commit is contained in:
@@ -45,6 +45,17 @@ const REQUIRED_WHEN_USED: ReadonlyMap<string, string> = new Map([
|
||||
* Known placeholder values that must never be used as real secrets.
|
||||
* Comparison is case-insensitive to catch common variants.
|
||||
*/
|
||||
/**
|
||||
* Previous-version secrets used during key rotation. Validated if set but never
|
||||
* required. Note: JWT_REFRESH_SECRET_PREVIOUS currently has no runtime consumer
|
||||
* because refresh tokens are opaque random bytes, not JWTs — the variable is
|
||||
* accepted here for forward-compatibility should the refresh mechanism change.
|
||||
*/
|
||||
const OPTIONAL_PREVIOUS_SECRETS: readonly string[] = [
|
||||
'JWT_SECRET_PREVIOUS',
|
||||
'JWT_REFRESH_SECRET_PREVIOUS',
|
||||
];
|
||||
|
||||
const FORBIDDEN_SECRET_VALUES: readonly string[] = [
|
||||
'change_me',
|
||||
'changeme',
|
||||
@@ -127,6 +138,25 @@ export function validateEnv(): void {
|
||||
);
|
||||
}
|
||||
|
||||
// Validate optional previous secrets if they are set (rotation window).
|
||||
const prevSecretErrors: string[] = [];
|
||||
for (const key of OPTIONAL_PREVIOUS_SECRETS) {
|
||||
const value = process.env[key];
|
||||
if (value) {
|
||||
const error = validateJwtSecret(key, value);
|
||||
if (error) {
|
||||
prevSecretErrors.push(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prevSecretErrors.length > 0) {
|
||||
throw new Error(
|
||||
`Insecure previous-secret configuration:\n ${prevSecretErrors.join('\n ')}\n` +
|
||||
'Previous secrets must meet the same strength requirements as primary secrets.',
|
||||
);
|
||||
}
|
||||
|
||||
if (!isProduction) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user