From 02b846b55a5a693b75797ff135115e602cbe1a26 Mon Sep 17 00:00:00 2001 From: Bartosz Date: Tue, 9 Jun 2026 10:13:32 +0200 Subject: [PATCH 1/4] 3.0.1: slim down default request context Reduce the default request context built by ContextGetDefaultService to headers, ip and library. Remove the internal client-id extraction service and the now-unused cookie helper (HeadersGetCookieService) along with the cookies plumbing in ContextGetDefaultService / ContextPrepareService. --- CHANGELOG.md | 12 ++++ README.md | 2 +- package-lock.json | 4 +- package.json | 2 +- src/client-id/client-id.module.ts | 1 - .../services/client-id-extract.service.ts | 12 ---- src/client-id/services/index.ts | 1 - .../services/context-get-default.service.ts | 10 +-- .../services/context-prepare.service.ts | 1 - .../services/headers-get-cookie.service.ts | 20 ------ src/headers/services/index.ts | 1 - .../client-id-extract.service.test.ts | 53 -------------- .../context-get-default.service.test.ts | 7 +- .../headers-get-cookie.service.test.ts | 70 ------------------- .../services/payload-prepare-service.test.ts | 1 - 15 files changed, 18 insertions(+), 179 deletions(-) delete mode 100644 src/client-id/client-id.module.ts delete mode 100644 src/client-id/services/client-id-extract.service.ts delete mode 100644 src/client-id/services/index.ts delete mode 100644 src/headers/services/headers-get-cookie.service.ts delete mode 100644 test/client-id/services/client-id-extract.service.test.ts delete mode 100644 test/headers/services/headers-get-cookie.service.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index cca129e..0729f76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 3.0.1 + +**Housekeeping:** + +- Slim down the default request context to `headers`, `ip`, and `library`. The + client id is carried by `headers` (the `x-castle-client-id` header / `__cid` + cookie) and resolved by Castle server-side, so the SDK no longer derives it + separately. +- Remove the internal client-id extraction service and the now-unused cookie + helper (`HeadersGetCookieService`) along with the `cookies` plumbing in + `ContextGetDefaultService` / `ContextPrepareService`. + ## 3.0.0 **BREAKING CHANGES:** diff --git a/README.md b/README.md index 3976ff4..5257c05 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ const castle = new Castle({ apiSecret: process.env.CASTLE_API_SECRET }); const context = ContextPrepareService.call( req, - { cookies: req.cookies }, + undefined, castle.configuration ); diff --git a/package-lock.json b/package-lock.json index 2c4d2f5..813ddf0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@castleio/sdk", - "version": "3.0.0", + "version": "3.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@castleio/sdk", - "version": "3.0.0", + "version": "3.0.1", "license": "MIT", "dependencies": { "pino": "^9.7.0", diff --git a/package.json b/package.json index 1aa1ccf..879f33b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "$schema": "http://json.schemastore.org/package", "name": "@castleio/sdk", "description": "Castle SDK for Node", - "version": "3.0.0", + "version": "3.0.1", "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", diff --git a/src/client-id/client-id.module.ts b/src/client-id/client-id.module.ts deleted file mode 100644 index e371345..0000000 --- a/src/client-id/client-id.module.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './services'; diff --git a/src/client-id/services/client-id-extract.service.ts b/src/client-id/services/client-id-extract.service.ts deleted file mode 100644 index b44eb51..0000000 --- a/src/client-id/services/client-id-extract.service.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { IncomingHttpHeaders } from 'http2'; -import { HeadersGetCookieService } from '../../headers/headers.module'; - -export const ClientIdExtractService = { - call: (headers: IncomingHttpHeaders = {}, cookies?: string | string[]) => { - return ( - headers['x-castle-client-id'] || - HeadersGetCookieService.call(cookies, '__cid') || - '' - ); - }, -}; diff --git a/src/client-id/services/index.ts b/src/client-id/services/index.ts deleted file mode 100644 index 049b4ed..0000000 --- a/src/client-id/services/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './client-id-extract.service'; diff --git a/src/context/services/context-get-default.service.ts b/src/context/services/context-get-default.service.ts index 8e557fd..f77fc8c 100644 --- a/src/context/services/context-get-default.service.ts +++ b/src/context/services/context-get-default.service.ts @@ -1,6 +1,5 @@ import { isEmpty, pickByTruthy } from '../../utils/object'; import { Configuration } from '../../configuration'; -import { ClientIdExtractService } from '../../client-id/client-id.module'; import { HeadersExtractService } from '../../headers/headers.module'; import { IPsExtractService } from '../../ips/ips.module'; import { version } from '../../../package.json'; @@ -8,19 +7,13 @@ import type { IncomingHttpHeaders } from 'http2'; const requestContextData = ( request: { headers: IncomingHttpHeaders }, - cookies: string | undefined, configuration: Configuration ): { [key: string]: any } => { if (isEmpty(request)) { return {}; } - const cookiesForClientId = - cookies || (request.headers as { [key: string]: any })?.cookies; return { - client_id: - ClientIdExtractService.call(request.headers, cookiesForClientId) || false, - active: true, headers: HeadersExtractService.call(request.headers, configuration), ip: IPsExtractService.call(request.headers, configuration), }; @@ -29,11 +22,10 @@ const requestContextData = ( export const ContextGetDefaultService = { call: ( request: { headers: IncomingHttpHeaders }, - cookies: string | undefined, configuration: Configuration ): { [key: string]: any } => { return { - ...pickByTruthy(requestContextData(request, cookies, configuration)), + ...pickByTruthy(requestContextData(request, configuration)), library: { name: 'castle-node', version, diff --git a/src/context/services/context-prepare.service.ts b/src/context/services/context-prepare.service.ts index fe67d76..c53bd08 100644 --- a/src/context/services/context-prepare.service.ts +++ b/src/context/services/context-prepare.service.ts @@ -11,7 +11,6 @@ export const ContextPrepareService = { ) => { const defaultContext = ContextGetDefaultService.call( request, - options?.cookies, configuration ); return deepMerge(defaultContext, options?.context); diff --git a/src/headers/services/headers-get-cookie.service.ts b/src/headers/services/headers-get-cookie.service.ts deleted file mode 100644 index d3695df..0000000 --- a/src/headers/services/headers-get-cookie.service.ts +++ /dev/null @@ -1,20 +0,0 @@ -export const HeadersGetCookieService = { - call: ( - cookies: string | string[] | undefined, - name: string - ): string | undefined => { - if (!cookies) { - return; - } - - const pattern = new RegExp(`(?:^|\\s+)\\s?${name}=(.*?)(?:;|$)`); - const results = cookies.toString().match(pattern); - - if (results && results.length === 2) { - // return the last match - return results[1]; - } - - return; - }, -}; diff --git a/src/headers/services/index.ts b/src/headers/services/index.ts index 9508f73..48cb18f 100644 --- a/src/headers/services/index.ts +++ b/src/headers/services/index.ts @@ -1,2 +1 @@ export * from './headers-extract.service'; -export * from './headers-get-cookie.service'; diff --git a/test/client-id/services/client-id-extract.service.test.ts b/test/client-id/services/client-id-extract.service.test.ts deleted file mode 100644 index b3e4e70..0000000 --- a/test/client-id/services/client-id-extract.service.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { ClientIdExtractService } from '../../../src/client-id/client-id.module'; - -describe('ClientIdExtractService', () => { - describe('call', () => { - const expectedFromCookie = 'abcd'; - const expectedFromHeader = 'abcde'; - - describe('with client_id', () => { - it('extracts client_id from cookie', () => { - const headers = { - cookie: '__cid=abcd;other=efgh', - 'x-forwarded-for': '1.2.3.4', - }; - const received = ClientIdExtractService.call(headers, headers.cookie); - expect(received).toEqual(expectedFromCookie); - }); - }); - - describe('with x-castle-client-id header', () => { - it('extracts client_id from cookie', () => { - const headers = { - cookie: '', - 'x-forwarded-for': '1.2.3.4', - 'x-castle-client-id': expectedFromHeader, - }; - const received = ClientIdExtractService.call(headers, headers.cookie); - expect(received).toEqual(expectedFromHeader); - }); - }); - - describe('with cookies and headers undefined', () => { - it('returns empty string', () => { - const headers = { - cookie: '', - }; - const received = ClientIdExtractService.call(headers, headers.cookie); - expect(received).toEqual(''); - }); - }); - - describe('with headers and cookie defined', () => { - it('extracts client_id from headers', () => { - const headers = { - cookie: '__cid=abcd;other=efgh', - 'x-forwarded-for': '1.2.3.4', - 'x-castle-client-id': expectedFromHeader, - }; - const received = ClientIdExtractService.call(headers, headers.cookie); - expect(received).toEqual(expectedFromHeader); - }); - }); - }); -}); diff --git a/test/context/services/context-get-default.service.test.ts b/test/context/services/context-get-default.service.test.ts index eb0e8e7..7c524ab 100644 --- a/test/context/services/context-get-default.service.test.ts +++ b/test/context/services/context-get-default.service.test.ts @@ -14,7 +14,6 @@ describe('ContextGetDefaultService', () => { name: 'castle-node', version, }, - client_id: 'client_id', ip: '1.2.3.4', }; @@ -35,11 +34,7 @@ describe('ContextGetDefaultService', () => { } as ExpressRequest; it('generates default context', () => { - const received = ContextGetDefaultService.call( - mockRequest, - undefined, - config - ); + const received = ContextGetDefaultService.call(mockRequest, config); expect(received).toMatchObject(expected); }); }); diff --git a/test/headers/services/headers-get-cookie.service.test.ts b/test/headers/services/headers-get-cookie.service.test.ts deleted file mode 100644 index a66cd90..0000000 --- a/test/headers/services/headers-get-cookie.service.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { HeadersGetCookieService } from '../../../src/headers/headers.module'; - -describe('HeadersGetCookieService', () => { - describe('call', () => { - it('doesnt get cookie from empty header', () => { - const headers = { - cookie: '', - }; - - expect( - HeadersGetCookieService.call(headers.cookie, '_key') - ).toBeUndefined(); - }); - - it('gets cookie with encoded value', () => { - const headers = { - cookie: - '__uid=21917; cfids187=; cf=\u002C\u003B; a_key=f195d29f3ee38e07bd95ae9c', - }; - - expect(HeadersGetCookieService.call(headers.cookie, 'cf')).toEqual(','); - }); - - it('gets cookie from the end', () => { - const headers = { - cookie: '__uid=21917; cfids187=; _key=f195d29f3ee38e07bd95ae9c', - }; - expect(HeadersGetCookieService.call(headers.cookie, '_key')).toEqual( - 'f195d29f3ee38e07bd95ae9c' - ); - }); - - it('gets cookie from the middle', () => { - const headers = { - cookie: - '__uid=21917; cfids187=; cf=abc; a_key=f195d29f3ee38e07bd95ae9c', - }; - expect(HeadersGetCookieService.call(headers.cookie, 'cf')).toEqual('abc'); - }); - - it('gets cookie from the start', () => { - const headers = { - cookie: - '__uid=21917; cfids187=; cf=abc; a_key=f195d29f3ee38e07bd95ae9c', - }; - expect(HeadersGetCookieService.call(headers.cookie, '__uid')).toEqual( - '21917' - ); - }); - - it('does not get cookie with similar prefix', () => { - const headers = { - cookie: '__uid=21917; cfids187=; a_key=f195d29f3ee38e07bd95ae9c', - }; - expect( - HeadersGetCookieService.call(headers.cookie, '_key') - ).toBeUndefined(); - }); - - it('doesnt get cookie from the middle when empty', () => { - const headers = { - cookie: - '__uid=21917; cfids187=; cf=abc; a_key=f195d29f3ee38e07bd95ae9c', - }; - expect(HeadersGetCookieService.call(headers.cookie, 'cfids187')).toEqual( - '' - ); - }); - }); -}); diff --git a/test/payload/services/payload-prepare-service.test.ts b/test/payload/services/payload-prepare-service.test.ts index 3fcb545..1f2a01a 100644 --- a/test/payload/services/payload-prepare-service.test.ts +++ b/test/payload/services/payload-prepare-service.test.ts @@ -11,7 +11,6 @@ describe('PayloadPrepareService', () => { version, }, client_id: '123', - active: true, }, user_id: '123', }; From d1f26569ba508b0be6856c49788b0a966edddd87 Mon Sep 17 00:00:00 2001 From: Bartosz Date: Tue, 9 Jun 2026 11:27:43 +0200 Subject: [PATCH 2/4] docs: drop client id from ContextPrepareService description --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5257c05..1d1738f 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ With CommonJS: const { Castle, ContextPrepareService } = require('@castleio/sdk'); ``` -`ContextPrepareService.call(request, options, configuration)` extracts the IP, headers, and client id that Castle needs from a Node `request` object. See [Advanced configuration](#advanced-configuration) for how header allow/deny lists and proxy chains are resolved. +`ContextPrepareService.call(request, options, configuration)` extracts the IP and headers that Castle needs from a Node `request` object. See [Advanced configuration](#advanced-configuration) for how header allow/deny lists and proxy chains are resolved. ## Configuration From 58def28b1a9869adcf6fbbdc25c0c8f440ac1692 Mon Sep 17 00:00:00 2001 From: Bartosz Date: Mon, 8 Jun 2026 16:45:04 +0200 Subject: [PATCH 3/4] docs: branch release flow off develop and merge into master Update RELEASING.md so releases start from develop and add the step to merge develop into master and push before tagging and publishing. --- RELEASING.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 2d84c21..d258810 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -1,13 +1,14 @@ # Releasing -1. Create branch `release/X.Y.Z` from `master`. +1. Create branch `release/X.Y.Z` from `develop`. 2. Update `version` in `package.json` to the new version 3. Run `npm install` (to refresh `package-lock.json`) 4. Update the `CHANGELOG.md` for the impending release 5. `git commit -am "release X.Y.Z"` (where X.Y.Z is the new version) -6. Push to Github, make PR to the `master` branch, and when approved, merge. -7. Make a release on Github from the `master` branch, specify tag as `vX.Y.Z` to create a tag. -8. `git checkout master && git pull` -9. Clean unversioned files: `git clean -fdx dist` -10. `npm run build && npm pack` to verify the package -11. `npm publish` +6. Push to Github, make PR to the `develop` branch, and when approved, merge. +7. Pull latest `develop`, merge it into `master`, and push `master` to `origin`. +8. Make a release on Github from the `master` branch, specify tag as `vX.Y.Z` to create a tag. +9. `git checkout master && git pull` +10. Clean unversioned files: `git clean -fdx dist` +11. `npm run build && npm pack` to verify the package +12. `npm publish` From 22249121e9cc77094818197af5d86b937b131c14 Mon Sep 17 00:00:00 2001 From: Bartosz Date: Tue, 9 Jun 2026 14:38:00 +0200 Subject: [PATCH 4/4] Release as 3.1.0 (minor) instead of 3.0.1 --- CHANGELOG.md | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0729f76..0448fb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 3.0.1 +## 3.1.0 **Housekeeping:** diff --git a/package-lock.json b/package-lock.json index 813ddf0..e0bf4f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@castleio/sdk", - "version": "3.0.1", + "version": "3.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@castleio/sdk", - "version": "3.0.1", + "version": "3.1.0", "license": "MIT", "dependencies": { "pino": "^9.7.0", diff --git a/package.json b/package.json index 879f33b..167701a 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "$schema": "http://json.schemastore.org/package", "name": "@castleio/sdk", "description": "Castle SDK for Node", - "version": "3.0.1", + "version": "3.1.0", "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts",