From df1433777ac532879f56ead786df4754f19a0b5c Mon Sep 17 00:00:00 2001 From: Stefanos Anagnostou Date: Thu, 20 Jun 2024 20:21:40 +0300 Subject: [PATCH 1/5] fix(clerkjs): Set localhost SameSite=None cookies as Secure --- .changeset/plenty-pandas-leave.md | 5 +++++ .../src/core/auth/cookies/clientUat.ts | 3 ++- .../src/core/auth/cookies/devBrowser.ts | 3 ++- .../clerk-js/src/core/auth/cookies/session.ts | 3 ++- .../src/core/auth/getSecureAttribute.ts | 20 +++++++++++++++++++ packages/clerk-js/src/utils/runtime.ts | 7 +------ 6 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 .changeset/plenty-pandas-leave.md create mode 100644 packages/clerk-js/src/core/auth/getSecureAttribute.ts diff --git a/.changeset/plenty-pandas-leave.md b/.changeset/plenty-pandas-leave.md new file mode 100644 index 00000000000..18746e120ad --- /dev/null +++ b/.changeset/plenty-pandas-leave.md @@ -0,0 +1,5 @@ +--- +"@clerk/clerk-js": patch +--- + +Set the localhost cookies with the Secure attribute diff --git a/packages/clerk-js/src/core/auth/cookies/clientUat.ts b/packages/clerk-js/src/core/auth/cookies/clientUat.ts index f731ce9dab8..332e001c767 100644 --- a/packages/clerk-js/src/core/auth/cookies/clientUat.ts +++ b/packages/clerk-js/src/core/auth/cookies/clientUat.ts @@ -4,6 +4,7 @@ import type { ClientResource } from '@clerk/types'; import { inCrossOriginIframe } from '../../../utils'; import { getCookieDomain } from '../getCookieDomain'; +import { getSecureAttribute } from '../getSecureAttribute'; const CLIENT_UAT_COOKIE_NAME = '__client_uat'; @@ -28,7 +29,7 @@ export const createClientUatCookie = (): ClientUatCookieHandler => { const set = (client: ClientResource | undefined) => { const expires = addYears(Date.now(), 1); const sameSite = inCrossOriginIframe() ? 'None' : 'Strict'; - const secure = window.location.protocol === 'https:'; + const secure = getSecureAttribute(sameSite); const domain = getCookieDomain(); // '0' indicates the user is signed out diff --git a/packages/clerk-js/src/core/auth/cookies/devBrowser.ts b/packages/clerk-js/src/core/auth/cookies/devBrowser.ts index d32555644b9..270dbc67750 100644 --- a/packages/clerk-js/src/core/auth/cookies/devBrowser.ts +++ b/packages/clerk-js/src/core/auth/cookies/devBrowser.ts @@ -3,6 +3,7 @@ import { addYears } from '@clerk/shared/date'; import { DEV_BROWSER_JWT_KEY } from '@clerk/shared/devBrowser'; import { inCrossOriginIframe } from '../../../utils'; +import { getSecureAttribute } from '../getSecureAttribute'; export type DevBrowserCookieHandler = { set: (jwt: string) => void; @@ -24,7 +25,7 @@ export const createDevBrowserCookie = (): DevBrowserCookieHandler => { const set = (jwt: string) => { const expires = addYears(Date.now(), 1); const sameSite = inCrossOriginIframe() ? 'None' : 'Lax'; - const secure = window.location.protocol === 'https:'; + const secure = getSecureAttribute(sameSite); return devBrowserCookie.set(jwt, { expires, diff --git a/packages/clerk-js/src/core/auth/cookies/session.ts b/packages/clerk-js/src/core/auth/cookies/session.ts index e36899ad7ed..85e4c08d753 100644 --- a/packages/clerk-js/src/core/auth/cookies/session.ts +++ b/packages/clerk-js/src/core/auth/cookies/session.ts @@ -2,6 +2,7 @@ import { createCookieHandler } from '@clerk/shared/cookie'; import { addYears } from '@clerk/shared/date'; import { inCrossOriginIframe } from '../../../utils'; +import { getSecureAttribute } from '../getSecureAttribute'; const SESSION_COOKIE_NAME = '__session'; @@ -23,7 +24,7 @@ export const createSessionCookie = (): SessionCookieHandler => { const set = (token: string) => { const expires = addYears(Date.now(), 1); const sameSite = inCrossOriginIframe() ? 'None' : 'Lax'; - const secure = window.location.protocol === 'https:'; + const secure = getSecureAttribute(sameSite); return sessionCookie.set(token, { expires, diff --git a/packages/clerk-js/src/core/auth/getSecureAttribute.ts b/packages/clerk-js/src/core/auth/getSecureAttribute.ts new file mode 100644 index 00000000000..959652eaa3f --- /dev/null +++ b/packages/clerk-js/src/core/auth/getSecureAttribute.ts @@ -0,0 +1,20 @@ +type SameSiteAttributeType = 'None' | 'Lax' | 'Strict'; + +// The Secure attribute is required for SameSite=None on Chrome and Firefox +// Also, localhost is considered secure for testing purposes by Chrome and Firefox +// Safari does not support the Secure attribute on localhost, although it returns true for isSecureContext +// ref: https://bugs.webkit.org/show_bug.cgi?id=232088#c8 +export const getSecureAttribute = (sameSite: SameSiteAttributeType) => { + const httpsProtocol = window.location.protocol === 'https:'; + + // Return true on Safari only for `https` protocol + if (typeof (window as any).safari !== 'undefined') { + return httpsProtocol; + } + + if (typeof window.isSecureContext !== 'undefined') { + return window.isSecureContext; + } + + return httpsProtocol || (window.location.hostname === 'localhost' && sameSite === 'None'); +}; diff --git a/packages/clerk-js/src/utils/runtime.ts b/packages/clerk-js/src/utils/runtime.ts index 06a40433579..10b190d4344 100644 --- a/packages/clerk-js/src/utils/runtime.ts +++ b/packages/clerk-js/src/utils/runtime.ts @@ -10,14 +10,9 @@ export function usesHttps() { return inBrowser() && window.location.protocol === 'https:'; } -function isCypress() { - return typeof window !== 'undefined' && typeof (window as any).Cypress !== 'undefined'; -} - export function inIframe() { // checks if the current window is an iframe - // excludes the case where the current window runs in a Cypress test - return inBrowser() && window.self !== window.top && !isCypress(); + return inBrowser() && window.self !== window.top; } export function inCrossOriginIframe() { From ce7b2a39dfb37b0ed83f8f1e3080347b56883f3e Mon Sep 17 00:00:00 2001 From: Stefanos Anagnostou Date: Mon, 1 Jul 2024 14:56:59 +0300 Subject: [PATCH 2/5] Add tests --- .../auth/__tests__/getSecureAttribute.test.ts | 73 +++++++++++++++++++ .../src/core/auth/getSecureAttribute.ts | 15 ++-- 2 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts diff --git a/packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts b/packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts new file mode 100644 index 00000000000..09485388bf8 --- /dev/null +++ b/packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts @@ -0,0 +1,73 @@ +import { getSecureAttribute } from '../getSecureAttribute'; + +const oldWindowLocation = window.location; +const oldWindowSafari = (window as any).safari; +const oldWindowSecureContext = window.isSecureContext; + +describe('getSecureAttribute', () => { + afterAll(() => { + Object.defineProperty(global.window, 'location', { + value: oldWindowLocation, + }); + Object.defineProperty(global.window, 'safari', { + value: oldWindowSafari, + }); + Object.defineProperty(global.window, 'isSecureContext', { + value: oldWindowSecureContext, + }); + }); + + it('returns true if the protocol is https', () => { + const mockWindowLocation = new URL('https://www.clerk.com') as any as Window['location']; + Object.defineProperty(global.window, 'location', { value: mockWindowLocation }); + expect(getSecureAttribute('None')).toBe(true); + }); + + it('returns false if the protocol is not https and the browser is Safari', () => { + const mockWindowLocation = new URL('http://www.clerk.com') as any as Window['location']; + Object.defineProperty(global.window, 'location', { value: mockWindowLocation }); + Object.defineProperty(global.window, 'safari', { value: { dummyValue: true } }); + Object.defineProperty(global.window, 'isSecureContext', { value: undefined }); + expect(getSecureAttribute('None')).toBe(false); + }); + + it('returns true if isSecureContext is true', () => { + const mockWindowLocation = new URL('http://www.clerk.com') as any as Window['location']; + Object.defineProperty(global.window, 'location', { value: mockWindowLocation }); + Object.defineProperty(global.window, 'safari', { value: undefined }); + Object.defineProperty(global.window, 'isSecureContext', { value: true }); + expect(getSecureAttribute('None')).toBe(true); + }); + + it('returns false if isSecureContext is false', () => { + const mockWindowLocation = new URL('http://www.clerk.com') as any as Window['location']; + Object.defineProperty(global.window, 'location', { value: mockWindowLocation }); + Object.defineProperty(global.window, 'safari', { value: undefined }); + Object.defineProperty(global.window, 'isSecureContext', { value: false }); + expect(getSecureAttribute('None')).toBe(false); + }); + + it('returns true if the protocol is http and the hostname is localhost and sameSite is None', () => { + const mockWindowLocation = new URL('http://localhost') as any as Window['location']; + Object.defineProperty(global.window, 'location', { value: mockWindowLocation }); + Object.defineProperty(global.window, 'safari', { value: undefined }); + Object.defineProperty(global.window, 'isSecureContext', { value: undefined }); + expect(getSecureAttribute('None')).toBe(true); + }); + + it('returns false if the protocol is http and the hostname is localhost and sameSite is not None', () => { + const mockWindowLocation = new URL('http://localhost') as any as Window['location']; + Object.defineProperty(global.window, 'location', { value: mockWindowLocation }); + Object.defineProperty(global.window, 'safari', { value: undefined }); + Object.defineProperty(global.window, 'isSecureContext', { value: undefined }); + expect(getSecureAttribute('Lax')).toBe(false); + }); + + it('returns false if the protocol is http and the hostname is not localhost', () => { + const mockWindowLocation = new URL('http://www.clerk.com') as any as Window['location']; + Object.defineProperty(global.window, 'location', { value: mockWindowLocation }); + Object.defineProperty(global.window, 'safari', { value: undefined }); + Object.defineProperty(global.window, 'isSecureContext', { value: undefined }); + expect(getSecureAttribute('None')).toBe(false); + }); +}); diff --git a/packages/clerk-js/src/core/auth/getSecureAttribute.ts b/packages/clerk-js/src/core/auth/getSecureAttribute.ts index 959652eaa3f..9ae533197fb 100644 --- a/packages/clerk-js/src/core/auth/getSecureAttribute.ts +++ b/packages/clerk-js/src/core/auth/getSecureAttribute.ts @@ -4,17 +4,22 @@ type SameSiteAttributeType = 'None' | 'Lax' | 'Strict'; // Also, localhost is considered secure for testing purposes by Chrome and Firefox // Safari does not support the Secure attribute on localhost, although it returns true for isSecureContext // ref: https://bugs.webkit.org/show_bug.cgi?id=232088#c8 -export const getSecureAttribute = (sameSite: SameSiteAttributeType) => { - const httpsProtocol = window.location.protocol === 'https:'; +export const getSecureAttribute = (sameSite: SameSiteAttributeType): boolean => { + if (window.location.protocol === 'https:') { + return true; + } - // Return true on Safari only for `https` protocol + // This is because Safari does not support the Secure attribute on localhost if (typeof (window as any).safari !== 'undefined') { - return httpsProtocol; + return false; } + // This is a useful way to check if the current context is secure + // This is needed for example in environments like Firefox Remote Control + // where the `localhost` is not considered secure if (typeof window.isSecureContext !== 'undefined') { return window.isSecureContext; } - return httpsProtocol || (window.location.hostname === 'localhost' && sameSite === 'None'); + return window.location.hostname === 'localhost' && sameSite === 'None'; }; From 1902e9d1ffea3c7cb9bc6fdefebf598c47356410 Mon Sep 17 00:00:00 2001 From: Stefanos Anagnostou Date: Mon, 1 Jul 2024 16:10:18 +0300 Subject: [PATCH 3/5] Fix tests --- .../auth/__tests__/getSecureAttribute.test.ts | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts b/packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts index 09485388bf8..ef0c07b8984 100644 --- a/packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts +++ b/packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts @@ -19,55 +19,55 @@ describe('getSecureAttribute', () => { it('returns true if the protocol is https', () => { const mockWindowLocation = new URL('https://www.clerk.com') as any as Window['location']; - Object.defineProperty(global.window, 'location', { value: mockWindowLocation }); + Object.defineProperty(global.window, 'location', { value: mockWindowLocation, configurable: true }); expect(getSecureAttribute('None')).toBe(true); }); it('returns false if the protocol is not https and the browser is Safari', () => { const mockWindowLocation = new URL('http://www.clerk.com') as any as Window['location']; - Object.defineProperty(global.window, 'location', { value: mockWindowLocation }); - Object.defineProperty(global.window, 'safari', { value: { dummyValue: true } }); - Object.defineProperty(global.window, 'isSecureContext', { value: undefined }); + Object.defineProperty(global.window, 'location', { value: mockWindowLocation, configurable: true }); + Object.defineProperty(global.window, 'safari', { value: { dummyValue: true }, configurable: true }); + Object.defineProperty(global.window, 'isSecureContext', { value: undefined, configurable: true }); expect(getSecureAttribute('None')).toBe(false); }); it('returns true if isSecureContext is true', () => { const mockWindowLocation = new URL('http://www.clerk.com') as any as Window['location']; - Object.defineProperty(global.window, 'location', { value: mockWindowLocation }); - Object.defineProperty(global.window, 'safari', { value: undefined }); - Object.defineProperty(global.window, 'isSecureContext', { value: true }); + Object.defineProperty(global.window, 'location', { value: mockWindowLocation, configurable: true }); + Object.defineProperty(global.window, 'safari', { value: undefined, configurable: true }); + Object.defineProperty(global.window, 'isSecureContext', { value: true, configurable: true }); expect(getSecureAttribute('None')).toBe(true); }); it('returns false if isSecureContext is false', () => { const mockWindowLocation = new URL('http://www.clerk.com') as any as Window['location']; - Object.defineProperty(global.window, 'location', { value: mockWindowLocation }); - Object.defineProperty(global.window, 'safari', { value: undefined }); - Object.defineProperty(global.window, 'isSecureContext', { value: false }); + Object.defineProperty(global.window, 'location', { value: mockWindowLocation, configurable: true }); + Object.defineProperty(global.window, 'safari', { value: undefined, configurable: true }); + Object.defineProperty(global.window, 'isSecureContext', { value: false, configurable: true }); expect(getSecureAttribute('None')).toBe(false); }); it('returns true if the protocol is http and the hostname is localhost and sameSite is None', () => { const mockWindowLocation = new URL('http://localhost') as any as Window['location']; - Object.defineProperty(global.window, 'location', { value: mockWindowLocation }); - Object.defineProperty(global.window, 'safari', { value: undefined }); - Object.defineProperty(global.window, 'isSecureContext', { value: undefined }); + Object.defineProperty(global.window, 'location', { value: mockWindowLocation, configurable: true }); + Object.defineProperty(global.window, 'safari', { value: undefined, configurable: true }); + Object.defineProperty(global.window, 'isSecureContext', { value: undefined, configurable: true }); expect(getSecureAttribute('None')).toBe(true); }); it('returns false if the protocol is http and the hostname is localhost and sameSite is not None', () => { const mockWindowLocation = new URL('http://localhost') as any as Window['location']; - Object.defineProperty(global.window, 'location', { value: mockWindowLocation }); - Object.defineProperty(global.window, 'safari', { value: undefined }); - Object.defineProperty(global.window, 'isSecureContext', { value: undefined }); + Object.defineProperty(global.window, 'location', { value: mockWindowLocation, configurable: true }); + Object.defineProperty(global.window, 'safari', { value: undefined, configurable: true }); + Object.defineProperty(global.window, 'isSecureContext', { value: undefined, configurable: true }); expect(getSecureAttribute('Lax')).toBe(false); }); it('returns false if the protocol is http and the hostname is not localhost', () => { const mockWindowLocation = new URL('http://www.clerk.com') as any as Window['location']; - Object.defineProperty(global.window, 'location', { value: mockWindowLocation }); - Object.defineProperty(global.window, 'safari', { value: undefined }); - Object.defineProperty(global.window, 'isSecureContext', { value: undefined }); + Object.defineProperty(global.window, 'location', { value: mockWindowLocation, configurable: true }); + Object.defineProperty(global.window, 'safari', { value: undefined, configurable: true }); + Object.defineProperty(global.window, 'isSecureContext', { value: undefined, configurable: true }); expect(getSecureAttribute('None')).toBe(false); }); }); From aef1748e54ee0d03490b1980c8cf6c059bded845 Mon Sep 17 00:00:00 2001 From: Stefanos Anagnostou Date: Mon, 1 Jul 2024 16:30:40 +0300 Subject: [PATCH 4/5] Fix tests --- .../auth/__tests__/getSecureAttribute.test.ts | 83 ++++++++++--------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts b/packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts index ef0c07b8984..8ff9f69c4df 100644 --- a/packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts +++ b/packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts @@ -1,73 +1,76 @@ import { getSecureAttribute } from '../getSecureAttribute'; -const oldWindowLocation = window.location; -const oldWindowSafari = (window as any).safari; -const oldWindowSecureContext = window.isSecureContext; - describe('getSecureAttribute', () => { - afterAll(() => { - Object.defineProperty(global.window, 'location', { - value: oldWindowLocation, - }); - Object.defineProperty(global.window, 'safari', { - value: oldWindowSafari, - }); - Object.defineProperty(global.window, 'isSecureContext', { - value: oldWindowSecureContext, - }); + let windowSpy: any; + + beforeEach(() => { + windowSpy = jest.spyOn(window, 'window', 'get'); + }); + + afterEach(() => { + windowSpy.mockRestore(); }); it('returns true if the protocol is https', () => { - const mockWindowLocation = new URL('https://www.clerk.com') as any as Window['location']; - Object.defineProperty(global.window, 'location', { value: mockWindowLocation, configurable: true }); + windowSpy.mockImplementation(() => ({ + location: new URL('https://www.clerk.com'), + safari: undefined, + isSecureContext: undefined, + })); expect(getSecureAttribute('None')).toBe(true); }); it('returns false if the protocol is not https and the browser is Safari', () => { - const mockWindowLocation = new URL('http://www.clerk.com') as any as Window['location']; - Object.defineProperty(global.window, 'location', { value: mockWindowLocation, configurable: true }); - Object.defineProperty(global.window, 'safari', { value: { dummyValue: true }, configurable: true }); - Object.defineProperty(global.window, 'isSecureContext', { value: undefined, configurable: true }); + windowSpy.mockImplementation(() => ({ + location: new URL('http://www.clerk.com'), + safari: { dummyValue: true }, + isSecureContext: undefined, + })); expect(getSecureAttribute('None')).toBe(false); }); it('returns true if isSecureContext is true', () => { - const mockWindowLocation = new URL('http://www.clerk.com') as any as Window['location']; - Object.defineProperty(global.window, 'location', { value: mockWindowLocation, configurable: true }); - Object.defineProperty(global.window, 'safari', { value: undefined, configurable: true }); - Object.defineProperty(global.window, 'isSecureContext', { value: true, configurable: true }); + windowSpy.mockImplementation(() => ({ + location: new URL('http://www.clerk.com'), + safari: undefined, + isSecureContext: true, + })); expect(getSecureAttribute('None')).toBe(true); }); it('returns false if isSecureContext is false', () => { - const mockWindowLocation = new URL('http://www.clerk.com') as any as Window['location']; - Object.defineProperty(global.window, 'location', { value: mockWindowLocation, configurable: true }); - Object.defineProperty(global.window, 'safari', { value: undefined, configurable: true }); - Object.defineProperty(global.window, 'isSecureContext', { value: false, configurable: true }); + windowSpy.mockImplementation(() => ({ + location: new URL('http://www.clerk.com'), + safari: undefined, + isSecureContext: false, + })); expect(getSecureAttribute('None')).toBe(false); }); it('returns true if the protocol is http and the hostname is localhost and sameSite is None', () => { - const mockWindowLocation = new URL('http://localhost') as any as Window['location']; - Object.defineProperty(global.window, 'location', { value: mockWindowLocation, configurable: true }); - Object.defineProperty(global.window, 'safari', { value: undefined, configurable: true }); - Object.defineProperty(global.window, 'isSecureContext', { value: undefined, configurable: true }); + windowSpy.mockImplementation(() => ({ + location: new URL('http://localhost'), + safari: undefined, + isSecureContext: undefined, + })); expect(getSecureAttribute('None')).toBe(true); }); it('returns false if the protocol is http and the hostname is localhost and sameSite is not None', () => { - const mockWindowLocation = new URL('http://localhost') as any as Window['location']; - Object.defineProperty(global.window, 'location', { value: mockWindowLocation, configurable: true }); - Object.defineProperty(global.window, 'safari', { value: undefined, configurable: true }); - Object.defineProperty(global.window, 'isSecureContext', { value: undefined, configurable: true }); + windowSpy.mockImplementation(() => ({ + location: new URL('http://localhost'), + safari: undefined, + isSecureContext: undefined, + })); expect(getSecureAttribute('Lax')).toBe(false); }); it('returns false if the protocol is http and the hostname is not localhost', () => { - const mockWindowLocation = new URL('http://www.clerk.com') as any as Window['location']; - Object.defineProperty(global.window, 'location', { value: mockWindowLocation, configurable: true }); - Object.defineProperty(global.window, 'safari', { value: undefined, configurable: true }); - Object.defineProperty(global.window, 'isSecureContext', { value: undefined, configurable: true }); + windowSpy.mockImplementation(() => ({ + location: new URL('http://www.clerk.com'), + safari: undefined, + isSecureContext: undefined, + })); expect(getSecureAttribute('None')).toBe(false); }); }); From 97f4cfbfc5d38ba919a2cc9be0a07949b23afb49 Mon Sep 17 00:00:00 2001 From: Stefanos Anagnostou Date: Wed, 10 Jul 2024 18:15:17 +0300 Subject: [PATCH 5/5] Improve the logic for Secure attribute, setting it to false when SameSite is Lax or Strict --- .../auth/__tests__/getSecureAttribute.test.ts | 18 ++++++++++++++++++ .../src/core/auth/getSecureAttribute.ts | 7 ++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts b/packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts index 8ff9f69c4df..36b2b699de6 100644 --- a/packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts +++ b/packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts @@ -20,6 +20,15 @@ describe('getSecureAttribute', () => { expect(getSecureAttribute('None')).toBe(true); }); + it('returns false if the protocol is not https and the SameSite is not None', () => { + windowSpy.mockImplementation(() => ({ + location: new URL('http://www.clerk.com'), + safari: undefined, + isSecureContext: undefined, + })); + expect(getSecureAttribute('Lax')).toBe(false); + }); + it('returns false if the protocol is not https and the browser is Safari', () => { windowSpy.mockImplementation(() => ({ location: new URL('http://www.clerk.com'), @@ -73,4 +82,13 @@ describe('getSecureAttribute', () => { })); expect(getSecureAttribute('None')).toBe(false); }); + + it('returns false in case window.safari is undefined and isSecureContext is true on localhost for Lax cookie', () => { + windowSpy.mockImplementation(() => ({ + location: new URL('http://localhost'), + safari: undefined, + isSecureContext: true, + })); + expect(getSecureAttribute('Lax')).toBe(false); + }); }); diff --git a/packages/clerk-js/src/core/auth/getSecureAttribute.ts b/packages/clerk-js/src/core/auth/getSecureAttribute.ts index 9ae533197fb..18c6083a1a8 100644 --- a/packages/clerk-js/src/core/auth/getSecureAttribute.ts +++ b/packages/clerk-js/src/core/auth/getSecureAttribute.ts @@ -9,6 +9,11 @@ export const getSecureAttribute = (sameSite: SameSiteAttributeType): boolean => return true; } + // If the cookie is not SameSite=None, then the Secure attribute is not required + if (sameSite !== 'None') { + return false; + } + // This is because Safari does not support the Secure attribute on localhost if (typeof (window as any).safari !== 'undefined') { return false; @@ -21,5 +26,5 @@ export const getSecureAttribute = (sameSite: SameSiteAttributeType): boolean => return window.isSecureContext; } - return window.location.hostname === 'localhost' && sameSite === 'None'; + return window.location.hostname === 'localhost'; };