From b30ef8a57d276ac72e729157a843ea5722a09da3 Mon Sep 17 00:00:00 2001 From: meza Date: Sat, 8 Jul 2023 17:04:29 +0100 Subject: [PATCH] feat: added the option to not use post for the callback. Helps with LAX cookies --- README.md | 3 ++- src/Auth0RemixTypes.ts | 1 + src/__snapshots__/index.test.ts.snap | 16 ++++---------- src/index.test.ts | 29 +++++++++++++++++++++++++ src/index.ts | 32 +++++++++++++++++++--------- 5 files changed, 58 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 0c0706d2..39563793 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,8 @@ export const authenticator = new Auth0RemixServer({ clientDetails: { domain: process.env.AUTH0_DOMAIN, clientID: process.env.AUTH0_CLIENT_ID, - clientSecret: process.env.AUTH0_CLIENT_SECRET + clientSecret: process.env.AUTH0_CLIENT_SECRET, + usePost: true // optional, defaults to true }, callbackURL: `${process.env.APP_DOMAIN}/auth/callback`, refreshTokenRotationEnabled: true, diff --git a/src/Auth0RemixTypes.ts b/src/Auth0RemixTypes.ts index 93ba8b5d..6a0443fe 100644 --- a/src/Auth0RemixTypes.ts +++ b/src/Auth0RemixTypes.ts @@ -63,6 +63,7 @@ export interface ClientCredentials { clientSecret: string; audience: string; organization?: string | undefined; + usePost?: boolean | undefined; } export interface SessionStore { diff --git a/src/__snapshots__/index.test.ts.snap b/src/__snapshots__/index.test.ts.snap index 0f6bb3f4..b3a29bec 100644 --- a/src/__snapshots__/index.test.ts.snap +++ b/src/__snapshots__/index.test.ts.snap @@ -14,18 +14,10 @@ exports[`Auth0 Remix Server > logging out > calls the correct url 1`] = `"https: exports[`Auth0 Remix Server > logging out > includes the headers supplied 1`] = `"https://test.domain.com/v2/logout?client_id=clientId&returnTo=http%3A%2F%2Flocalhost%3A3000%2Flogout-with-headers"`; -exports[`Auth0 Remix Server > the authorization process > adds custom callback url parameters 1`] = `"https://test.domain.com/authorize?response_type=code&response_mode=form_post&client_id=clientId&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauth0%2Fcallback%3Ftest1%3DtestA%26test2%3DtestB%26test3%3DtestC&scope=offline_access+openid+profile+email&audience=https%3A%2F%2Ftest.domain.com%2Fapi%2Fv2%2F"`; +exports[`Auth0 Remix Server > the authorization process > adds custom callback url parameters 1`] = `"https://test.domain.com/authorize?redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauth0%2Fcallback%3Ftest1%3DtestA%26test2%3DtestB%26test3%3DtestC&response_type=code&response_mode=form_post&client_id=clientId&scope=offline_access+openid+profile+email&audience=https%3A%2F%2Ftest.domain.com%2Fapi%2Fv2%2F"`; -exports[`Auth0 Remix Server > the authorization process > adds the connection when needed 1`] = `"https://test.domain.com/authorize?response_type=code&response_mode=form_post&client_id=clientId&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauth0%2Fcallback&scope=offline_access+openid+profile+email&audience=https%3A%2F%2Ftest.domain.com%2Fapi%2Fv2%2F&connection=google"`; +exports[`Auth0 Remix Server > the authorization process > adds the organisation if needed 1`] = `"https://test.domain.com/authorize?redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauth0%2Fcallback&response_type=code&response_mode=form_post&client_id=clientId&scope=offline_access+openid+profile+email&audience=https%3A%2F%2Ftest.domain.com%2Fapi%2Fv2%2F&organization=test-org"`; -exports[`Auth0 Remix Server > the authorization process > adds the organisation if needed 1`] = `"https://test.domain.com/authorize?response_type=code&response_mode=form_post&client_id=clientId&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauth0%2Fcallback&scope=offline_access+openid+profile+email&audience=https%3A%2F%2Ftest.domain.com%2Fapi%2Fv2%2F&organization=test-org"`; +exports[`Auth0 Remix Server > the authorization process > forces the signup if asked 1`] = `"https://test.domain.com/authorize?redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauth0%2Fcallback&response_type=code&response_mode=form_post&client_id=clientId&scope=offline_access+openid+profile+email&audience=https%3A%2F%2Ftest.domain.com%2Fapi%2Fv2%2F&screen_hint=signup"`; -exports[`Auth0 Remix Server > the authorization process > does silent auth if asked 1`] = `"https://test.domain.com/authorize?response_type=code&response_mode=form_post&client_id=clientId&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauth0%2Fcallback&scope=offline_access+openid+profile+email&audience=https%3A%2F%2Ftest.domain.com%2Fapi%2Fv2%2F&prompt=none"`; - -exports[`Auth0 Remix Server > the authorization process > forces the login if asked 1`] = `"https://test.domain.com/authorize?response_type=code&response_mode=form_post&client_id=clientId&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauth0%2Fcallback&scope=offline_access+openid+profile+email&audience=https%3A%2F%2Ftest.domain.com%2Fapi%2Fv2%2F&prompt=login"`; - -exports[`Auth0 Remix Server > the authorization process > forces the signup if asked 1`] = `"https://test.domain.com/authorize?response_type=code&response_mode=form_post&client_id=clientId&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauth0%2Fcallback&scope=offline_access+openid+profile+email&audience=https%3A%2F%2Ftest.domain.com%2Fapi%2Fv2%2F&screen_hint=signup"`; - -exports[`Auth0 Remix Server > the authorization process > redirects to the authorization endpoint 1`] = `"https://test.domain.com/authorize?response_type=code&response_mode=form_post&client_id=clientId&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauth0%2Fcallback&scope=offline_access+openid+profile+email&audience=https%3A%2F%2Ftest.domain.com%2Fapi%2Fv2%2F"`; - -exports[`Auth0 Remix Server > the authorization process > works correctly when both are asked 1`] = `"https://test.domain.com/authorize?response_type=code&response_mode=form_post&client_id=clientId&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauth0%2Fcallback&scope=offline_access+openid+profile+email&audience=https%3A%2F%2Ftest.domain.com%2Fapi%2Fv2%2F&prompt=login&screen_hint=signup"`; +exports[`Auth0 Remix Server > the authorization process > redirects to the authorization endpoint 1`] = `"https://test.domain.com/authorize?redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauth0%2Fcallback&response_type=code&response_mode=form_post&client_id=clientId&scope=offline_access+openid+profile+email&audience=https%3A%2F%2Ftest.domain.com%2Fapi%2Fv2%2F"`; diff --git a/src/index.test.ts b/src/index.test.ts index 5e653807..044d73fc 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -64,6 +64,35 @@ describe('Auth0 Remix Server', () => { expect(redirectUrl).toMatchSnapshot(); }); + it('uses post when post is not set', ({ authOptions }) => { + const authorizer = new Auth0RemixServer(authOptions); + + expect(() => authorizer.authorize()).toThrowError(redirectError); // a redirect happened + + const redirectUrl = vi.mocked(redirect).mock.calls[0][0]; + expect(redirectUrl).toContain('response_mode=form_post'); + }); + + it('uses post when post is set to true', ({ authOptions }) => { + authOptions.clientDetails.usePost = true; + const authorizer = new Auth0RemixServer(authOptions); + + expect(() => authorizer.authorize()).toThrowError(redirectError); // a redirect happened + + const redirectUrl = vi.mocked(redirect).mock.calls[0][0]; + expect(redirectUrl).toContain('response_mode=form_post'); + }); + + it('uses get when post is set to false', ({ authOptions }) => { + authOptions.clientDetails.usePost = false; + const authorizer = new Auth0RemixServer(authOptions); + + expect(() => authorizer.authorize()).toThrowError(redirectError); // a redirect happened + + const redirectUrl = vi.mocked(redirect).mock.calls[0][0]; + expect(redirectUrl).not.toContain('response_mode=form_post'); + }); + it('forces the login if asked', ({ authOptions }) => { const authorizer = new Auth0RemixServer(authOptions); diff --git a/src/index.ts b/src/index.ts index 2a2e5613..36cc64a1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -63,7 +63,8 @@ export class Auth0RemixServer { clientID: auth0RemixOptions.clientDetails.clientID, clientSecret: auth0RemixOptions.clientDetails.clientSecret, audience: auth0RemixOptions.clientDetails.audience || `${this.domain}/api/v2/`, - organization: auth0RemixOptions.clientDetails.organization + organization: auth0RemixOptions.clientDetails.organization, + usePost: auth0RemixOptions.clientDetails.usePost }; this.session = { store: auth0RemixOptions.session.store, @@ -105,12 +106,6 @@ export class Auth0RemixServer { } public authorize(opts: AuthorizeOptions = {}) { - const scope = [ - 'offline_access', // required for refresh token - 'openid', // required for id_token and the /userinfo api endpoint - 'profile', - 'email' - ]; const cbUrl = new URL(this.callbackURL); if (opts.callbackParams) { @@ -120,10 +115,28 @@ export class Auth0RemixServer { } const authorizationURL = new URL(this.auth0Urls.authorizationURL); + authorizationURL.searchParams.set('redirect_uri', cbUrl.toString()); + + this.setAuthorizationParameters(authorizationURL, opts); + + throw redirect(authorizationURL.toString()); + } + + private setAuthorizationParameters(authorizationURL: URL, opts: AuthorizeOptions = {}) { + const scope = [ + 'offline_access', // required for refresh token + 'openid', // required for id_token and the /userinfo api endpoint + 'profile', + 'email' + ]; + authorizationURL.searchParams.set('response_type', 'code'); - authorizationURL.searchParams.set('response_mode', 'form_post'); + + if (this.clientCredentials.usePost !== false) { + authorizationURL.searchParams.set('response_mode', 'form_post'); + } + authorizationURL.searchParams.set('client_id', this.clientCredentials.clientID); - authorizationURL.searchParams.set('redirect_uri', cbUrl.toString()); authorizationURL.searchParams.set('scope', scope.join(' ')); authorizationURL.searchParams.set('audience', this.clientCredentials.audience); if (this.clientCredentials.organization) { @@ -142,7 +155,6 @@ export class Auth0RemixServer { authorizationURL.searchParams.set('connection', opts.connection); } - throw redirect(authorizationURL.toString()); } public async handleCallback(request: Request, options: HandleCallbackOptions): Promise {