From 845a5a0dada33a3950ffed4f7d6a52090fa6de9a Mon Sep 17 00:00:00 2001 From: Tobias Bocanegra Date: Thu, 27 Oct 2022 08:49:19 +0200 Subject: [PATCH] feat: also support x-fwd-scheme (#172) --- src/utils/auth-cookie.js | 8 ++++---- src/utils/auth.js | 9 +++++---- test/utils/auth-cookie.test.js | 13 ++++++++++++- test/utils/auth.test.js | 24 ++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/utils/auth-cookie.js b/src/utils/auth-cookie.js index 6f6728b8..2cce461c 100644 --- a/src/utils/auth-cookie.js +++ b/src/utils/auth-cookie.js @@ -11,21 +11,21 @@ */ import { parse, serialize } from 'cookie'; -export function clearAuthCookie() { +export function clearAuthCookie(secure) { return serialize('hlx-auth-token', '', { path: '/', httpOnly: true, - secure: true, + secure, expires: new Date(0), sameSite: 'lax', }); } -export function setAuthCookie(idToken) { +export function setAuthCookie(idToken, secure) { return serialize('hlx-auth-token', idToken, { path: '/', httpOnly: true, - secure: true, + secure, sameSite: 'lax', }); } diff --git a/src/utils/auth.js b/src/utils/auth.js index f0f71700..a7f191e4 100644 --- a/src/utils/auth.js +++ b/src/utils/auth.js @@ -91,7 +91,8 @@ function getRequestHostAndProto(state, req) { if (!host) { host = state.config.host; } - const proto = req.headers.get('x-forwarded-proto') || 'https'; + // fastly overrides the x-forwarded-proto, so we use x-forwarded-scheme + const proto = req.headers.get('x-forwarded-scheme') || req.headers.get('x-forwarded-proto') || 'https'; state.log.info(`request host is: ${host} (${proto})`); return { host, @@ -218,7 +219,7 @@ export class AuthInfo { res.status = 302; res.body = ''; res.headers.set('location', url.href); - res.headers.set('set-cookie', clearAuthCookie()); + res.headers.set('set-cookie', clearAuthCookie(proto === 'https')); res.headers.set('cache-control', 'no-store, private, must-revalidate'); res.error = 'moved'; } @@ -299,12 +300,12 @@ export class AuthInfo { // ctx.attributes.authInfo?.withCookieInvalid(false); const location = state.createExternalLocation(req.params.state.requestPath || '/'); - log.info('[auth] redirecting to home page with id_token cookie', location); + log.info('[auth] redirecting to original page with hlx-auth-token cookie: ', location); res.status = 302; res.body = `please go to ${location}`; res.headers.set('location', location); res.headers.set('content-tye', 'text/plain'); - res.headers.set('set-cookie', setAuthCookie(idToken)); + res.headers.set('set-cookie', setAuthCookie(idToken, req.params.state.requestProto === 'https')); res.headers.set('cache-control', 'no-store, private, must-revalidate'); res.error = 'moved'; } diff --git a/test/utils/auth-cookie.test.js b/test/utils/auth-cookie.test.js index 5c90720c..cd5dc917 100644 --- a/test/utils/auth-cookie.test.js +++ b/test/utils/auth-cookie.test.js @@ -16,12 +16,23 @@ import { clearAuthCookie, getAuthCookie, setAuthCookie } from '../../src/utils/a describe('Auth Cookie Test', () => { it('clears the auth cookie', () => { - assert.strictEqual(clearAuthCookie(), 'hlx-auth-token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Secure; SameSite=Lax'); + assert.strictEqual(clearAuthCookie(), 'hlx-auth-token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; SameSite=Lax'); + }); + + it('clears the auth cookie (secure)', () => { + assert.strictEqual(clearAuthCookie(true), 'hlx-auth-token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Secure; SameSite=Lax'); }); it('sets the auth cookie', () => { assert.strictEqual( setAuthCookie('1234'), + 'hlx-auth-token=1234; Path=/; HttpOnly; SameSite=Lax', + ); + }); + + it('sets the auth cookie (secure)', () => { + assert.strictEqual( + setAuthCookie('1234', true), 'hlx-auth-token=1234; Path=/; HttpOnly; Secure; SameSite=Lax', ); }); diff --git a/test/utils/auth.test.js b/test/utils/auth.test.js index fd510e63..2af886bf 100644 --- a/test/utils/auth.test.js +++ b/test/utils/auth.test.js @@ -398,6 +398,30 @@ describe('AuthInfo tests', () => { }); }); + it('redirects to the login page (xfh, scheme)', async () => { + const authInfo = AuthInfo + .Default() + .withIdp(idpFakeTestIDP); + + const state = new PipelineState({}); + const req = new PipelineRequest('https://localhost', { + headers: { + 'x-forwarded-host': 'localhost', + 'x-forwarded-scheme': 'http', + 'x-forwarded-proto': 'ftp', + }, + }); + const res = new PipelineResponse(); + await authInfo.redirectToLogin(state, req, res); + assert.strictEqual(res.status, 302); + const reqState = new URL(res.headers.get('location')).searchParams.get('state'); + assert.deepStrictEqual(decodeJwt(reqState), { + requestHost: 'localhost', + requestProto: 'http', + requestPath: '/', + }); + }); + it('redirects to the login page (xfh - multi)', async () => { const authInfo = AuthInfo .Default()