fix(19): IN-04 enforce single-use OIDC-link nonce to prevent state replay
This commit is contained in:
@@ -42,6 +42,7 @@ import {
|
||||
} from '../broker/credentialSync.js';
|
||||
import { hashPassword, verifyPassword } from '../auth/localCredentials.js';
|
||||
import { resolveOidcConfig, discoverAuthorizationEndpoint } from '../auth/oidcConfig.js';
|
||||
import { registerLinkNonce } from '../auth/linkNonceStore.js';
|
||||
// Side-effect import: brings in the ContextVariableMap augmentation for c.get('user')
|
||||
import '../auth/devBypass.js';
|
||||
|
||||
@@ -319,12 +320,18 @@ meRouter.post('/link-oidc', async (c) => {
|
||||
// T-19-09: nonce prevents state replay attacks (each link attempt gets a fresh nonce)
|
||||
const nonce = randomBytes(16).toString('hex');
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const exp = now + 600; // 10-minute window
|
||||
const signedState = await Jwt.sign(
|
||||
{ linkUserId: currentUserId, nonce, iat: now, exp: now + 600 }, // 10-minute window
|
||||
{ linkUserId: currentUserId, nonce, iat: now, exp },
|
||||
secret,
|
||||
'HS256',
|
||||
);
|
||||
|
||||
// IN-04: record the nonce so /callback can enforce SINGLE USE. Without this the signed
|
||||
// state JWT is fully replayable for its 10-minute signature lifetime and the nonce is
|
||||
// decorative. registerLinkNonce keeps it valid only until the state's own exp.
|
||||
registerLinkNonce(nonce, exp);
|
||||
|
||||
// Build the OIDC authorization URL if OIDC is configured (else return null — PWA disables button).
|
||||
// WR-04: resolve issuer/clientId/redirectUri from env-OR-app_config (single source of truth,
|
||||
// consistent with /api/auth/mode and the OIDC fallback middleware) so a wizard-configured
|
||||
|
||||
Reference in New Issue
Block a user