fix: ensure that OIDC correlation cookies are present in callback

This commit is contained in:
Michael Thomas
2026-03-27 11:44:23 -04:00
parent e2b382f133
commit 419d1bc99b
2 changed files with 71 additions and 0 deletions

View File

@@ -768,5 +768,63 @@ describe('OpenID Connect', () => {
assert.strictEqual(response.status, 403);
assert.strictEqual(response.body.error, ApiErrorCode.Unauthorized);
});
it('rejects callback when correlation cookies are missing', async function () {
await setupFetchMock();
const callbackUrl = new URL(OIDC_REDIRECT_URL);
callbackUrl.searchParams.set('code', '123456');
callbackUrl.searchParams.set('state', 'somestate');
// Send callback with no signed cookies at all
const response = await request(app)
.post('/auth/oidc/callback/test')
.set('Accept', 'application/json')
.send({ callbackUrl: callbackUrl.toString() });
assert.strictEqual(response.status, 400);
assert.strictEqual(
response.body.error,
ApiErrorCode.OidcAuthorizationFailed
);
});
it('rejects callback when only one correlation cookie is present', async function () {
await setupFetchMock();
// Perform login to get only the state cookie
const loginResponse = await request(app)
.get('/auth/oidc/login/test')
.set('Accept', 'application/json');
assert.strictEqual(loginResponse.status, 200);
const redirectUrl = new URL(loginResponse.body.redirectUrl);
const state = redirectUrl.searchParams.get('state');
// Extract only the state cookie, dropping the PKCE verifier cookie
const cookies = loginResponse.get('Set-Cookie');
assert.notStrictEqual(cookies, undefined);
const stateCookieOnly = cookies!
.filter((c) => c.includes('oidc-state'))
.map((c) => c.split(';')[0])
.join('; ');
const callbackUrl = new URL(OIDC_REDIRECT_URL);
callbackUrl.searchParams.set('code', '123456');
if (state) callbackUrl.searchParams.set('state', state);
const response = await request(app)
.post('/auth/oidc/callback/test')
.set('Accept', 'application/json')
.set('Cookie', stateCookieOnly)
.send({ callbackUrl: callbackUrl.toString() });
assert.strictEqual(response.status, 400);
assert.strictEqual(
response.body.error,
ApiErrorCode.OidcAuthorizationFailed
);
});
});
});

View File

@@ -798,6 +798,19 @@ authRoutes.post(
const pkceCodeVerifier: string | undefined =
req.signedCookies[OIDC_CODE_VERIFIER_KEY];
const expectedState: string | undefined = req.signedCookies[OIDC_STATE_KEY];
if (!pkceCodeVerifier || !expectedState) {
logger.warn('Rejected OIDC callback without correlation cookies', {
label: 'Auth',
provider: provider.slug,
ip: req.ip,
});
return next({
status: 400,
error: ApiErrorCode.OidcAuthorizationFailed,
});
}
res.clearCookie(OIDC_CODE_VERIFIER_KEY);
res.clearCookie(OIDC_STATE_KEY);