jazz-browser-auth-clerk 0.8.14 → 0.8.16

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.
package/dist/index.js CHANGED
@@ -1,10 +1,41 @@
1
+ const localStorageKey = "jazz-clerk-auth";
2
+ function saveCredentialsToLocalStorage(credentials) {
3
+ localStorage.setItem(localStorageKey, JSON.stringify({
4
+ accountID: credentials.accountID,
5
+ secret: credentials.secret,
6
+ }));
7
+ }
1
8
  export class BrowserClerkAuth {
2
9
  constructor(driver, clerkClient) {
3
10
  this.driver = driver;
4
11
  this.clerkClient = clerkClient;
5
12
  }
6
13
  async start() {
14
+ // Check local storage for credentials
15
+ const locallyStoredCredentials = localStorage.getItem(localStorageKey);
16
+ if (locallyStoredCredentials) {
17
+ try {
18
+ const credentials = JSON.parse(locallyStoredCredentials);
19
+ return {
20
+ type: "existing",
21
+ credentials,
22
+ saveCredentials: async () => { }, // No need to save credentials when recovering from local storage
23
+ onSuccess: () => { },
24
+ onError: (error) => {
25
+ this.driver.onError(error);
26
+ },
27
+ logOut: () => {
28
+ localStorage.removeItem(localStorageKey);
29
+ void this.clerkClient.signOut();
30
+ },
31
+ };
32
+ }
33
+ catch (e) {
34
+ console.error("Error parsing local storage credentials", e);
35
+ }
36
+ }
7
37
  if (this.clerkClient.user) {
38
+ // Check clerk user metadata for credentials
8
39
  const storedCredentials = this.clerkClient.user.unsafeMetadata;
9
40
  if (storedCredentials.jazzAccountID) {
10
41
  if (!storedCredentials.jazzAccountSecret) {
@@ -16,6 +47,12 @@ export class BrowserClerkAuth {
16
47
  accountID: storedCredentials.jazzAccountID,
17
48
  secret: storedCredentials.jazzAccountSecret,
18
49
  },
50
+ saveCredentials: async ({ accountID, secret }) => {
51
+ saveCredentialsToLocalStorage({
52
+ accountID,
53
+ secret,
54
+ });
55
+ },
19
56
  onSuccess: () => { },
20
57
  onError: (error) => {
21
58
  this.driver.onError(error);
@@ -26,6 +63,7 @@ export class BrowserClerkAuth {
26
63
  };
27
64
  }
28
65
  else {
66
+ // No credentials found, so we need to create new credentials
29
67
  return {
30
68
  type: "new",
31
69
  creationProps: {
@@ -33,11 +71,15 @@ export class BrowserClerkAuth {
33
71
  this.clerkClient.user.username ||
34
72
  this.clerkClient.user.id,
35
73
  },
36
- saveCredentials: async (credentials) => {
74
+ saveCredentials: async ({ accountID, secret }) => {
75
+ saveCredentialsToLocalStorage({
76
+ accountID,
77
+ secret,
78
+ });
37
79
  await this.clerkClient.user?.update({
38
80
  unsafeMetadata: {
39
- jazzAccountID: credentials.accountID,
40
- jazzAccountSecret: credentials.secret,
81
+ jazzAccountID: accountID,
82
+ jazzAccountSecret: secret,
41
83
  },
42
84
  });
43
85
  },
@@ -52,6 +94,7 @@ export class BrowserClerkAuth {
52
94
  }
53
95
  }
54
96
  else {
97
+ // Clerk user not found, so we can't authenticate
55
98
  throw new Error("Not signed in");
56
99
  }
57
100
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAkBA,MAAM,OAAO,gBAAgB;IACzB,YACW,MAA+B,EACrB,WAA+B;QADzC,WAAM,GAAN,MAAM,CAAyB;QACrB,gBAAW,GAAX,WAAW,CAAoB;IACjD,CAAC;IAEJ,KAAK,CAAC,KAAK;QACP,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;YACxB,MAAM,iBAAiB,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,cAAc,CAAC;YAC/D,IAAI,iBAAiB,CAAC,aAAa,EAAE,CAAC;gBAClC,IAAI,CAAC,iBAAiB,CAAC,iBAAiB,EAAE,CAAC;oBACvC,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;gBACnD,CAAC;gBACD,OAAO;oBACH,IAAI,EAAE,UAAU;oBAChB,WAAW,EAAE;wBACT,SAAS,EACL,iBAAiB,CAAC,aAA4B;wBAClD,MAAM,EAAE,iBAAiB,CAAC,iBAAgC;qBAC7D;oBACD,SAAS,EAAE,GAAG,EAAE,GAAE,CAAC;oBACnB,OAAO,EAAE,CAAC,KAAqB,EAAE,EAAE;wBAC/B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;oBAC/B,CAAC;oBACD,MAAM,EAAE,GAAG,EAAE;wBACT,KAAK,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;oBACpC,CAAC;iBACJ,CAAC;YACN,CAAC;iBAAM,CAAC;gBACJ,OAAO;oBACH,IAAI,EAAE,KAAK;oBACX,aAAa,EAAE;wBACX,IAAI,EACA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ;4BAC9B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ;4BAC9B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;qBAC/B;oBACD,eAAe,EAAE,KAAK,EAAE,WAGvB,EAAE,EAAE;wBACD,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC;4BAChC,cAAc,EAAE;gCACZ,aAAa,EAAE,WAAW,CAAC,SAAS;gCACpC,iBAAiB,EAAE,WAAW,CAAC,MAAM;6BACxC;yBACJ,CAAC,CAAC;oBACP,CAAC;oBACD,SAAS,EAAE,GAAG,EAAE,GAAE,CAAC;oBACnB,OAAO,EAAE,CAAC,KAAqB,EAAE,EAAE;wBAC/B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;oBAC/B,CAAC;oBACD,MAAM,EAAE,GAAG,EAAE;wBACT,KAAK,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;oBACpC,CAAC;iBACJ,CAAC;YACN,CAAC;QACL,CAAC;aAAM,CAAC;YACJ,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;QACrC,CAAC;IACL,CAAC;CACJ"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,102 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { BrowserClerkAuth } from "../index.js";
3
+ describe("BrowserClerkAuth", () => {
4
+ let mockLocalStorage;
5
+ let mockClerkClient;
6
+ let mockDriver;
7
+ beforeEach(() => {
8
+ // Mock localStorage
9
+ mockLocalStorage = {};
10
+ global.localStorage = {
11
+ getItem: vi.fn((key) => mockLocalStorage[key] || null),
12
+ setItem: vi.fn((key, value) => {
13
+ mockLocalStorage[key] = value;
14
+ }),
15
+ removeItem: vi.fn((key) => {
16
+ delete mockLocalStorage[key];
17
+ }),
18
+ clear: vi.fn(),
19
+ length: 0,
20
+ key: vi.fn(),
21
+ };
22
+ // Mock Clerk client
23
+ mockClerkClient = {
24
+ user: {
25
+ unsafeMetadata: {},
26
+ fullName: "Test User",
27
+ username: "testuser",
28
+ id: "test-id",
29
+ update: vi.fn(),
30
+ },
31
+ signOut: vi.fn(),
32
+ };
33
+ // Mock driver
34
+ mockDriver = {
35
+ onError: vi.fn(),
36
+ };
37
+ });
38
+ describe("clerk credentials in localStorage", () => {
39
+ it("should get credentials from localStorage when clerk user is not signed in", async () => {
40
+ mockLocalStorage["jazz-clerk-auth"] = JSON.stringify({
41
+ accountID: "test-account-id",
42
+ accountSecret: "test-secret",
43
+ });
44
+ const auth = new BrowserClerkAuth(mockDriver, {
45
+ ...mockClerkClient,
46
+ user: null,
47
+ });
48
+ const result = await auth.start();
49
+ expect(result.type).toBe("existing");
50
+ });
51
+ });
52
+ describe("clerk credentials not in localStorage", () => {
53
+ it("should return new credentials when clerk user signs up", async () => {
54
+ const auth = new BrowserClerkAuth(mockDriver, mockClerkClient);
55
+ const result = await auth.start();
56
+ expect(result.type).toBe("new");
57
+ });
58
+ it("should return existing credentials when clerk user is signed in", async () => {
59
+ mockClerkClient = {
60
+ user: {
61
+ unsafeMetadata: {
62
+ jazzAccountID: "test-account-id",
63
+ jazzAccountSecret: "test-secret",
64
+ },
65
+ fullName: "Test User",
66
+ username: "testuser",
67
+ id: "test-id",
68
+ update: vi.fn(),
69
+ },
70
+ signOut: vi.fn(),
71
+ };
72
+ const auth = new BrowserClerkAuth(mockDriver, mockClerkClient);
73
+ const result = await auth.start();
74
+ expect(result.type).toBe("existing");
75
+ });
76
+ it("should throw error when not signed in", async () => {
77
+ const auth = new BrowserClerkAuth(mockDriver, {
78
+ ...mockClerkClient,
79
+ user: null,
80
+ });
81
+ await expect(auth.start()).rejects.toThrow("Not signed in");
82
+ });
83
+ it("should save credentials to localStorage", async () => {
84
+ const auth = new BrowserClerkAuth(mockDriver, mockClerkClient);
85
+ const result = await auth.start();
86
+ if (result.saveCredentials) {
87
+ await result.saveCredentials({
88
+ accountID: "test-account-id",
89
+ secret: "test-secret",
90
+ });
91
+ }
92
+ expect(mockLocalStorage["jazz-clerk-auth"]).toBeDefined();
93
+ });
94
+ it("should call clerk signOut when logging out", async () => {
95
+ const auth = new BrowserClerkAuth(mockDriver, mockClerkClient);
96
+ const result = await auth.start();
97
+ result.logOut();
98
+ expect(mockClerkClient.signOut).toHaveBeenCalled();
99
+ });
100
+ });
101
+ });
102
+ //# sourceMappingURL=BrowserClerkAuth.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BrowserClerkAuth.test.js","sourceRoot":"","sources":["../../src/tests/BrowserClerkAuth.test.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAsB,MAAM,aAAa,CAAC;AAEnE,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,IAAI,gBAA2C,CAAC;IAChD,IAAI,eAAmC,CAAC;IACxC,IAAI,UAAmC,CAAC;IAExC,UAAU,CAAC,GAAG,EAAE;QACd,oBAAoB;QACpB,gBAAgB,GAAG,EAAE,CAAC;QACtB,MAAM,CAAC,YAAY,GAAG;YACpB,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,GAAW,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;YAC9D,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,GAAW,EAAE,KAAa,EAAE,EAAE;gBAC5C,gBAAgB,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YAChC,CAAC,CAAC;YACF,UAAU,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,GAAW,EAAE,EAAE;gBAChC,OAAO,gBAAgB,CAAC,GAAG,CAAC,CAAC;YAC/B,CAAC,CAAC;YACF,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE;YACd,MAAM,EAAE,CAAC;YACT,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE;SACb,CAAC;QAEF,oBAAoB;QACpB,eAAe,GAAG;YAChB,IAAI,EAAE;gBACJ,cAAc,EAAE,EAAE;gBAClB,QAAQ,EAAE,WAAW;gBACrB,QAAQ,EAAE,UAAU;gBACpB,EAAE,EAAE,SAAS;gBACb,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE;aAChB;YACD,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE;SACjB,CAAC;QAEF,cAAc;QACd,UAAU,GAAG;YACX,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE;SACjB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,mCAAmC,EAAE,GAAG,EAAE;QACjD,EAAE,CAAC,2EAA2E,EAAE,KAAK,IAAI,EAAE;YACzF,gBAAgB,CAAC,iBAAiB,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC;gBACnD,SAAS,EAAE,iBAAiB;gBAC5B,aAAa,EAAE,aAAa;aAC7B,CAAC,CAAC;YAEH,MAAM,IAAI,GAAG,IAAI,gBAAgB,CAAC,UAAU,EAAE;gBAC5C,GAAG,eAAe;gBAClB,IAAI,EAAE,IAAI;aACX,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,uCAAuC,EAAE,GAAG,EAAE;QACrD,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;YACtE,MAAM,IAAI,GAAG,IAAI,gBAAgB,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;YAC/D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;YAC/E,eAAe,GAAG;gBAChB,IAAI,EAAE;oBACJ,cAAc,EAAE;wBACd,aAAa,EAAE,iBAAiB;wBAChC,iBAAiB,EAAE,aAAa;qBACjC;oBACD,QAAQ,EAAE,WAAW;oBACrB,QAAQ,EAAE,UAAU;oBACpB,EAAE,EAAE,SAAS;oBACb,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE;iBAChB;gBACD,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE;aACjB,CAAC;YAEF,MAAM,IAAI,GAAG,IAAI,gBAAgB,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;YAC/D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;YACrD,MAAM,IAAI,GAAG,IAAI,gBAAgB,CAAC,UAAU,EAAE;gBAC5C,GAAG,eAAe;gBAClB,IAAI,EAAE,IAAI;aACX,CAAC,CAAC;YAEH,MAAM,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;YACvD,MAAM,IAAI,GAAG,IAAI,gBAAgB,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;YAC/D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;YAClC,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;gBAC3B,MAAM,MAAM,CAAC,eAAe,CAAC;oBAC3B,SAAS,EAAE,iBAAgC;oBAC3C,MAAM,EAAE,aAA4B;iBACrC,CAAC,CAAC;YACL,CAAC;YAED,MAAM,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAC5D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;YAC1D,MAAM,IAAI,GAAG,IAAI,gBAAgB,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;YAC/D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;YAClC,MAAM,CAAC,MAAM,EAAE,CAAC;YAEhB,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,gBAAgB,EAAE,CAAC;QACrD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,25 +1,23 @@
1
1
  {
2
2
  "name": "jazz-browser-auth-clerk",
3
- "version": "0.8.14",
3
+ "version": "0.8.16",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "src/index.ts",
7
7
  "license": "MIT",
8
- "lint-staged": {
9
- "*.{ts,tsx}": "eslint --fix",
10
- "*.{js,jsx,mdx,json}": "prettier --write"
11
- },
12
8
  "dependencies": {
13
- "cojson": "0.8.12",
14
- "jazz-browser": "0.8.14",
15
- "jazz-tools": "0.8.14"
9
+ "cojson": "0.8.16",
10
+ "jazz-browser": "0.8.16",
11
+ "jazz-tools": "0.8.16"
16
12
  },
17
13
  "devDependencies": {
18
14
  "typescript": "^5.3.3"
19
15
  },
20
16
  "scripts": {
21
- "lint": "eslint . --ext ts,tsx",
22
- "format": "prettier --write './src/**/*.{ts,tsx}'",
23
- "build": "npm run lint && rm -rf ./dist && tsc --sourceMap --outDir dist"
17
+ "format-and-lint": "biome check .",
18
+ "format-and-lint:fix": "biome check . --write",
19
+ "build": "rm -rf ./dist && tsc --sourceMap --outDir dist",
20
+ "test": "vitest --run --root ../../ --project jazz-browser-auth-clerk",
21
+ "test:watch": "vitest --watch --root ../../ --project jazz-browser-auth-clerk"
24
22
  }
25
23
  }
package/src/index.ts CHANGED
@@ -1,87 +1,135 @@
1
- import { Account, AuthMethod, AuthResult, ID } from "jazz-tools";
2
1
  import { AgentSecret } from "cojson";
2
+ import { Account, AuthMethod, AuthResult, Credentials, ID } from "jazz-tools";
3
+
4
+ const localStorageKey = "jazz-clerk-auth";
3
5
 
4
6
  export type MinimalClerkClient = {
5
- user: {
7
+ user:
8
+ | {
6
9
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
10
  unsafeMetadata: Record<string, any>;
8
11
  fullName: string | null;
9
12
  username: string | null;
10
13
  id: string;
11
14
  update: (args: {
12
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
- unsafeMetadata: Record<string, any>;
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ unsafeMetadata: Record<string, any>;
14
17
  }) => Promise<unknown>;
15
- } | null | undefined;
16
- signOut: () => Promise<void>;
18
+ }
19
+ | null
20
+ | undefined;
21
+ signOut: () => Promise<void>;
22
+ };
23
+
24
+ function saveCredentialsToLocalStorage(credentials: Credentials) {
25
+ localStorage.setItem(
26
+ localStorageKey,
27
+ JSON.stringify({
28
+ accountID: credentials.accountID,
29
+ secret: credentials.secret,
30
+ }),
31
+ );
17
32
  }
18
33
 
19
34
  export class BrowserClerkAuth implements AuthMethod {
20
- constructor(
21
- public driver: BrowserClerkAuth.Driver,
22
- private readonly clerkClient: MinimalClerkClient
23
- ) {}
35
+ constructor(
36
+ public driver: BrowserClerkAuth.Driver,
37
+ private readonly clerkClient: MinimalClerkClient,
38
+ ) {}
39
+
40
+ async start(): Promise<AuthResult> {
41
+ // Check local storage for credentials
42
+ const locallyStoredCredentials = localStorage.getItem(localStorageKey);
24
43
 
25
- async start(): Promise<AuthResult> {
26
- if (this.clerkClient.user) {
27
- const storedCredentials = this.clerkClient.user.unsafeMetadata;
28
- if (storedCredentials.jazzAccountID) {
29
- if (!storedCredentials.jazzAccountSecret) {
30
- throw new Error("No secret for existing user");
31
- }
32
- return {
33
- type: "existing",
34
- credentials: {
35
- accountID:
36
- storedCredentials.jazzAccountID as ID<Account>,
37
- secret: storedCredentials.jazzAccountSecret as AgentSecret,
38
- },
39
- onSuccess: () => {},
40
- onError: (error: string | Error) => {
41
- this.driver.onError(error);
42
- },
43
- logOut: () => {
44
- void this.clerkClient.signOut();
45
- },
46
- };
47
- } else {
48
- return {
49
- type: "new",
50
- creationProps: {
51
- name:
52
- this.clerkClient.user.fullName ||
53
- this.clerkClient.user.username ||
54
- this.clerkClient.user.id,
55
- },
56
- saveCredentials: async (credentials: {
57
- accountID: ID<Account>;
58
- secret: AgentSecret;
59
- }) => {
60
- await this.clerkClient.user?.update({
61
- unsafeMetadata: {
62
- jazzAccountID: credentials.accountID,
63
- jazzAccountSecret: credentials.secret,
64
- },
65
- });
66
- },
67
- onSuccess: () => {},
68
- onError: (error: string | Error) => {
69
- this.driver.onError(error);
70
- },
71
- logOut: () => {
72
- void this.clerkClient.signOut();
73
- },
74
- };
75
- }
76
- } else {
77
- throw new Error("Not signed in");
44
+ if (locallyStoredCredentials) {
45
+ try {
46
+ const credentials = JSON.parse(locallyStoredCredentials) as Credentials;
47
+ return {
48
+ type: "existing",
49
+ credentials,
50
+ saveCredentials: async () => {}, // No need to save credentials when recovering from local storage
51
+ onSuccess: () => {},
52
+ onError: (error: string | Error) => {
53
+ this.driver.onError(error);
54
+ },
55
+ logOut: () => {
56
+ localStorage.removeItem(localStorageKey);
57
+ void this.clerkClient.signOut();
58
+ },
59
+ };
60
+ } catch (e) {
61
+ console.error("Error parsing local storage credentials", e);
62
+ }
63
+ }
64
+
65
+ if (this.clerkClient.user) {
66
+ // Check clerk user metadata for credentials
67
+ const storedCredentials = this.clerkClient.user.unsafeMetadata;
68
+ if (storedCredentials.jazzAccountID) {
69
+ if (!storedCredentials.jazzAccountSecret) {
70
+ throw new Error("No secret for existing user");
78
71
  }
72
+ return {
73
+ type: "existing",
74
+ credentials: {
75
+ accountID: storedCredentials.jazzAccountID as ID<Account>,
76
+ secret: storedCredentials.jazzAccountSecret as AgentSecret,
77
+ },
78
+ saveCredentials: async ({ accountID, secret }: Credentials) => {
79
+ saveCredentialsToLocalStorage({
80
+ accountID,
81
+ secret,
82
+ });
83
+ },
84
+ onSuccess: () => {},
85
+ onError: (error: string | Error) => {
86
+ this.driver.onError(error);
87
+ },
88
+ logOut: () => {
89
+ void this.clerkClient.signOut();
90
+ },
91
+ };
92
+ } else {
93
+ // No credentials found, so we need to create new credentials
94
+ return {
95
+ type: "new",
96
+ creationProps: {
97
+ name:
98
+ this.clerkClient.user.fullName ||
99
+ this.clerkClient.user.username ||
100
+ this.clerkClient.user.id,
101
+ },
102
+ saveCredentials: async ({ accountID, secret }: Credentials) => {
103
+ saveCredentialsToLocalStorage({
104
+ accountID,
105
+ secret,
106
+ });
107
+ await this.clerkClient.user?.update({
108
+ unsafeMetadata: {
109
+ jazzAccountID: accountID,
110
+ jazzAccountSecret: secret,
111
+ },
112
+ });
113
+ },
114
+ onSuccess: () => {},
115
+ onError: (error: string | Error) => {
116
+ this.driver.onError(error);
117
+ },
118
+ logOut: () => {
119
+ void this.clerkClient.signOut();
120
+ },
121
+ };
122
+ }
123
+ } else {
124
+ // Clerk user not found, so we can't authenticate
125
+ throw new Error("Not signed in");
79
126
  }
127
+ }
80
128
  }
81
129
 
82
130
  // eslint-disable-next-line @typescript-eslint/no-namespace
83
131
  export namespace BrowserClerkAuth {
84
- export interface Driver {
85
- onError: (error: string | Error) => void;
86
- }
132
+ export interface Driver {
133
+ onError: (error: string | Error) => void;
134
+ }
87
135
  }
@@ -0,0 +1,119 @@
1
+ import { AgentSecret } from "cojson";
2
+ import { Account, ID } from "jazz-tools";
3
+ import { beforeEach, describe, expect, it, vi } from "vitest";
4
+ import { BrowserClerkAuth, MinimalClerkClient } from "../index.js";
5
+
6
+ describe("BrowserClerkAuth", () => {
7
+ let mockLocalStorage: { [key: string]: string };
8
+ let mockClerkClient: MinimalClerkClient;
9
+ let mockDriver: BrowserClerkAuth.Driver;
10
+
11
+ beforeEach(() => {
12
+ // Mock localStorage
13
+ mockLocalStorage = {};
14
+ global.localStorage = {
15
+ getItem: vi.fn((key: string) => mockLocalStorage[key] || null),
16
+ setItem: vi.fn((key: string, value: string) => {
17
+ mockLocalStorage[key] = value;
18
+ }),
19
+ removeItem: vi.fn((key: string) => {
20
+ delete mockLocalStorage[key];
21
+ }),
22
+ clear: vi.fn(),
23
+ length: 0,
24
+ key: vi.fn(),
25
+ };
26
+
27
+ // Mock Clerk client
28
+ mockClerkClient = {
29
+ user: {
30
+ unsafeMetadata: {},
31
+ fullName: "Test User",
32
+ username: "testuser",
33
+ id: "test-id",
34
+ update: vi.fn(),
35
+ },
36
+ signOut: vi.fn(),
37
+ };
38
+
39
+ // Mock driver
40
+ mockDriver = {
41
+ onError: vi.fn(),
42
+ };
43
+ });
44
+
45
+ describe("clerk credentials in localStorage", () => {
46
+ it("should get credentials from localStorage when clerk user is not signed in", async () => {
47
+ mockLocalStorage["jazz-clerk-auth"] = JSON.stringify({
48
+ accountID: "test-account-id",
49
+ accountSecret: "test-secret",
50
+ });
51
+
52
+ const auth = new BrowserClerkAuth(mockDriver, {
53
+ ...mockClerkClient,
54
+ user: null,
55
+ });
56
+
57
+ const result = await auth.start();
58
+ expect(result.type).toBe("existing");
59
+ });
60
+ });
61
+
62
+ describe("clerk credentials not in localStorage", () => {
63
+ it("should return new credentials when clerk user signs up", async () => {
64
+ const auth = new BrowserClerkAuth(mockDriver, mockClerkClient);
65
+ const result = await auth.start();
66
+ expect(result.type).toBe("new");
67
+ });
68
+
69
+ it("should return existing credentials when clerk user is signed in", async () => {
70
+ mockClerkClient = {
71
+ user: {
72
+ unsafeMetadata: {
73
+ jazzAccountID: "test-account-id",
74
+ jazzAccountSecret: "test-secret",
75
+ },
76
+ fullName: "Test User",
77
+ username: "testuser",
78
+ id: "test-id",
79
+ update: vi.fn(),
80
+ },
81
+ signOut: vi.fn(),
82
+ };
83
+
84
+ const auth = new BrowserClerkAuth(mockDriver, mockClerkClient);
85
+ const result = await auth.start();
86
+ expect(result.type).toBe("existing");
87
+ });
88
+
89
+ it("should throw error when not signed in", async () => {
90
+ const auth = new BrowserClerkAuth(mockDriver, {
91
+ ...mockClerkClient,
92
+ user: null,
93
+ });
94
+
95
+ await expect(auth.start()).rejects.toThrow("Not signed in");
96
+ });
97
+
98
+ it("should save credentials to localStorage", async () => {
99
+ const auth = new BrowserClerkAuth(mockDriver, mockClerkClient);
100
+ const result = await auth.start();
101
+ if (result.saveCredentials) {
102
+ await result.saveCredentials({
103
+ accountID: "test-account-id" as ID<Account>,
104
+ secret: "test-secret" as AgentSecret,
105
+ });
106
+ }
107
+
108
+ expect(mockLocalStorage["jazz-clerk-auth"]).toBeDefined();
109
+ });
110
+
111
+ it("should call clerk signOut when logging out", async () => {
112
+ const auth = new BrowserClerkAuth(mockDriver, mockClerkClient);
113
+ const result = await auth.start();
114
+ result.logOut();
115
+
116
+ expect(mockClerkClient.signOut).toHaveBeenCalled();
117
+ });
118
+ });
119
+ });
package/tsconfig.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "jsx": "react",
11
11
  "forceConsistentCasingInFileNames": true,
12
12
  "noUncheckedIndexedAccess": true,
13
- "esModuleInterop": true,
13
+ "esModuleInterop": true
14
14
  },
15
- "include": ["./src/**/*"],
15
+ "include": ["./src/**/*"]
16
16
  }
package/.eslintrc.cjs DELETED
@@ -1,24 +0,0 @@
1
- module.exports = {
2
- extends: [
3
- "eslint:recommended",
4
- "plugin:@typescript-eslint/recommended",
5
- "plugin:require-extensions/recommended",
6
- "prettier"
7
- ],
8
- parser: "@typescript-eslint/parser",
9
- plugins: ["@typescript-eslint", "require-extensions"],
10
- parserOptions: {
11
- project: "./tsconfig.json",
12
- tsconfigRootDir: __dirname,
13
- },
14
- ignorePatterns: [".eslint.cjs"],
15
- root: true,
16
- rules: {
17
- "no-unused-vars": "off",
18
- "@typescript-eslint/no-unused-vars": [
19
- "error",
20
- { argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
21
- ],
22
- "@typescript-eslint/no-floating-promises": "error",
23
- },
24
- }
package/.prettierrc.js DELETED
@@ -1,9 +0,0 @@
1
- /** @type {import("prettier").Config} */
2
- const config = {
3
- trailingComma: "all",
4
- tabWidth: 4,
5
- semi: true,
6
- singleQuote: false,
7
- };
8
-
9
- export default config;