From c4380a4a166a808b07d57f9909d76472ec3e7d1e Mon Sep 17 00:00:00 2001 From: Paul Vaneveld Date: Wed, 17 Sep 2025 16:43:16 +0200 Subject: [PATCH 1/8] fix: fialing tests --- packages/express-adapter/src/cookies.test.ts | 6 +++--- packages/fastify-adapter/src/cookies.test.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/express-adapter/src/cookies.test.ts b/packages/express-adapter/src/cookies.test.ts index 9898fba..e131e25 100644 --- a/packages/express-adapter/src/cookies.test.ts +++ b/packages/express-adapter/src/cookies.test.ts @@ -114,7 +114,7 @@ describe("CookieTokenSource", () => { sameSite: "strict", domain: undefined, expires: undefined, - path: "/", + path: undefined, }); }); @@ -139,7 +139,7 @@ describe("CookieTokenSource", () => { secure: true, sameSite: "strict", domain: undefined, - path: "/", + path: undefined, expires: expect.any(Date), }); }); @@ -164,7 +164,7 @@ describe("CookieTokenSource", () => { sameSite: "strict", expires: undefined, domain: undefined, - path: "/", + path: undefined, }); }); diff --git a/packages/fastify-adapter/src/cookies.test.ts b/packages/fastify-adapter/src/cookies.test.ts index 72189d9..401bef8 100644 --- a/packages/fastify-adapter/src/cookies.test.ts +++ b/packages/fastify-adapter/src/cookies.test.ts @@ -126,7 +126,7 @@ describe("CookieTokenSource", () => { sameSite: "strict", domain: undefined, expires: undefined, - path: "/", + path: undefined, }); }); @@ -151,7 +151,7 @@ describe("CookieTokenSource", () => { secure: true, sameSite: "strict", domain: undefined, - path: "/", + path: undefined, expires: expect.any(Date), }); }); @@ -176,7 +176,7 @@ describe("CookieTokenSource", () => { sameSite: "strict", domain: undefined, expires: undefined, - path: "/", + path: undefined, }); }); From b13077b305105003fcf14b696f1e9a45a81ccf10 Mon Sep 17 00:00:00 2001 From: Paul Vaneveld Date: Wed, 17 Sep 2025 16:54:55 +0200 Subject: [PATCH 2/8] feat: add refresh token and auth token path option --- .../core/src/tokensource/cookies-base.test.ts | 39 ++++++++++++++++--- packages/core/src/tokensource/cookies-base.ts | 24 ++++++++---- packages/express-adapter/src/cookies.ts | 4 +- packages/fastify-adapter/src/cookies.test.ts | 2 +- packages/fastify-adapter/src/cookies.ts | 4 +- 5 files changed, 56 insertions(+), 17 deletions(-) diff --git a/packages/core/src/tokensource/cookies-base.test.ts b/packages/core/src/tokensource/cookies-base.test.ts index 1c7d560..21c2a59 100644 --- a/packages/core/src/tokensource/cookies-base.test.ts +++ b/packages/core/src/tokensource/cookies-base.test.ts @@ -1,5 +1,5 @@ import { parse, type SerializeOptions, serialize } from "cookie"; -import { describe, expect, it } from "vitest"; +import { describe, expect, it, } from "vitest"; import { type BaseCookieSourceOptions, BaseCookieTokenSource, @@ -13,7 +13,7 @@ import { * support multiple Set-Cookie headers, so we just 'join' them with a comma. */ class TestAdapter implements CookieAdapter { - constructor(private options: BaseCookieSourceOptions) {} + constructor(private options: BaseCookieSourceOptions) {} getCookie(request: Request, name: string): string | undefined { const header = request.headers.get("cookie"); @@ -55,7 +55,7 @@ class TestAdapter implements CookieAdapter { class TestCookieTokenSource extends BaseCookieTokenSource { protected adapter: CookieAdapter; - constructor(options: BaseCookieSourceOptions) { + constructor(options: BaseCookieSourceOptions) { super(options); this.adapter = new TestAdapter(options); } @@ -282,7 +282,7 @@ describe("CookieTokenSource", () => { cookieTokenSource.deleteAccessToken(request, response); const cookies = getCookies(response); - expect(cookies).toEqual([{ userToken: "" }, { guestToken: "" }]); + expect(cookies).toEqual([{ userToken: "", Path: "/" }, { guestToken: "", Path: "/" }]); }); // Test for deleting refresh tokens @@ -305,8 +305,8 @@ describe("CookieTokenSource", () => { const cookies = getCookies(response); expect(cookies).toEqual([ { refreshToken: "", Path: "/refresh" }, - { guestRefreshTokenExists: "" }, - { userRefreshTokenExists: "" }, + { guestRefreshTokenExists: "" , Path: "/"}, + { userRefreshTokenExists: "" , Path: "/"}, ]); }); @@ -330,4 +330,31 @@ describe("CookieTokenSource", () => { const cookies = getCookies(response); expect(cookies).toEqual([{ refreshToken: "", Path: "/refresh" }]); }); + + it("should get the refresh path from the refresh path function", () => { + const request: Request = new Request("http://localhost"); + + const cookieTokenSource = new TestCookieTokenSource({ + secure: true, + sameSite: "strict", + refreshTokenPath: () => "/refresh", + }); + + const result = cookieTokenSource["_getRefreshTokenPath"](request); + expect(result).toBe("/refresh"); + }); + + it("should get the cookiePath from the cookiePath function", () => { + const request: Request = new Request("http://localhost"); + + const cookieTokenSource = new TestCookieTokenSource({ + secure: true, + sameSite: "strict", + refreshTokenPath: "/refresh", + cookiePathFn: () => "/cookie", + }); + + const result = cookieTokenSource["options"].cookiePathFn?.(request); + expect(result).toBe("/cookie"); + }); }); diff --git a/packages/core/src/tokensource/cookies-base.ts b/packages/core/src/tokensource/cookies-base.ts index dd7d028..c1faf83 100644 --- a/packages/core/src/tokensource/cookies-base.ts +++ b/packages/core/src/tokensource/cookies-base.ts @@ -63,14 +63,15 @@ type CookieSettings = { expiresIn: number | "session"; }; -export type BaseCookieSourceOptions = { +export type BaseCookieSourceOptions = { secure: boolean; sameSite: "strict" | "lax" | "none" | boolean; - refreshTokenPath: string; + refreshTokenPath: string | ((request: TRequest) => string | undefined); cookieNames?: Partial; guestToken?: CookieSettings; userToken?: CookieSettings; refreshToken?: CookieSettings; + cookiePathFn?: (request: TRequest) => string | undefined; }; export abstract class BaseCookieTokenSource @@ -79,7 +80,7 @@ export abstract class BaseCookieTokenSource protected cookieNames: CookieNames; protected abstract adapter: CookieAdapter; - constructor(protected options: BaseCookieSourceOptions) { + constructor(protected options: BaseCookieSourceOptions) { this.cookieNames = { ...DEFAULT_COOKIE_NAMES, ...(options.cookieNames ?? {}), @@ -96,7 +97,7 @@ export abstract class BaseCookieTokenSource deleteRefreshToken(request: TRequest, response: TResponse): void { this.adapter.clearCookie(request, response, this.cookieNames.refreshToken, { - path: this.options.refreshTokenPath, + path: this._getRefreshTokenPath(request), domain: this.adapter.getPrivateDomain(request), }); @@ -128,6 +129,7 @@ export abstract class BaseCookieTokenSource if (this.adapter.getCookie(request, name)) { this.adapter.clearCookie(request, response, name, { domain: this.adapter.getPublicDomain(request), + path: this.options.cookiePathFn?.(request) ?? "/", }); } } @@ -140,6 +142,7 @@ export abstract class BaseCookieTokenSource if (this.adapter.getCookie(request, name)) { this.adapter.clearCookie(request, response, name, { domain: this.adapter.getPublicDomain(request), + path: this.options.cookiePathFn?.(request) ?? "/", }); } } @@ -180,7 +183,7 @@ export abstract class BaseCookieTokenSource opts.expiresIn === "session" ? undefined : new Date(Date.now() + opts.expiresIn * 1000), - path: "/", + path: this.options.cookiePathFn?.(request) ?? "/", }; if (isAuthenticated) { @@ -189,7 +192,7 @@ export abstract class BaseCookieTokenSource response, this.cookieNames.userData, token, - cookieOptions, + cookieOptions ); this.deleteAccessTokenByName( request, @@ -244,7 +247,7 @@ export abstract class BaseCookieTokenSource opts.expiresIn === "session" ? undefined : new Date(Date.now() + opts.expiresIn * 1000), - path: "/", + path: this.options.cookiePathFn?.(request) ?? "/", }; if (isAuthenticated) { @@ -299,7 +302,7 @@ export abstract class BaseCookieTokenSource { ...cookieOptions, httpOnly: true, - path: this.options.refreshTokenPath, + path: this._getRefreshTokenPath(request), }, ); @@ -331,4 +334,9 @@ export abstract class BaseCookieTokenSource ); } } + + private _getRefreshTokenPath(req: TRequest): string | undefined { + const path = this.options.refreshTokenPath; + return typeof path === "function" ? path(req) : path; + } } diff --git a/packages/express-adapter/src/cookies.ts b/packages/express-adapter/src/cookies.ts index 0e77239..31dafbf 100644 --- a/packages/express-adapter/src/cookies.ts +++ b/packages/express-adapter/src/cookies.ts @@ -5,9 +5,11 @@ import { } from "@labdigital/federated-token/tokensource"; import type { CookieOptions, Request, Response } from "express"; -type ExpressCookieSourceOptions = BaseCookieSourceOptions & { +type ExpressCookieSourceOptions = BaseCookieSourceOptions & { + refreshTokenPath: string | ((request: Request) => string | undefined); publicDomainFn?: (request: Request) => string | undefined; privateDomainFn?: (request: Request) => string | undefined; + cookiePathFn?: (request: Request) => string | undefined; }; class ExpressCookieAdapter implements CookieAdapter { diff --git a/packages/fastify-adapter/src/cookies.test.ts b/packages/fastify-adapter/src/cookies.test.ts index 401bef8..46edbd9 100644 --- a/packages/fastify-adapter/src/cookies.test.ts +++ b/packages/fastify-adapter/src/cookies.test.ts @@ -1,6 +1,6 @@ import type { CookieSerializeOptions } from "@fastify/cookie"; import type { FastifyReply, FastifyRequest } from "fastify"; -import { describe, expect, it } from "vitest"; +import { describe, expect, it, } from "vitest"; import { CookieTokenSource } from "./cookies"; type CookieValue = { diff --git a/packages/fastify-adapter/src/cookies.ts b/packages/fastify-adapter/src/cookies.ts index 4f9159b..1086cb2 100644 --- a/packages/fastify-adapter/src/cookies.ts +++ b/packages/fastify-adapter/src/cookies.ts @@ -6,9 +6,11 @@ import { } from "@labdigital/federated-token/tokensource"; import type { FastifyReply, FastifyRequest } from "fastify"; -type FastifyCookieSourceOptions = BaseCookieSourceOptions & { +type FastifyCookieSourceOptions = BaseCookieSourceOptions & { + refreshTokenPath: string | ((request: Request) => string | undefined); publicDomainFn?: (request: FastifyRequest) => string | undefined; privateDomainFn?: (request: FastifyRequest) => string | undefined; + cookiePathFn?: (request: Request) => string | undefined; }; class FastifyCookieAdapter From e4e2f8960cde104e9c5fa35ecb715ae8778ddbe9 Mon Sep 17 00:00:00 2001 From: Paul Vaneveld Date: Wed, 17 Sep 2025 16:56:07 +0200 Subject: [PATCH 3/8] Revert "fix: fialing tests" This reverts commit c4380a4a166a808b07d57f9909d76472ec3e7d1e. --- packages/express-adapter/src/cookies.test.ts | 6 +++--- packages/fastify-adapter/src/cookies.test.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/express-adapter/src/cookies.test.ts b/packages/express-adapter/src/cookies.test.ts index e131e25..9898fba 100644 --- a/packages/express-adapter/src/cookies.test.ts +++ b/packages/express-adapter/src/cookies.test.ts @@ -114,7 +114,7 @@ describe("CookieTokenSource", () => { sameSite: "strict", domain: undefined, expires: undefined, - path: undefined, + path: "/", }); }); @@ -139,7 +139,7 @@ describe("CookieTokenSource", () => { secure: true, sameSite: "strict", domain: undefined, - path: undefined, + path: "/", expires: expect.any(Date), }); }); @@ -164,7 +164,7 @@ describe("CookieTokenSource", () => { sameSite: "strict", expires: undefined, domain: undefined, - path: undefined, + path: "/", }); }); diff --git a/packages/fastify-adapter/src/cookies.test.ts b/packages/fastify-adapter/src/cookies.test.ts index 46edbd9..274cfb7 100644 --- a/packages/fastify-adapter/src/cookies.test.ts +++ b/packages/fastify-adapter/src/cookies.test.ts @@ -126,7 +126,7 @@ describe("CookieTokenSource", () => { sameSite: "strict", domain: undefined, expires: undefined, - path: undefined, + path: "/", }); }); @@ -151,7 +151,7 @@ describe("CookieTokenSource", () => { secure: true, sameSite: "strict", domain: undefined, - path: undefined, + path: "/", expires: expect.any(Date), }); }); @@ -176,7 +176,7 @@ describe("CookieTokenSource", () => { sameSite: "strict", domain: undefined, expires: undefined, - path: undefined, + path: "/", }); }); From f51ce5de76b6c092572051d2c1c4cd361874e73f Mon Sep 17 00:00:00 2001 From: Paul Vaneveld Date: Wed, 17 Sep 2025 16:57:14 +0200 Subject: [PATCH 4/8] fix: linting --- packages/core/src/tokensource/cookies-base.test.ts | 11 +++++++---- packages/core/src/tokensource/cookies-base.ts | 2 +- packages/fastify-adapter/src/cookies.test.ts | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/core/src/tokensource/cookies-base.test.ts b/packages/core/src/tokensource/cookies-base.test.ts index 21c2a59..fcecafb 100644 --- a/packages/core/src/tokensource/cookies-base.test.ts +++ b/packages/core/src/tokensource/cookies-base.test.ts @@ -1,5 +1,5 @@ import { parse, type SerializeOptions, serialize } from "cookie"; -import { describe, expect, it, } from "vitest"; +import { describe, expect, it } from "vitest"; import { type BaseCookieSourceOptions, BaseCookieTokenSource, @@ -282,7 +282,10 @@ describe("CookieTokenSource", () => { cookieTokenSource.deleteAccessToken(request, response); const cookies = getCookies(response); - expect(cookies).toEqual([{ userToken: "", Path: "/" }, { guestToken: "", Path: "/" }]); + expect(cookies).toEqual([ + { userToken: "", Path: "/" }, + { guestToken: "", Path: "/" }, + ]); }); // Test for deleting refresh tokens @@ -305,8 +308,8 @@ describe("CookieTokenSource", () => { const cookies = getCookies(response); expect(cookies).toEqual([ { refreshToken: "", Path: "/refresh" }, - { guestRefreshTokenExists: "" , Path: "/"}, - { userRefreshTokenExists: "" , Path: "/"}, + { guestRefreshTokenExists: "", Path: "/" }, + { userRefreshTokenExists: "", Path: "/" }, ]); }); diff --git a/packages/core/src/tokensource/cookies-base.ts b/packages/core/src/tokensource/cookies-base.ts index c1faf83..c600c37 100644 --- a/packages/core/src/tokensource/cookies-base.ts +++ b/packages/core/src/tokensource/cookies-base.ts @@ -192,7 +192,7 @@ export abstract class BaseCookieTokenSource response, this.cookieNames.userData, token, - cookieOptions + cookieOptions, ); this.deleteAccessTokenByName( request, diff --git a/packages/fastify-adapter/src/cookies.test.ts b/packages/fastify-adapter/src/cookies.test.ts index 274cfb7..72189d9 100644 --- a/packages/fastify-adapter/src/cookies.test.ts +++ b/packages/fastify-adapter/src/cookies.test.ts @@ -1,6 +1,6 @@ import type { CookieSerializeOptions } from "@fastify/cookie"; import type { FastifyReply, FastifyRequest } from "fastify"; -import { describe, expect, it, } from "vitest"; +import { describe, expect, it } from "vitest"; import { CookieTokenSource } from "./cookies"; type CookieValue = { From 7940e40566ffad9c20908ee996f5cb80ddd72f91 Mon Sep 17 00:00:00 2001 From: Paul Vaneveld Date: Wed, 17 Sep 2025 17:01:33 +0200 Subject: [PATCH 5/8] chore: changeset --- .changeset/tidy-tips-study.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/tidy-tips-study.md diff --git a/.changeset/tidy-tips-study.md b/.changeset/tidy-tips-study.md new file mode 100644 index 0000000..af3171e --- /dev/null +++ b/.changeset/tidy-tips-study.md @@ -0,0 +1,7 @@ +--- +"@labdigital/federated-token-express-adapter": minor +"@labdigital/federated-token-fastify-adapter": minor +"@labdigital/federated-token": minor +--- + +Add support for setting the cookie path on both auth and refresh tokens From ae86ecfceb71e4c7edafd5c080e217a39d93cd81 Mon Sep 17 00:00:00 2001 From: Paul Vaneveld Date: Thu, 18 Sep 2025 09:21:16 +0200 Subject: [PATCH 6/8] fix: use fastify request --- packages/fastify-adapter/src/cookies.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/fastify-adapter/src/cookies.ts b/packages/fastify-adapter/src/cookies.ts index 1086cb2..d16eee7 100644 --- a/packages/fastify-adapter/src/cookies.ts +++ b/packages/fastify-adapter/src/cookies.ts @@ -7,10 +7,10 @@ import { import type { FastifyReply, FastifyRequest } from "fastify"; type FastifyCookieSourceOptions = BaseCookieSourceOptions & { - refreshTokenPath: string | ((request: Request) => string | undefined); + refreshTokenPath: string | ((request: FastifyRequest) => string | undefined); publicDomainFn?: (request: FastifyRequest) => string | undefined; privateDomainFn?: (request: FastifyRequest) => string | undefined; - cookiePathFn?: (request: Request) => string | undefined; + cookiePathFn?: (request: FastifyRequest) => string | undefined; }; class FastifyCookieAdapter From 49eeaca5f45a0fdc95fd8407f73553695759ef05 Mon Sep 17 00:00:00 2001 From: Paul Vaneveld Date: Thu, 18 Sep 2025 10:05:58 +0200 Subject: [PATCH 7/8] chore: changeset --- .changeset/pre.json | 13 +++++++++++++ .../{tidy-tips-study.md => slick-rabbits-count.md} | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 .changeset/pre.json rename .changeset/{tidy-tips-study.md => slick-rabbits-count.md} (67%) diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 0000000..b71f52b --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,13 @@ +{ + "mode": "pre", + "tag": "beta", + "initialVersions": { + "@labdigital/federated-token-apollo": "2.1.0", + "@labdigital/federated-token": "2.1.0", + "@labdigital/federated-token-express-adapter": "2.1.0", + "@labdigital/federated-token-fastify-adapter": "2.1.0", + "@labdigital/federated-token-react": "2.1.0", + "@labdigital/federated-token-yoga": "2.1.0" + }, + "changesets": [] +} diff --git a/.changeset/tidy-tips-study.md b/.changeset/slick-rabbits-count.md similarity index 67% rename from .changeset/tidy-tips-study.md rename to .changeset/slick-rabbits-count.md index af3171e..0b02320 100644 --- a/.changeset/tidy-tips-study.md +++ b/.changeset/slick-rabbits-count.md @@ -4,4 +4,4 @@ "@labdigital/federated-token": minor --- -Add support for setting the cookie path on both auth and refresh tokens +Add support for cookie path and refresh token path function From 73b29def89edb159f4f3baee37864c77f8fa0405 Mon Sep 17 00:00:00 2001 From: Paul Vaneveld Date: Thu, 18 Sep 2025 10:07:29 +0200 Subject: [PATCH 8/8] chore: lint --- .changeset/pre.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.changeset/pre.json b/.changeset/pre.json index b71f52b..9e4948a 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -1,13 +1,13 @@ { - "mode": "pre", - "tag": "beta", - "initialVersions": { - "@labdigital/federated-token-apollo": "2.1.0", - "@labdigital/federated-token": "2.1.0", - "@labdigital/federated-token-express-adapter": "2.1.0", - "@labdigital/federated-token-fastify-adapter": "2.1.0", - "@labdigital/federated-token-react": "2.1.0", - "@labdigital/federated-token-yoga": "2.1.0" - }, - "changesets": [] + "mode": "pre", + "tag": "beta", + "initialVersions": { + "@labdigital/federated-token-apollo": "2.1.0", + "@labdigital/federated-token": "2.1.0", + "@labdigital/federated-token-express-adapter": "2.1.0", + "@labdigital/federated-token-fastify-adapter": "2.1.0", + "@labdigital/federated-token-react": "2.1.0", + "@labdigital/federated-token-yoga": "2.1.0" + }, + "changesets": [] }