@civic/auth 0.1.6-beta.2 → 0.1.6-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/cjs/shared/version.d.ts +1 -1
  2. package/dist/cjs/shared/version.js +1 -1
  3. package/dist/cjs/shared/version.js.map +1 -1
  4. package/dist/esm/shared/version.d.ts +1 -1
  5. package/dist/esm/shared/version.js +1 -1
  6. package/dist/esm/shared/version.js.map +1 -1
  7. package/dist/src/lib/cookies.d.ts.map +1 -0
  8. package/dist/src/lib/cookies.js +51 -0
  9. package/dist/src/lib/cookies.js.map +1 -0
  10. package/dist/test/unit/lib/cookies.test.d.ts.map +1 -0
  11. package/dist/test/unit/lib/cookies.test.js +25 -0
  12. package/dist/test/unit/lib/cookies.test.js.map +1 -0
  13. package/dist/test/unit/nextjs/config.test.d.ts.map +1 -0
  14. package/dist/test/unit/nextjs/config.test.js +203 -0
  15. package/dist/test/unit/nextjs/config.test.js.map +1 -0
  16. package/dist/test/unit/server/refresh.test.d.ts.map +1 -0
  17. package/dist/test/unit/server/refresh.test.js +55 -0
  18. package/dist/test/unit/server/refresh.test.js.map +1 -0
  19. package/dist/test/unit/shared/GenericStandaloneAuthenticationRefresher.test.d.ts +2 -0
  20. package/dist/test/unit/shared/GenericStandaloneAuthenticationRefresher.test.d.ts.map +1 -0
  21. package/dist/test/unit/shared/GenericStandaloneAuthenticationRefresher.test.js +144 -0
  22. package/dist/test/unit/shared/GenericStandaloneAuthenticationRefresher.test.js.map +1 -0
  23. package/dist/test/unit/shared/GenericStandaloneAuthenticationRefresher.text.d.ts +2 -0
  24. package/dist/test/unit/shared/GenericStandaloneAuthenticationRefresher.text.d.ts.map +1 -0
  25. package/dist/test/unit/shared/GenericStandaloneAuthenticationRefresher.text.js +144 -0
  26. package/dist/test/unit/shared/GenericStandaloneAuthenticationRefresher.text.js.map +1 -0
  27. package/dist/tsconfig.cjs.tsbuildinfo +1 -1
  28. package/dist/tsconfig.esm.tsbuildinfo +1 -1
  29. package/dist/tsconfig.tsbuildinfo +1 -0
  30. package/package.json +19 -18
  31. package/dist/cjs/reactjs/hooks/useAuth.d.ts +0 -3
  32. package/dist/cjs/reactjs/hooks/useAuth.d.ts.map +0 -1
  33. package/dist/cjs/reactjs/hooks/useAuth.js +0 -15
  34. package/dist/cjs/reactjs/hooks/useAuth.js.map +0 -1
  35. package/dist/cjs/shared/hooks/useClientTokenExchangeSession.d.ts +0 -3
  36. package/dist/cjs/shared/hooks/useClientTokenExchangeSession.d.ts.map +0 -1
  37. package/dist/cjs/shared/hooks/useClientTokenExchangeSession.js +0 -16
  38. package/dist/cjs/shared/hooks/useClientTokenExchangeSession.js.map +0 -1
  39. package/dist/cjs/shared/providers/AuthProvider.d.ts +0 -22
  40. package/dist/cjs/shared/providers/AuthProvider.d.ts.map +0 -1
  41. package/dist/cjs/shared/providers/AuthProvider.js +0 -108
  42. package/dist/cjs/shared/providers/AuthProvider.js.map +0 -1
  43. package/dist/cjs/shared/providers/CivicAuthProvider.d.ts +0 -6
  44. package/dist/cjs/shared/providers/CivicAuthProvider.d.ts.map +0 -1
  45. package/dist/cjs/shared/providers/CivicAuthProvider.js +0 -38
  46. package/dist/cjs/shared/providers/CivicAuthProvider.js.map +0 -1
  47. package/dist/cjs/shared/providers/ClientTokenExchangeSessionProvider.d.ts +0 -17
  48. package/dist/cjs/shared/providers/ClientTokenExchangeSessionProvider.d.ts.map +0 -1
  49. package/dist/cjs/shared/providers/ClientTokenExchangeSessionProvider.js +0 -168
  50. package/dist/cjs/shared/providers/ClientTokenExchangeSessionProvider.js.map +0 -1
  51. package/dist/esm/reactjs/hooks/useAuth.d.ts +0 -3
  52. package/dist/esm/reactjs/hooks/useAuth.d.ts.map +0 -1
  53. package/dist/esm/reactjs/hooks/useAuth.js +0 -12
  54. package/dist/esm/reactjs/hooks/useAuth.js.map +0 -1
  55. package/dist/esm/shared/hooks/useClientTokenExchangeSession.d.ts +0 -3
  56. package/dist/esm/shared/hooks/useClientTokenExchangeSession.d.ts.map +0 -1
  57. package/dist/esm/shared/hooks/useClientTokenExchangeSession.js +0 -13
  58. package/dist/esm/shared/hooks/useClientTokenExchangeSession.js.map +0 -1
  59. package/dist/esm/shared/providers/AuthProvider.d.ts +0 -22
  60. package/dist/esm/shared/providers/AuthProvider.d.ts.map +0 -1
  61. package/dist/esm/shared/providers/AuthProvider.js +0 -72
  62. package/dist/esm/shared/providers/AuthProvider.js.map +0 -1
  63. package/dist/esm/shared/providers/CivicAuthProvider.d.ts +0 -6
  64. package/dist/esm/shared/providers/CivicAuthProvider.d.ts.map +0 -1
  65. package/dist/esm/shared/providers/CivicAuthProvider.js +0 -32
  66. package/dist/esm/shared/providers/CivicAuthProvider.js.map +0 -1
  67. package/dist/esm/shared/providers/ClientTokenExchangeSessionProvider.d.ts +0 -17
  68. package/dist/esm/shared/providers/ClientTokenExchangeSessionProvider.d.ts.map +0 -1
  69. package/dist/esm/shared/providers/ClientTokenExchangeSessionProvider.js +0 -131
  70. package/dist/esm/shared/providers/ClientTokenExchangeSessionProvider.js.map +0 -1
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "@civic/auth:0.1.6-beta.2";
1
+ export declare const VERSION = "@civic/auth:0.1.6-beta.3";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -2,5 +2,5 @@
2
2
  // This is an auto-generated file. Do not edit.
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.VERSION = void 0;
5
- exports.VERSION = "@civic/auth:0.1.6-beta.2";
5
+ exports.VERSION = "@civic/auth:0.1.6-beta.3";
6
6
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/shared/version.ts"],"names":[],"mappings":";AAAA,+CAA+C;;;AAElC,QAAA,OAAO,GAAG,0BAA0B,CAAC","sourcesContent":["// This is an auto-generated file. Do not edit.\n\nexport const VERSION = \"@civic/auth:0.1.6-beta.2\";\n"]}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/shared/version.ts"],"names":[],"mappings":";AAAA,+CAA+C;;;AAElC,QAAA,OAAO,GAAG,0BAA0B,CAAC","sourcesContent":["// This is an auto-generated file. Do not edit.\n\nexport const VERSION = \"@civic/auth:0.1.6-beta.3\";\n"]}
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "@civic/auth:0.1.6-beta.2";
1
+ export declare const VERSION = "@civic/auth:0.1.6-beta.3";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // This is an auto-generated file. Do not edit.
2
- export const VERSION = "@civic/auth:0.1.6-beta.2";
2
+ export const VERSION = "@civic/auth:0.1.6-beta.3";
3
3
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/shared/version.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAE/C,MAAM,CAAC,MAAM,OAAO,GAAG,0BAA0B,CAAC","sourcesContent":["// This is an auto-generated file. Do not edit.\n\nexport const VERSION = \"@civic/auth:0.1.6-beta.2\";\n"]}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/shared/version.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAE/C,MAAM,CAAC,MAAM,OAAO,GAAG,0BAA0B,CAAC","sourcesContent":["// This is an auto-generated file. Do not edit.\n\nexport const VERSION = \"@civic/auth:0.1.6-beta.3\";\n"]}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../../src/lib/cookies.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAK7E,QAAA,MAAM,oBAAoB,aACd;IACR,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,EAAE,4DAqBJ,CAAC;AAEF,cAAM,oBAAqB,SAAQ,aAAa;IAClC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC;gBAAnC,MAAM,GAAE,OAAO,CAAC,kBAAkB,CAAM;IAOvD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAUxC,GAAG,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG5D;AAED,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,CAAC"}
@@ -0,0 +1,51 @@
1
+ import { CookieStorage } from "@/shared/lib/storage.js";
2
+ // TODO move this into its own CookieStorage class that implements AuthStorage
3
+ // and provides getters and a non-implemented setter
4
+ // it should just return the string and let the user etc. be parsed by the caller
5
+ const getWindowCookieValue = (requests) => {
6
+ const cookie = window.document.cookie;
7
+ if (!cookie)
8
+ return null;
9
+ const cookies = cookie.split(";");
10
+ const response = {};
11
+ for (const c of cookies) {
12
+ const [name, value] = c.trim().split("=");
13
+ const request = requests.find((r) => r.key === name);
14
+ if (value && request) {
15
+ try {
16
+ const decodeURIComponentValue = decodeURIComponent(value);
17
+ response[request.key] = request.parseJson
18
+ ? JSON.parse(decodeURIComponentValue)
19
+ : decodeURIComponentValue;
20
+ }
21
+ catch {
22
+ response[request.key] = value;
23
+ }
24
+ }
25
+ }
26
+ return response;
27
+ };
28
+ class BrowserCookieStorage extends CookieStorage {
29
+ config;
30
+ constructor(config = {}) {
31
+ super({
32
+ secure: true,
33
+ httpOnly: false,
34
+ });
35
+ this.config = config;
36
+ }
37
+ async get(key) {
38
+ const value = `; ${document.cookie}`;
39
+ const parts = value.split(`; ${key}=`);
40
+ if (parts && parts.length === 2) {
41
+ return parts.pop()?.split(";").shift() ?? null;
42
+ }
43
+ return null;
44
+ }
45
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
46
+ async set(_key, _value) {
47
+ throw new Error("Not implemented.");
48
+ }
49
+ }
50
+ export { BrowserCookieStorage, getWindowCookieValue };
51
+ //# sourceMappingURL=cookies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookies.js","sourceRoot":"","sources":["../../../src/lib/cookies.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAGxD,8EAA8E;AAC9E,oDAAoD;AACpD,iFAAiF;AACjF,MAAM,oBAAoB,GAAG,CAC3B,QAIG,EACH,EAAE;IACF,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;IACtC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAqD,EAAE,CAAC;IACtE,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;QACrD,IAAI,KAAK,IAAI,OAAO,EAAE,CAAC;YACrB,IAAI,CAAC;gBACH,MAAM,uBAAuB,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;gBAC1D,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,SAAS;oBACvC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,uBAAuB,CAAC;oBACrC,CAAC,CAAC,uBAAuB,CAAC;YAC9B,CAAC;YAAC,MAAM,CAAC;gBACP,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YAChC,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAEF,MAAM,oBAAqB,SAAQ,aAAa;IACzB;IAArB,YAAqB,SAAsC,EAAE;QAC3D,KAAK,CAAC;YACJ,MAAM,EAAE,IAAI;YACZ,QAAQ,EAAE,KAAK;SAChB,CAAC,CAAC;QAJgB,WAAM,GAAN,MAAM,CAAkC;IAK7D,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,MAAM,KAAK,GAAG,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC;QACvC,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,IAAI,IAAI,CAAC;QACjD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,6DAA6D;IAC7D,KAAK,CAAC,GAAG,CAAC,IAAiB,EAAE,MAAc;QACzC,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACtC,CAAC;CACF;AAED,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,CAAC","sourcesContent":["import { CookieStorage } from \"@/shared/lib/storage.js\";\nimport type { OAuthTokens, TokensCookieConfig } from \"@/shared/lib/types.js\";\n\n// TODO move this into its own CookieStorage class that implements AuthStorage\n// and provides getters and a non-implemented setter\n// it should just return the string and let the user etc. be parsed by the caller\nconst getWindowCookieValue = (\n requests: {\n key: string;\n window: Window;\n parseJson?: boolean;\n }[],\n) => {\n const cookie = window.document.cookie;\n if (!cookie) return null;\n const cookies = cookie.split(\";\");\n const response: Record<string, string | Record<string, unknown>> = {};\n for (const c of cookies) {\n const [name, value] = c.trim().split(\"=\");\n const request = requests.find((r) => r.key === name);\n if (value && request) {\n try {\n const decodeURIComponentValue = decodeURIComponent(value);\n response[request.key] = request.parseJson\n ? JSON.parse(decodeURIComponentValue)\n : decodeURIComponentValue;\n } catch {\n response[request.key] = value;\n }\n }\n }\n return response;\n};\n\nclass BrowserCookieStorage extends CookieStorage {\n constructor(readonly config: Partial<TokensCookieConfig> = {}) {\n super({\n secure: true,\n httpOnly: false,\n });\n }\n\n async get(key: string): Promise<string | null> {\n const value = `; ${document.cookie}`;\n const parts = value.split(`; ${key}=`);\n if (parts && parts.length === 2) {\n return parts.pop()?.split(\";\").shift() ?? null;\n }\n return null;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n async set(_key: OAuthTokens, _value: string): Promise<void> {\n throw new Error(\"Not implemented.\");\n }\n}\n\nexport { BrowserCookieStorage, getWindowCookieValue };\n"]}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookies.test.d.ts","sourceRoot":"","sources":["../../../../test/unit/lib/cookies.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,25 @@
1
+ import { BrowserCookieStorage } from "@/lib/cookies.js";
2
+ import { describe, it } from "vitest";
3
+ import { OAuthTokens } from "@/shared/lib/types.js";
4
+ describe("cookies.ts", () => {
5
+ describe("BrowserCookieStorage", () => {
6
+ const originalCookieValue = document.cookie;
7
+ afterAll(() => {
8
+ document.cookie = originalCookieValue;
9
+ });
10
+ it("should get a cookie values", () => {
11
+ const cookieStorage = new BrowserCookieStorage();
12
+ document.cookie = "testCookie=testValue";
13
+ document.cookie = "testCookie2=testValue2";
14
+ document.cookie = "testCookie3=testValue3";
15
+ expect(cookieStorage.get("testCookie")).resolves.toBe("testValue");
16
+ expect(cookieStorage.get("testCookie2")).resolves.toBe("testValue2");
17
+ expect(cookieStorage.get("testCookie3")).resolves.toBe("testValue3");
18
+ });
19
+ it("should throw an error when trying to set a value", () => {
20
+ const cookieStorage = new BrowserCookieStorage();
21
+ expect(() => cookieStorage.set(OAuthTokens.TIMESTAMP, "cookieValue")).rejects.toThrow("Not implemented.");
22
+ });
23
+ });
24
+ });
25
+ //# sourceMappingURL=cookies.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookies.test.js","sourceRoot":"","sources":["../../../../test/unit/lib/cookies.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;QACpC,MAAM,mBAAmB,GAAG,QAAQ,CAAC,MAAM,CAAC;QAC5C,QAAQ,CAAC,GAAG,EAAE;YACZ,QAAQ,CAAC,MAAM,GAAG,mBAAmB,CAAC;QACxC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;YACpC,MAAM,aAAa,GAAG,IAAI,oBAAoB,EAAE,CAAC;YACjD,QAAQ,CAAC,MAAM,GAAG,sBAAsB,CAAC;YACzC,QAAQ,CAAC,MAAM,GAAG,wBAAwB,CAAC;YAC3C,QAAQ,CAAC,MAAM,GAAG,wBAAwB,CAAC;YAC3C,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACnE,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACrE,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACvE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;YAC1D,MAAM,aAAa,GAAG,IAAI,oBAAoB,EAAE,CAAC;YACjD,MAAM,CAAC,GAAG,EAAE,CACV,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC,SAAS,EAAE,aAAa,CAAC,CACxD,CAAC,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { BrowserCookieStorage } from \"@/lib/cookies.js\";\nimport { describe, it } from \"vitest\";\nimport { OAuthTokens } from \"@/shared/lib/types.js\";\n\ndescribe(\"cookies.ts\", () => {\n describe(\"BrowserCookieStorage\", () => {\n const originalCookieValue = document.cookie;\n afterAll(() => {\n document.cookie = originalCookieValue;\n });\n\n it(\"should get a cookie values\", () => {\n const cookieStorage = new BrowserCookieStorage();\n document.cookie = \"testCookie=testValue\";\n document.cookie = \"testCookie2=testValue2\";\n document.cookie = \"testCookie3=testValue3\";\n expect(cookieStorage.get(\"testCookie\")).resolves.toBe(\"testValue\");\n expect(cookieStorage.get(\"testCookie2\")).resolves.toBe(\"testValue2\");\n expect(cookieStorage.get(\"testCookie3\")).resolves.toBe(\"testValue3\");\n });\n\n it(\"should throw an error when trying to set a value\", () => {\n const cookieStorage = new BrowserCookieStorage();\n expect(() =>\n cookieStorage.set(OAuthTokens.TIMESTAMP, \"cookieValue\"),\n ).rejects.toThrow(\"Not implemented.\");\n });\n });\n});\n"]}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.test.d.ts","sourceRoot":"","sources":["../../../../test/unit/nextjs/config.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,203 @@
1
+ /* eslint-disable turbo/no-undeclared-env-vars */
2
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
3
+ import { resolveAuthConfig, createCivicAuthPlugin, defaultAuthConfig, } from "@/nextjs/config.js";
4
+ import { DEFAULT_AUTH_SERVER } from "@/constants.js";
5
+ const defaultCookies = {
6
+ id_token: {
7
+ httpOnly: true,
8
+ path: "/",
9
+ sameSite: "strict",
10
+ secure: false,
11
+ },
12
+ access_token: {
13
+ httpOnly: true,
14
+ path: "/",
15
+ sameSite: "strict",
16
+ secure: false,
17
+ },
18
+ refresh_token: {
19
+ httpOnly: true,
20
+ path: "/",
21
+ sameSite: "strict",
22
+ secure: false,
23
+ },
24
+ expires_in: {
25
+ httpOnly: false,
26
+ path: "/",
27
+ sameSite: "strict",
28
+ secure: false,
29
+ },
30
+ timestamp: {
31
+ httpOnly: false,
32
+ path: "/",
33
+ sameSite: "strict",
34
+ secure: false,
35
+ },
36
+ code_verifier: {
37
+ httpOnly: true,
38
+ path: "/",
39
+ sameSite: "strict",
40
+ secure: false,
41
+ },
42
+ app_url: {
43
+ httpOnly: true,
44
+ path: "/",
45
+ sameSite: "strict",
46
+ secure: false,
47
+ },
48
+ };
49
+ describe("nextjs/config", () => {
50
+ const originalEnv = process.env;
51
+ beforeEach(() => {
52
+ vi.resetModules();
53
+ process.env = { ...originalEnv, NODE_ENV: "development" };
54
+ });
55
+ afterEach(() => {
56
+ process.env = originalEnv;
57
+ vi.restoreAllMocks();
58
+ });
59
+ describe("resolveAuthConfig", () => {
60
+ it("should throw an error if clientId is not provided", () => {
61
+ expect(() => resolveAuthConfig()).toThrowError("Civic Auth client ID is required");
62
+ });
63
+ it("should use default values when no config is provided", () => {
64
+ // client id must be defined
65
+ process.env._civic_auth_client_id = "clientId";
66
+ const result = resolveAuthConfig();
67
+ expect(result).toEqual({ ...defaultAuthConfig, clientId: "clientId" });
68
+ });
69
+ it("should override default values with provided config", () => {
70
+ const config = {
71
+ clientId: "clientId",
72
+ callbackUrl: "/custom/callback",
73
+ loginUrl: "/custom/login",
74
+ include: ["/protected/*"],
75
+ exclude: ["/public/*"],
76
+ };
77
+ const result = resolveAuthConfig(config);
78
+ expect(result.callbackUrl).toBe("/custom/callback");
79
+ expect(result.loginUrl).toBe("/custom/login");
80
+ expect(result.include).toEqual(["/protected/*"]);
81
+ expect(result.exclude).toEqual(["/public/*"]);
82
+ });
83
+ it("should use environment variables if set", () => {
84
+ process.env._civic_auth_client_id = "clientId";
85
+ process.env._civic_auth_callback_url = "/env/callback";
86
+ process.env._civic_auth_login_url = "/env/login";
87
+ process.env._civic_auth_includes = "/env/protected/*";
88
+ process.env._civic_auth_excludes = "/env/public/*";
89
+ const result = resolveAuthConfig();
90
+ expect(result.callbackUrl).toBe("/env/callback");
91
+ expect(result.loginUrl).toBe("/env/login");
92
+ expect(result.include).toEqual(["/env/protected/*"]);
93
+ expect(result.exclude).toEqual(["/env/public/*"]);
94
+ });
95
+ it("should prioritize provided config over environment variables", () => {
96
+ process.env._civic_auth_callback_url = "/env/callback";
97
+ const config = {
98
+ clientId: "clientId",
99
+ callbackUrl: "/config/callback",
100
+ };
101
+ const result = resolveAuthConfig(config);
102
+ expect(result.callbackUrl).toBe("/config/callback");
103
+ });
104
+ it("should merge cookie configurations correctly", () => {
105
+ const config = {
106
+ clientId: "clientId",
107
+ cookies: {
108
+ tokens: {
109
+ id_token: { ...defaultCookies.id_token, secure: false },
110
+ },
111
+ user: {
112
+ httpOnly: false,
113
+ secure: false,
114
+ sameSite: "strict",
115
+ },
116
+ },
117
+ };
118
+ const result = resolveAuthConfig(config);
119
+ expect(result.cookies.tokens).toEqual({
120
+ ...defaultCookies,
121
+ id_token: { ...defaultCookies.id_token, secure: false },
122
+ });
123
+ expect(result.cookies.user).toEqual({
124
+ httpOnly: false,
125
+ secure: false,
126
+ sameSite: "strict",
127
+ path: "/",
128
+ maxAge: 3600,
129
+ });
130
+ });
131
+ it("should set secure to true in the default cookie configs if NODE_ENV is not development", async () => {
132
+ process.env = { ...originalEnv, NODE_ENV: "production" };
133
+ // re-import to get the configs with the updated NODE_ENV
134
+ const configs = await import("../../../src/nextjs/config.js");
135
+ const result = configs.resolveAuthConfig({ clientId: "clientId" });
136
+ Object.values(result.cookies.tokens).forEach((cookie) => {
137
+ expect(cookie.secure).toBe(true);
138
+ });
139
+ });
140
+ });
141
+ describe("createCivicAuthPlugin", () => {
142
+ it("should return a function", () => {
143
+ const plugin = createCivicAuthPlugin({ clientId: "clientId" });
144
+ expect(typeof plugin).toBe("function");
145
+ });
146
+ it("should set environment variables based on resolved config", () => {
147
+ const config = {
148
+ clientId: "clientId",
149
+ callbackUrl: "/custom/callback",
150
+ loginUrl: "/custom/login",
151
+ logoutUrl: "/custom/logout",
152
+ logoutCallbackUrl: "/custom/logoutcallback",
153
+ include: ["/protected/*"],
154
+ exclude: ["/public/*"],
155
+ cookies: {
156
+ tokens: defaultCookies,
157
+ user: {
158
+ secure: false,
159
+ sameSite: "strict",
160
+ maxAge: 3600,
161
+ },
162
+ },
163
+ };
164
+ const plugin = createCivicAuthPlugin(config);
165
+ const nextConfig = plugin({});
166
+ expect(nextConfig.env).toEqual({
167
+ _civic_auth_client_id: "clientId",
168
+ _civic_auth_callback_url: "/custom/callback",
169
+ _civic_auth_challenge_url: "/api/auth/challenge",
170
+ _civic_auth_login_url: "/custom/login",
171
+ _civic_auth_logout_url: "/custom/logout",
172
+ _civic_auth_logout_callback_url: "/custom/logoutcallback",
173
+ _civic_auth_includes: "/protected/*",
174
+ _civic_auth_excludes: "/public/*",
175
+ _civic_oauth_server: DEFAULT_AUTH_SERVER,
176
+ _civic_auth_cookie_config: JSON.stringify({
177
+ tokens: defaultCookies,
178
+ user: {
179
+ secure: false,
180
+ httpOnly: false,
181
+ sameSite: "strict",
182
+ path: "/",
183
+ maxAge: 3600,
184
+ },
185
+ }),
186
+ });
187
+ });
188
+ it("should merge with existing Next.js config", () => {
189
+ const existingConfig = {
190
+ reactStrictMode: true,
191
+ env: {
192
+ CUSTOM_VAR: "value",
193
+ },
194
+ };
195
+ const plugin = createCivicAuthPlugin({ clientId: "clientId" });
196
+ const nextConfig = plugin(existingConfig);
197
+ expect(nextConfig.reactStrictMode).toBe(true);
198
+ expect(nextConfig.env).toHaveProperty("CUSTOM_VAR", "value");
199
+ expect(nextConfig.env).toHaveProperty("_civic_auth_callback_url");
200
+ });
201
+ });
202
+ });
203
+ //# sourceMappingURL=config.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.test.js","sourceRoot":"","sources":["../../../../test/unit/nextjs/config.test.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable turbo/no-undeclared-env-vars */\nimport { describe, it, expect, beforeEach, afterEach, vi } from \"vitest\";\nimport {\n resolveAuthConfig,\n createCivicAuthPlugin,\n type AuthConfig,\n defaultAuthConfig,\n} from \"@/nextjs/config.js\";\nimport type { TokensCookieConfig } from \"@/index.js\";\nimport { DEFAULT_AUTH_SERVER } from \"@/constants.js\";\n\nconst defaultCookies = {\n id_token: {\n httpOnly: true,\n path: \"/\",\n sameSite: \"strict\",\n secure: false,\n },\n access_token: {\n httpOnly: true,\n path: \"/\",\n sameSite: \"strict\",\n secure: false,\n },\n refresh_token: {\n httpOnly: true,\n path: \"/\",\n sameSite: \"strict\",\n secure: false,\n },\n expires_in: {\n httpOnly: false,\n path: \"/\",\n sameSite: \"strict\",\n secure: false,\n },\n timestamp: {\n httpOnly: false,\n path: \"/\",\n sameSite: \"strict\",\n secure: false,\n },\n code_verifier: {\n httpOnly: true,\n path: \"/\",\n sameSite: \"strict\",\n secure: false,\n },\n app_url: {\n httpOnly: true,\n path: \"/\",\n sameSite: \"strict\",\n secure: false,\n },\n} as TokensCookieConfig;\n\ndescribe(\"nextjs/config\", () => {\n const originalEnv = process.env;\n\n beforeEach(() => {\n vi.resetModules();\n process.env = { ...originalEnv, NODE_ENV: \"development\" };\n });\n\n afterEach(() => {\n process.env = originalEnv;\n vi.restoreAllMocks();\n });\n\n describe(\"resolveAuthConfig\", () => {\n it(\"should throw an error if clientId is not provided\", () => {\n expect(() => resolveAuthConfig()).toThrowError(\n \"Civic Auth client ID is required\",\n );\n });\n\n it(\"should use default values when no config is provided\", () => {\n // client id must be defined\n process.env._civic_auth_client_id = \"clientId\";\n const result = resolveAuthConfig();\n expect(result).toEqual({ ...defaultAuthConfig, clientId: \"clientId\" });\n });\n\n it(\"should override default values with provided config\", () => {\n const config: AuthConfig = {\n clientId: \"clientId\",\n callbackUrl: \"/custom/callback\",\n loginUrl: \"/custom/login\",\n include: [\"/protected/*\"],\n exclude: [\"/public/*\"],\n };\n const result = resolveAuthConfig(config);\n expect(result.callbackUrl).toBe(\"/custom/callback\");\n expect(result.loginUrl).toBe(\"/custom/login\");\n expect(result.include).toEqual([\"/protected/*\"]);\n expect(result.exclude).toEqual([\"/public/*\"]);\n });\n\n it(\"should use environment variables if set\", () => {\n process.env._civic_auth_client_id = \"clientId\";\n process.env._civic_auth_callback_url = \"/env/callback\";\n process.env._civic_auth_login_url = \"/env/login\";\n process.env._civic_auth_includes = \"/env/protected/*\";\n process.env._civic_auth_excludes = \"/env/public/*\";\n\n const result = resolveAuthConfig();\n expect(result.callbackUrl).toBe(\"/env/callback\");\n expect(result.loginUrl).toBe(\"/env/login\");\n expect(result.include).toEqual([\"/env/protected/*\"]);\n expect(result.exclude).toEqual([\"/env/public/*\"]);\n });\n\n it(\"should prioritize provided config over environment variables\", () => {\n process.env._civic_auth_callback_url = \"/env/callback\";\n const config: AuthConfig = {\n clientId: \"clientId\",\n callbackUrl: \"/config/callback\",\n };\n\n const result = resolveAuthConfig(config);\n expect(result.callbackUrl).toBe(\"/config/callback\");\n });\n\n it(\"should merge cookie configurations correctly\", () => {\n const config: AuthConfig = {\n clientId: \"clientId\",\n cookies: {\n tokens: {\n id_token: { ...defaultCookies.id_token, secure: false },\n } as TokensCookieConfig,\n user: {\n httpOnly: false,\n secure: false,\n sameSite: \"strict\",\n },\n },\n };\n\n const result = resolveAuthConfig(config);\n expect(result.cookies.tokens).toEqual({\n ...defaultCookies,\n id_token: { ...defaultCookies.id_token, secure: false },\n });\n\n expect(result.cookies.user).toEqual({\n httpOnly: false,\n secure: false,\n sameSite: \"strict\",\n path: \"/\",\n maxAge: 3600,\n });\n });\n\n it(\"should set secure to true in the default cookie configs if NODE_ENV is not development\", async () => {\n process.env = { ...originalEnv, NODE_ENV: \"production\" };\n // re-import to get the configs with the updated NODE_ENV\n const configs = await import(\"../../../src/nextjs/config.js\");\n const result = configs.resolveAuthConfig({ clientId: \"clientId\" });\n Object.values(result.cookies.tokens).forEach((cookie) => {\n expect(cookie.secure).toBe(true);\n });\n });\n });\n\n describe(\"createCivicAuthPlugin\", () => {\n it(\"should return a function\", () => {\n const plugin = createCivicAuthPlugin({ clientId: \"clientId\" });\n expect(typeof plugin).toBe(\"function\");\n });\n\n it(\"should set environment variables based on resolved config\", () => {\n const config = {\n clientId: \"clientId\",\n callbackUrl: \"/custom/callback\",\n loginUrl: \"/custom/login\",\n logoutUrl: \"/custom/logout\",\n logoutCallbackUrl: \"/custom/logoutcallback\",\n include: [\"/protected/*\"],\n exclude: [\"/public/*\"],\n cookies: {\n tokens: defaultCookies,\n user: {\n secure: false,\n sameSite: \"strict\",\n maxAge: 3600,\n } as const,\n },\n };\n\n const plugin = createCivicAuthPlugin(config);\n const nextConfig = plugin({});\n\n expect(nextConfig.env).toEqual({\n _civic_auth_client_id: \"clientId\",\n _civic_auth_callback_url: \"/custom/callback\",\n _civic_auth_challenge_url: \"/api/auth/challenge\",\n _civic_auth_login_url: \"/custom/login\",\n _civic_auth_logout_url: \"/custom/logout\",\n _civic_auth_logout_callback_url: \"/custom/logoutcallback\",\n _civic_auth_includes: \"/protected/*\",\n _civic_auth_excludes: \"/public/*\",\n _civic_oauth_server: DEFAULT_AUTH_SERVER,\n _civic_auth_cookie_config: JSON.stringify({\n tokens: defaultCookies,\n user: {\n secure: false,\n httpOnly: false,\n sameSite: \"strict\",\n path: \"/\",\n maxAge: 3600,\n },\n }),\n });\n });\n\n it(\"should merge with existing Next.js config\", () => {\n const existingConfig = {\n reactStrictMode: true,\n env: {\n CUSTOM_VAR: \"value\",\n },\n };\n\n const plugin = createCivicAuthPlugin({ clientId: \"clientId\" });\n const nextConfig = plugin(existingConfig);\n\n expect(nextConfig.reactStrictMode).toBe(true);\n expect(nextConfig.env).toHaveProperty(\"CUSTOM_VAR\", \"value\");\n expect(nextConfig.env).toHaveProperty(\"_civic_auth_callback_url\");\n });\n });\n});\n"]}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh.test.d.ts","sourceRoot":"","sources":["../../../../test/unit/server/refresh.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,55 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { refreshTokens } from "@/server/refresh.js";
3
+ import { GenericStandaloneAuthenticationRefresher } from "@/shared/lib/GenericStandaloneAuthenticationRefresher.js";
4
+ import { DEFAULT_AUTH_SERVER } from "@/constants.js";
5
+ // Mock the GenericStandaloneAuthenticationRefresher
6
+ vi.mock("@/shared/lib/GenericStandaloneAuthenticationRefresher.js", () => ({
7
+ GenericStandaloneAuthenticationRefresher: {
8
+ build: vi.fn(),
9
+ },
10
+ }));
11
+ describe("refresh.ts", () => {
12
+ const mockStorage = {
13
+ get: vi.fn(),
14
+ set: vi.fn(),
15
+ removeItem: vi.fn(),
16
+ };
17
+ const mockConfig = {
18
+ clientId: "test-client-id",
19
+ clientSecret: "test-client-secret",
20
+ redirectUrl: "http://localhost:3000/redirect",
21
+ };
22
+ const mockRefreshResponse = {
23
+ access_token: "new-access-token",
24
+ refresh_token: "new-refresh-token",
25
+ expires_in: 3600,
26
+ };
27
+ const mockRefresher = {
28
+ refreshTokens: vi.fn().mockResolvedValue(mockRefreshResponse),
29
+ setupAutorefresh: vi.fn(),
30
+ };
31
+ beforeEach(() => {
32
+ vi.clearAllMocks();
33
+ GenericStandaloneAuthenticationRefresher.build.mockResolvedValue(mockRefresher);
34
+ });
35
+ describe("refreshTokens", () => {
36
+ it("should create refresher with correct config and call refreshTokens", async () => {
37
+ const result = await refreshTokens(mockStorage, mockConfig);
38
+ expect(GenericStandaloneAuthenticationRefresher.build).toHaveBeenCalledWith({
39
+ ...mockConfig,
40
+ oauthServer: DEFAULT_AUTH_SERVER,
41
+ }, mockStorage, undefined);
42
+ expect(mockRefresher.refreshTokens).toHaveBeenCalled();
43
+ expect(result).toEqual(mockRefreshResponse);
44
+ });
45
+ it("should use custom oauthServer when provided", async () => {
46
+ const customConfig = {
47
+ ...mockConfig,
48
+ oauthServer: "https://custom-server.com",
49
+ };
50
+ await refreshTokens(mockStorage, customConfig);
51
+ expect(GenericStandaloneAuthenticationRefresher.build).toHaveBeenCalledWith(customConfig, mockStorage, undefined);
52
+ });
53
+ });
54
+ });
55
+ //# sourceMappingURL=refresh.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh.test.js","sourceRoot":"","sources":["../../../../test/unit/server/refresh.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,wCAAwC,EAAE,MAAM,0DAA0D,CAAC;AACpH,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAErD,oDAAoD;AACpD,EAAE,CAAC,IAAI,CAAC,0DAA0D,EAAE,GAAG,EAAE,CAAC,CAAC;IACzE,wCAAwC,EAAE;QACxC,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE;KACf;CACF,CAAC,CAAC,CAAC;AAEJ,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,MAAM,WAAW,GAAG;QAClB,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE;QACZ,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE;QACZ,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE;KACpB,CAAC;IAEF,MAAM,UAAU,GAAG;QACjB,QAAQ,EAAE,gBAAgB;QAC1B,YAAY,EAAE,oBAAoB;QAClC,WAAW,EAAE,gCAAgC;KAC9C,CAAC;IAEF,MAAM,mBAAmB,GAAG;QAC1B,YAAY,EAAE,kBAAkB;QAChC,aAAa,EAAE,mBAAmB;QAClC,UAAU,EAAE,IAAI;KACjB,CAAC;IAEF,MAAM,aAAa,GAAG;QACpB,aAAa,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,mBAAmB,CAAC;QAC7D,gBAAgB,EAAE,EAAE,CAAC,EAAE,EAAE;KAC1B,CAAC;IAEF,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,aAAa,EAAE,CAAC;QAClB,wCAAwC,CAAC,KAAa,CAAC,iBAAiB,CACvE,aAAa,CACd,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;YAClF,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;YAE5D,MAAM,CACJ,wCAAwC,CAAC,KAAK,CAC/C,CAAC,oBAAoB,CACpB;gBACE,GAAG,UAAU;gBACb,WAAW,EAAE,mBAAmB;aACjC,EACD,WAAW,EACX,SAAS,CACV,CAAC;YACF,MAAM,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,gBAAgB,EAAE,CAAC;YACvD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;YAC3D,MAAM,YAAY,GAAG;gBACnB,GAAG,UAAU;gBACb,WAAW,EAAE,2BAA2B;aACzC,CAAC;YAEF,MAAM,aAAa,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;YAE/C,MAAM,CACJ,wCAAwC,CAAC,KAAK,CAC/C,CAAC,oBAAoB,CAAC,YAAY,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, it, expect, vi, beforeEach } from \"vitest\";\nimport { refreshTokens } from \"@/server/refresh.js\";\nimport { GenericStandaloneAuthenticationRefresher } from \"@/shared/lib/GenericStandaloneAuthenticationRefresher.js\";\nimport { DEFAULT_AUTH_SERVER } from \"@/constants.js\";\n\n// Mock the GenericStandaloneAuthenticationRefresher\nvi.mock(\"@/shared/lib/GenericStandaloneAuthenticationRefresher.js\", () => ({\n GenericStandaloneAuthenticationRefresher: {\n build: vi.fn(),\n },\n}));\n\ndescribe(\"refresh.ts\", () => {\n const mockStorage = {\n get: vi.fn(),\n set: vi.fn(),\n removeItem: vi.fn(),\n };\n\n const mockConfig = {\n clientId: \"test-client-id\",\n clientSecret: \"test-client-secret\",\n redirectUrl: \"http://localhost:3000/redirect\",\n };\n\n const mockRefreshResponse = {\n access_token: \"new-access-token\",\n refresh_token: \"new-refresh-token\",\n expires_in: 3600,\n };\n\n const mockRefresher = {\n refreshTokens: vi.fn().mockResolvedValue(mockRefreshResponse),\n setupAutorefresh: vi.fn(),\n };\n\n beforeEach(() => {\n vi.clearAllMocks();\n (GenericStandaloneAuthenticationRefresher.build as any).mockResolvedValue(\n mockRefresher,\n );\n });\n\n describe(\"refreshTokens\", () => {\n it(\"should create refresher with correct config and call refreshTokens\", async () => {\n const result = await refreshTokens(mockStorage, mockConfig);\n\n expect(\n GenericStandaloneAuthenticationRefresher.build,\n ).toHaveBeenCalledWith(\n {\n ...mockConfig,\n oauthServer: DEFAULT_AUTH_SERVER,\n },\n mockStorage,\n undefined,\n );\n expect(mockRefresher.refreshTokens).toHaveBeenCalled();\n expect(result).toEqual(mockRefreshResponse);\n });\n\n it(\"should use custom oauthServer when provided\", async () => {\n const customConfig = {\n ...mockConfig,\n oauthServer: \"https://custom-server.com\",\n };\n\n await refreshTokens(mockStorage, customConfig);\n\n expect(\n GenericStandaloneAuthenticationRefresher.build,\n ).toHaveBeenCalledWith(customConfig, mockStorage, undefined);\n });\n });\n});\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=GenericStandaloneAuthenticationRefresher.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GenericStandaloneAuthenticationRefresher.test.d.ts","sourceRoot":"","sources":["../../../../test/unit/shared/GenericStandaloneAuthenticationRefresher.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,144 @@
1
+ import { describe, it, expect, vi, beforeEach, } from "vitest";
2
+ import { getEndpointsWithOverrides, retrieveTokens, storeTokens, } from "@/shared/lib/util.js";
3
+ import { OAuth2Client } from "oslo/oauth2";
4
+ import { GenericStandaloneAuthenticationRefresher } from "@/shared/lib/GenericStandaloneAuthenticationRefresher.js";
5
+ vi.mock("@/shared/lib/util.js");
6
+ vi.mock("oslo/oauth2");
7
+ describe("GenericStandaloneAuthenticationRefresher", () => {
8
+ const mockAuthConfig = {
9
+ clientId: "mockClientId",
10
+ oauthServer: "http://mockOauthServer",
11
+ redirectUrl: "http://localhost/redirect",
12
+ };
13
+ const mockEndpoints = {
14
+ auth: "http://mockAuthEndpoint",
15
+ token: "http://mockTokenEndpoint",
16
+ jwks: "http://mockJwksEndpoint",
17
+ userinfo: "http://mockUserinfoEndpoint",
18
+ endsession: "http://mockEndsessionEndpoint",
19
+ };
20
+ const mockTokens = {
21
+ id_token: "mockIdToken",
22
+ access_token: "mockAccessToken",
23
+ refresh_token: "mockRefreshToken",
24
+ };
25
+ const mockStorage = {};
26
+ let refresher;
27
+ beforeEach(async () => {
28
+ vi.clearAllMocks();
29
+ vi.mocked(getEndpointsWithOverrides).mockResolvedValue(mockEndpoints);
30
+ refresher = await GenericStandaloneAuthenticationRefresher.build(mockAuthConfig, mockStorage);
31
+ });
32
+ describe("init", () => {
33
+ it("should initialize OAuth2 client with resolved endpoints", async () => {
34
+ expect(getEndpointsWithOverrides).toHaveBeenCalledWith(mockAuthConfig.oauthServer, undefined);
35
+ expect(refresher["endpoints"]).toEqual(mockEndpoints);
36
+ expect(refresher["oauth2client"]).toBeInstanceOf(OAuth2Client);
37
+ });
38
+ });
39
+ describe("refreshTokens", () => {
40
+ it("should refresh tokens and store them", async () => {
41
+ vi.mocked(retrieveTokens).mockResolvedValue(mockTokens);
42
+ const refreshedTokens = {
43
+ id_token: "newIdToken",
44
+ access_token: "newAccessToken",
45
+ refresh_token: "newRefreshToken",
46
+ };
47
+ vi.mocked(OAuth2Client.prototype.refreshAccessToken).mockResolvedValue(refreshedTokens);
48
+ const result = await refresher.refreshTokens();
49
+ expect(refresher["oauth2client"]?.refreshAccessToken).toHaveBeenCalledWith(mockTokens.refresh_token);
50
+ expect(storeTokens).toHaveBeenCalledWith(mockStorage, refreshedTokens);
51
+ expect(result).toEqual(refreshedTokens);
52
+ });
53
+ it("should throw an error if no refresh token is available", async () => {
54
+ vi.mocked(retrieveTokens).mockResolvedValue({
55
+ ...mockTokens,
56
+ refresh_token: undefined,
57
+ });
58
+ await expect(refresher.refreshTokens()).rejects.toThrow("No refresh token available");
59
+ });
60
+ });
61
+ describe("auto refresh", () => {
62
+ let setTimeoutSpy;
63
+ let clearTimeoutSpy;
64
+ beforeEach(() => {
65
+ vi.clearAllMocks(); // Ensure all mocks are reset
66
+ vi.useFakeTimers();
67
+ setTimeoutSpy = vi.spyOn(global, "setTimeout");
68
+ clearTimeoutSpy = vi.spyOn(global, "clearTimeout");
69
+ });
70
+ afterEach(() => {
71
+ vi.useRealTimers();
72
+ setTimeoutSpy.mockRestore();
73
+ clearTimeoutSpy.mockRestore();
74
+ vi.clearAllTimers(); // Clear any pending timers
75
+ });
76
+ it("should setup auto refresh timer based on token expiry", async () => {
77
+ const tokensWithExpiry = {
78
+ ...mockTokens,
79
+ expires_in: 3600, // 1 hour
80
+ };
81
+ vi.mocked(retrieveTokens).mockResolvedValue(tokensWithExpiry);
82
+ await refresher.setupAutorefresh();
83
+ // Should set timeout for (3600 - 30) seconds
84
+ expect(setTimeout).toHaveBeenCalledTimes(1);
85
+ expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 3570000);
86
+ });
87
+ it("should clear existing timeout when setting up new refresh", async () => {
88
+ const tokensWithExpiry = {
89
+ ...mockTokens,
90
+ expires_in: 3600,
91
+ };
92
+ vi.mocked(retrieveTokens).mockResolvedValue(tokensWithExpiry);
93
+ // Setup initial timeout
94
+ await refresher.setupAutorefresh();
95
+ // Setup another timeout
96
+ await refresher.setupAutorefresh();
97
+ expect(clearTimeout).toHaveBeenCalled();
98
+ });
99
+ it("should refresh tokens and setup new timer when timeout triggers", async () => {
100
+ const tokensWithExpiry = {
101
+ ...mockTokens,
102
+ expires_in: 3600,
103
+ };
104
+ const refreshedTokens = {
105
+ ...mockTokens,
106
+ id_token: "newIdToken",
107
+ access_token: "newAccessToken",
108
+ refresh_token: "newRefreshToken",
109
+ expires_in: 3600,
110
+ };
111
+ // Reset mock call history
112
+ vi.mocked(retrieveTokens).mockReset();
113
+ vi.mocked(OAuth2Client.prototype.refreshAccessToken).mockReset();
114
+ vi.mocked(retrieveTokens)
115
+ .mockResolvedValueOnce(tokensWithExpiry) // First call for initial setup
116
+ .mockResolvedValueOnce(tokensWithExpiry) // Second call in refreshTokens
117
+ .mockResolvedValueOnce(refreshedTokens); // Third call for next setup
118
+ vi.mocked(OAuth2Client.prototype.refreshAccessToken).mockResolvedValue(refreshedTokens);
119
+ await refresher.setupAutorefresh();
120
+ // Fast-forward time to trigger the refresh
121
+ await vi.advanceTimersByTimeAsync(3570000); // Use async version
122
+ expect(OAuth2Client.prototype.refreshAccessToken).toHaveBeenCalled();
123
+ expect(storeTokens).toHaveBeenCalledWith(mockStorage, refreshedTokens);
124
+ expect(setTimeout).toHaveBeenCalledTimes(2);
125
+ });
126
+ it("should handle refresh failures gracefully", async () => {
127
+ const tokensWithExpiry = {
128
+ ...mockTokens,
129
+ expires_in: 3600,
130
+ };
131
+ vi.mocked(retrieveTokens).mockResolvedValue(tokensWithExpiry);
132
+ vi.mocked(OAuth2Client.prototype.refreshAccessToken).mockRejectedValue(new Error("Refresh failed"));
133
+ const consoleSpy = vi
134
+ .spyOn(console, "error")
135
+ .mockImplementation(() => { });
136
+ await refresher.setupAutorefresh();
137
+ // Fast-forward time to trigger the refresh
138
+ vi.advanceTimersByTime(3570000);
139
+ await vi.runAllTimersAsync();
140
+ expect(consoleSpy).toHaveBeenCalledWith("Failed to refresh tokens:", expect.any(Error));
141
+ });
142
+ });
143
+ });
144
+ //# sourceMappingURL=GenericStandaloneAuthenticationRefresher.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GenericStandaloneAuthenticationRefresher.test.js","sourceRoot":"","sources":["../../../../test/unit/shared/GenericStandaloneAuthenticationRefresher.test.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n describe,\n it,\n expect,\n vi,\n beforeEach,\n type MockInstance,\n} from \"vitest\";\nimport {\n getEndpointsWithOverrides,\n retrieveTokens,\n storeTokens,\n} from \"@/shared/lib/util.js\";\nimport type { AuthStorage, Endpoints, OIDCTokenResponseBody } from \"@/types.js\";\nimport { OAuth2Client } from \"oslo/oauth2\";\nimport type { AuthConfig } from \"@/server/config.ts\";\nimport { GenericStandaloneAuthenticationRefresher } from \"@/shared/lib/GenericStandaloneAuthenticationRefresher.js\";\n\nvi.mock(\"@/shared/lib/util.js\");\nvi.mock(\"oslo/oauth2\");\n\ndescribe(\"GenericStandaloneAuthenticationRefresher\", () => {\n const mockAuthConfig: AuthConfig = {\n clientId: \"mockClientId\",\n oauthServer: \"http://mockOauthServer\",\n redirectUrl: \"http://localhost/redirect\",\n };\n const mockEndpoints: Endpoints = {\n auth: \"http://mockAuthEndpoint\",\n token: \"http://mockTokenEndpoint\",\n jwks: \"http://mockJwksEndpoint\",\n userinfo: \"http://mockUserinfoEndpoint\",\n endsession: \"http://mockEndsessionEndpoint\",\n };\n const mockTokens: OIDCTokenResponseBody = {\n id_token: \"mockIdToken\",\n access_token: \"mockAccessToken\",\n refresh_token: \"mockRefreshToken\",\n };\n const mockStorage = {} as AuthStorage;\n\n let refresher: GenericStandaloneAuthenticationRefresher;\n\n beforeEach(async () => {\n vi.clearAllMocks();\n vi.mocked(getEndpointsWithOverrides).mockResolvedValue(mockEndpoints);\n refresher = await GenericStandaloneAuthenticationRefresher.build(\n mockAuthConfig,\n mockStorage,\n );\n });\n\n describe(\"init\", () => {\n it(\"should initialize OAuth2 client with resolved endpoints\", async () => {\n expect(getEndpointsWithOverrides).toHaveBeenCalledWith(\n mockAuthConfig.oauthServer,\n undefined,\n );\n expect(refresher[\"endpoints\"]).toEqual(mockEndpoints);\n expect(refresher[\"oauth2client\"]).toBeInstanceOf(OAuth2Client);\n });\n });\n\n describe(\"refreshTokens\", () => {\n it(\"should refresh tokens and store them\", async () => {\n vi.mocked(retrieveTokens).mockResolvedValue(mockTokens);\n const refreshedTokens: OIDCTokenResponseBody = {\n id_token: \"newIdToken\",\n access_token: \"newAccessToken\",\n refresh_token: \"newRefreshToken\",\n };\n\n vi.mocked(OAuth2Client.prototype.refreshAccessToken).mockResolvedValue(\n refreshedTokens,\n );\n\n const result = await refresher.refreshTokens();\n\n expect(\n refresher[\"oauth2client\"]?.refreshAccessToken,\n ).toHaveBeenCalledWith(mockTokens.refresh_token);\n expect(storeTokens).toHaveBeenCalledWith(mockStorage, refreshedTokens);\n expect(result).toEqual(refreshedTokens);\n });\n\n it(\"should throw an error if no refresh token is available\", async () => {\n vi.mocked(retrieveTokens).mockResolvedValue({\n ...mockTokens,\n refresh_token: undefined,\n });\n\n await expect(refresher.refreshTokens()).rejects.toThrow(\n \"No refresh token available\",\n );\n });\n });\n\n describe(\"auto refresh\", () => {\n let setTimeoutSpy: MockInstance<typeof setTimeout>;\n let clearTimeoutSpy: MockInstance<typeof clearTimeout>;\n\n beforeEach(() => {\n vi.clearAllMocks(); // Ensure all mocks are reset\n vi.useFakeTimers();\n setTimeoutSpy = vi.spyOn(global, \"setTimeout\");\n clearTimeoutSpy = vi.spyOn(global, \"clearTimeout\");\n });\n\n afterEach(() => {\n vi.useRealTimers();\n setTimeoutSpy.mockRestore();\n clearTimeoutSpy.mockRestore();\n vi.clearAllTimers(); // Clear any pending timers\n });\n\n it(\"should setup auto refresh timer based on token expiry\", async () => {\n const tokensWithExpiry = {\n ...mockTokens,\n expires_in: 3600, // 1 hour\n };\n vi.mocked(retrieveTokens).mockResolvedValue(tokensWithExpiry);\n\n await refresher.setupAutorefresh();\n\n // Should set timeout for (3600 - 30) seconds\n expect(setTimeout).toHaveBeenCalledTimes(1);\n expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 3570000);\n });\n\n it(\"should clear existing timeout when setting up new refresh\", async () => {\n const tokensWithExpiry = {\n ...mockTokens,\n expires_in: 3600,\n };\n vi.mocked(retrieveTokens).mockResolvedValue(tokensWithExpiry);\n\n // Setup initial timeout\n await refresher.setupAutorefresh();\n\n // Setup another timeout\n await refresher.setupAutorefresh();\n\n expect(clearTimeout).toHaveBeenCalled();\n });\n\n it(\"should refresh tokens and setup new timer when timeout triggers\", async () => {\n const tokensWithExpiry = {\n ...mockTokens,\n expires_in: 3600,\n };\n const refreshedTokens = {\n ...mockTokens,\n id_token: \"newIdToken\",\n access_token: \"newAccessToken\",\n refresh_token: \"newRefreshToken\",\n expires_in: 3600,\n };\n\n // Reset mock call history\n vi.mocked(retrieveTokens).mockReset();\n vi.mocked(OAuth2Client.prototype.refreshAccessToken).mockReset();\n\n vi.mocked(retrieveTokens)\n .mockResolvedValueOnce(tokensWithExpiry) // First call for initial setup\n .mockResolvedValueOnce(tokensWithExpiry) // Second call in refreshTokens\n .mockResolvedValueOnce(refreshedTokens); // Third call for next setup\n\n vi.mocked(OAuth2Client.prototype.refreshAccessToken).mockResolvedValue(\n refreshedTokens,\n );\n\n await refresher.setupAutorefresh();\n\n // Fast-forward time to trigger the refresh\n await vi.advanceTimersByTimeAsync(3570000); // Use async version\n\n expect(OAuth2Client.prototype.refreshAccessToken).toHaveBeenCalled();\n expect(storeTokens).toHaveBeenCalledWith(mockStorage, refreshedTokens);\n expect(setTimeout).toHaveBeenCalledTimes(2);\n });\n\n it(\"should handle refresh failures gracefully\", async () => {\n const tokensWithExpiry = {\n ...mockTokens,\n expires_in: 3600,\n };\n\n vi.mocked(retrieveTokens).mockResolvedValue(tokensWithExpiry);\n vi.mocked(OAuth2Client.prototype.refreshAccessToken).mockRejectedValue(\n new Error(\"Refresh failed\"),\n );\n\n const consoleSpy = vi\n .spyOn(console, \"error\")\n .mockImplementation(() => {});\n\n await refresher.setupAutorefresh();\n\n // Fast-forward time to trigger the refresh\n vi.advanceTimersByTime(3570000);\n await vi.runAllTimersAsync();\n\n expect(consoleSpy).toHaveBeenCalledWith(\n \"Failed to refresh tokens:\",\n expect.any(Error),\n );\n });\n });\n});\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=GenericStandaloneAuthenticationRefresher.text.d.ts.map