From 54512c260350d4fcb3f85bc1f4c3fd197d6a17d7 Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Sun, 26 Feb 2023 14:59:31 +0800 Subject: [PATCH 1/4] fix(core): allow localhost CORS when only one endpoint available --- packages/core/src/middleware/koa-cors.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/core/src/middleware/koa-cors.ts b/packages/core/src/middleware/koa-cors.ts index 41fa02883..a2ebd50e9 100644 --- a/packages/core/src/middleware/koa-cors.ts +++ b/packages/core/src/middleware/koa-cors.ts @@ -13,14 +13,22 @@ export default function koaCors( if ( origin && - urlSets.some((set) => - set.deduplicated().some( + urlSets.some((set) => { + const deduplicated = set.deduplicated(); + + // The URL Set has only one endpoint available, just use that endpoint. + if (deduplicated.length <= 1) { + return deduplicated.some((url) => url.origin === origin); + } + + // For multiple endpoints, should filter out localhost in production. + return deduplicated.some( (url) => url.origin === origin && // Disable localhost CORS in production since it's unsafe !(EnvSet.values.isProduction && url.hostname === 'localhost') - ) - ) + ); + }) ) { return origin; } From 5e1466f405c4102707f8d6c47efedfb4d49f9152 Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Sun, 26 Feb 2023 15:01:20 +0800 Subject: [PATCH 2/4] chore: add changeset --- .changeset/modern-days-float.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/modern-days-float.md diff --git a/.changeset/modern-days-float.md b/.changeset/modern-days-float.md new file mode 100644 index 000000000..972f812ac --- /dev/null +++ b/.changeset/modern-days-float.md @@ -0,0 +1,5 @@ +--- +"@logto/core": patch +--- + +Allow localhost CORS when only one endpoint available From 237df1721cf402263e3cbbe247e2bfba3a837192 Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Sun, 26 Feb 2023 15:03:37 +0800 Subject: [PATCH 3/4] release: version core packages --- .changeset/pre.json | 1 + packages/cli/CHANGELOG.md | 2 ++ packages/cli/package.json | 2 +- packages/console/CHANGELOG.md | 2 ++ packages/console/package.json | 2 +- packages/core/CHANGELOG.md | 7 +++++++ packages/core/package.json | 2 +- packages/create/CHANGELOG.md | 6 ++++++ packages/create/package.json | 2 +- packages/integration-tests/CHANGELOG.md | 2 ++ packages/integration-tests/package.json | 2 +- packages/ui/CHANGELOG.md | 2 ++ packages/ui/package.json | 2 +- 13 files changed, 28 insertions(+), 6 deletions(-) diff --git a/.changeset/pre.json b/.changeset/pre.json index cd8c6d4f7..5c5b0ef1a 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -37,6 +37,7 @@ "lemon-cars-wonder", "lovely-rivers-sniff", "many-avocados-know", + "modern-days-float", "moody-timers-grin", "neat-snakes-smash", "pink-maps-yell", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 967731981..9aadae1eb 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,7 @@ # Change Log +## 1.0.0-rc.3 + ## 1.0.0-rc.2 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 42189645f..d05abc2e3 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@logto/cli", - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "description": "Logto CLI.", "author": "Silverhand Inc. ", "homepage": "https://github.com/logto-io/logto#readme", diff --git a/packages/console/CHANGELOG.md b/packages/console/CHANGELOG.md index 80c0923b1..0a4a4006b 100644 --- a/packages/console/CHANGELOG.md +++ b/packages/console/CHANGELOG.md @@ -1,5 +1,7 @@ # Change Log +## 1.0.0-rc.3 + ## 1.0.0-rc.2 ### Major Changes diff --git a/packages/console/package.json b/packages/console/package.json index bf09c9916..861fbc7ac 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -1,6 +1,6 @@ { "name": "@logto/console", - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "description": "> TODO: description", "author": "Silverhand Inc. ", "homepage": "https://github.com/logto-io/logto#readme", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 9e84b0ac5..a78a4f575 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 1.0.0-rc.3 + +### Patch Changes + +- 5e1466f40: Allow localhost CORS when only one endpoint available + - @logto/cli@1.0.0-rc.3 + ## 1.0.0-rc.2 ### Major Changes diff --git a/packages/core/package.json b/packages/core/package.json index 5f3258447..8b120eb26 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@logto/core", - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "description": "The open source identity solution.", "main": "build/index.js", "author": "Silverhand Inc. ", diff --git a/packages/create/CHANGELOG.md b/packages/create/CHANGELOG.md index c5b35ca21..b38295b14 100644 --- a/packages/create/CHANGELOG.md +++ b/packages/create/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## 1.0.0-rc.3 + +### Patch Changes + +- @logto/cli@1.0.0-rc.3 + ## 1.0.0-rc.2 ### Patch Changes diff --git a/packages/create/package.json b/packages/create/package.json index fb87310d0..50fd2facf 100644 --- a/packages/create/package.json +++ b/packages/create/package.json @@ -1,6 +1,6 @@ { "name": "@logto/create", - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "author": "Silverhand Inc. ", "license": "MPL-2.0", "type": "module", diff --git a/packages/integration-tests/CHANGELOG.md b/packages/integration-tests/CHANGELOG.md index 5fbc63e01..2ad2f8e3a 100644 --- a/packages/integration-tests/CHANGELOG.md +++ b/packages/integration-tests/CHANGELOG.md @@ -1,5 +1,7 @@ # Change Log +## 1.0.0-rc.3 + ## 1.0.0-rc.2 ## 1.0.0-rc.1 diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index aad9e085b..a92f6cf33 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@logto/integration-tests", - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "description": "Integration tests for Logto.", "author": "Silverhand Inc. ", "license": "MPL-2.0", diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md index 45b76629d..cf29f902a 100644 --- a/packages/ui/CHANGELOG.md +++ b/packages/ui/CHANGELOG.md @@ -1,5 +1,7 @@ # Change Log +## 1.0.0-rc.3 + ## 1.0.0-rc.2 ### Minor Changes diff --git a/packages/ui/package.json b/packages/ui/package.json index e84c6dad7..2153d7b1f 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@logto/ui", - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "license": "MPL-2.0", "type": "module", "private": true, From d61cd795ac81646388753657ab2bb397d19f3752 Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Mon, 27 Feb 2023 15:12:12 +0800 Subject: [PATCH 4/4] refactor(core): add unit tests --- packages/core/src/middleware/koa-cors.test.ts | 112 ++++++++++++++++++ packages/core/src/middleware/koa-cors.ts | 2 +- 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/middleware/koa-cors.test.ts diff --git a/packages/core/src/middleware/koa-cors.test.ts b/packages/core/src/middleware/koa-cors.test.ts new file mode 100644 index 000000000..18196fb74 --- /dev/null +++ b/packages/core/src/middleware/koa-cors.test.ts @@ -0,0 +1,112 @@ +import { createMockUtils } from '@logto/shared/esm'; +import type { RequestMethod } from 'node-mocks-http'; + +import GlobalValues from '#src/env-set/GlobalValues.js'; +import UrlSet from '#src/env-set/UrlSet.js'; +import createMockContext from '#src/test-utils/jest-koa-mocks/create-mock-context.js'; + +const { jest } = import.meta; + +const { mockEsmWithActual } = createMockUtils(jest); + +await mockEsmWithActual('#src/env-set/index.js', () => ({ + EnvSet: { + get values() { + return new GlobalValues(); + }, + }, +})); + +const { default: koaCors } = await import('./koa-cors.js'); + +// eslint-disable-next-line @typescript-eslint/no-empty-function +const noop = async () => {}; + +const mockContext = (method: RequestMethod, url: string) => { + const ctx = createMockContext({ method, url }); + + const setSpy = jest.spyOn(ctx, 'set'); + + return [ctx, setSpy] as const; +}; + +const expectCorsHeaders = (setSpy: jest.SpyInstance, origin: string) => { + if (origin) { + expect(setSpy).toHaveBeenCalledWith('Access-Control-Expose-Headers', '*'); + expect(setSpy).toHaveBeenCalledWith('Access-Control-Allow-Origin', origin); + } else { + expect(setSpy).not.toHaveBeenCalledWith( + 'Access-Control-Expose-Headers', + expect.stringMatching('.*') + ); + expect(setSpy).not.toHaveBeenCalledWith( + 'Access-Control-Allow-Origin', + expect.stringMatching('.*') + ); + } +}; + +describe('koaCors() middleware', () => { + const envBackup = Object.freeze({ ...process.env }); + + afterEach(() => { + process.env = { ...envBackup }; + }); + + it('should set proper CORS response headers for a single URL Set', async () => { + const endpoint = 'https://logto.io'; + process.env.ENDPOINT = endpoint; + process.env.NODE_ENV = 'dev'; + const urlSet = new UrlSet(false, 3001); + const run = koaCors(urlSet); + + const [ctx1, setSpy1] = mockContext('GET', endpoint + '/api'); + await run(ctx1, noop); + expectCorsHeaders(setSpy1, endpoint); + + const [ctx2, setSpy2] = mockContext('GET', 'http://localhost:3001/api'); + await run(ctx2, noop); + expectCorsHeaders(setSpy2, 'http://localhost:3001'); + }); + + it('should set proper CORS response headers for multiple URL Sets', async () => { + const endpoint = 'https://logto.io'; + const adminEndpoint = 'https://logto.admin'; + + process.env.ENDPOINT = endpoint; + process.env.ADMIN_ENDPOINT = adminEndpoint; + process.env.NODE_ENV = 'dev'; + const run = koaCors(new UrlSet(false, 3001), new UrlSet(true, 3002, 'ADMIN_')); + + const [ctx1, setSpy1] = mockContext('PUT', 'https://localhost:3002/api'); + await run(ctx1, noop); + expectCorsHeaders(setSpy1, 'https://localhost:3002'); + + const [ctx2, setSpy2] = mockContext('POST', adminEndpoint + '/api'); + await run(ctx2, noop); + expectCorsHeaders(setSpy2, adminEndpoint); + }); + + it('should set CORS response headers for localhost in production when endpoint is unavailable', async () => { + process.env.ENDPOINT = undefined; + process.env.NODE_ENV = 'production'; + const urlSet = new UrlSet(true, 3002); + const run = koaCors(urlSet); + + const [ctx, setSpy] = mockContext('POST', 'https://localhost:3002/api'); + await run(ctx, noop); + expectCorsHeaders(setSpy, 'https://localhost:3002'); + }); + + it('should not to set CORS response headers for localhost in production when endpoint is available', async () => { + const endpoint = 'https://logto.io'; + process.env.ENDPOINT = endpoint; + process.env.NODE_ENV = 'production'; + const urlSet = new UrlSet(false, 3001); + const run = koaCors(urlSet); + + const [ctx, setSpy] = mockContext('DELETE', 'http://localhost:3001/api'); + await run(ctx, noop); + expectCorsHeaders(setSpy, ''); + }); +}); diff --git a/packages/core/src/middleware/koa-cors.ts b/packages/core/src/middleware/koa-cors.ts index a2ebd50e9..07f766d24 100644 --- a/packages/core/src/middleware/koa-cors.ts +++ b/packages/core/src/middleware/koa-cors.ts @@ -9,7 +9,7 @@ export default function koaCors( ): MiddlewareType { return cors({ origin: (ctx) => { - const { origin } = ctx.request.headers; + const { origin } = ctx; if ( origin &&