jazz-tools 0.18.4 → 0.18.6
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/.turbo/turbo-build.log +54 -54
- package/CHANGELOG.md +28 -0
- package/dist/better-auth/auth/client.d.ts.map +1 -1
- package/dist/better-auth/auth/client.js +7 -1
- package/dist/better-auth/auth/client.js.map +1 -1
- package/dist/better-auth/auth/react.d.ts +0 -2142
- package/dist/better-auth/auth/react.d.ts.map +1 -1
- package/dist/better-auth/auth/react.js +2 -14
- package/dist/better-auth/auth/react.js.map +1 -1
- package/dist/better-auth/auth/server.d.ts +21 -1
- package/dist/better-auth/auth/server.d.ts.map +1 -1
- package/dist/better-auth/auth/server.js +83 -27
- package/dist/better-auth/auth/server.js.map +1 -1
- package/dist/better-auth/auth/tests/react.test.d.ts +2 -0
- package/dist/better-auth/auth/tests/react.test.d.ts.map +1 -0
- package/dist/browser/createBrowserContext.d.ts.map +1 -1
- package/dist/browser/index.js +7 -0
- package/dist/browser/index.js.map +1 -1
- package/dist/{chunk-LHQQZH7I.js → chunk-45VKEOXG.js} +123 -81
- package/dist/chunk-45VKEOXG.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/inspector/{custom-element-WCY6D3QJ.js → custom-element-IBHKHN27.js} +19 -69
- package/dist/inspector/custom-element-IBHKHN27.js.map +1 -0
- package/dist/inspector/index.d.ts +5 -1
- package/dist/inspector/index.d.ts.map +1 -1
- package/dist/inspector/index.js +18 -17
- package/dist/inspector/index.js.map +1 -1
- package/dist/inspector/register-custom-element.js +1 -1
- package/dist/inspector/viewer/new-app.d.ts +0 -3
- package/dist/inspector/viewer/new-app.d.ts.map +1 -1
- package/dist/react-core/index.js +3 -1
- package/dist/react-core/index.js.map +1 -1
- package/dist/testing.js +2 -2
- package/dist/testing.js.map +1 -1
- package/dist/tools/coValues/inbox.d.ts +5 -5
- package/dist/tools/coValues/inbox.d.ts.map +1 -1
- package/dist/tools/implementation/anonymousJazzAgent.d.ts +1 -1
- package/dist/tools/implementation/anonymousJazzAgent.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/coExport.d.ts +2 -0
- package/dist/tools/implementation/zodSchema/coExport.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/typeConverters/CoFieldSchemaInit.d.ts +4 -0
- package/dist/tools/implementation/zodSchema/typeConverters/CoFieldSchemaInit.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/typeConverters/TypeOfZodSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/unionUtils.d.ts.map +1 -1
- package/dist/worker/index.d.ts +8 -2
- package/dist/worker/index.d.ts.map +1 -1
- package/dist/worker/index.js +7 -3
- package/dist/worker/index.js.map +1 -1
- package/package.json +5 -4
- package/src/better-auth/auth/client.ts +8 -2
- package/src/better-auth/auth/react.tsx +2 -51
- package/src/better-auth/auth/server.ts +132 -31
- package/src/better-auth/auth/tests/client.test.ts +92 -4
- package/src/better-auth/auth/tests/react.test.tsx +43 -0
- package/src/better-auth/auth/tests/server.test.ts +317 -51
- package/src/browser/createBrowserContext.ts +8 -0
- package/src/inspector/custom-element.tsx +1 -1
- package/src/inspector/index.tsx +44 -0
- package/src/inspector/viewer/new-app.tsx +0 -18
- package/src/tools/coValues/inbox.ts +190 -108
- package/src/tools/implementation/anonymousJazzAgent.ts +1 -1
- package/src/tools/implementation/zodSchema/coExport.ts +2 -0
- package/src/tools/implementation/zodSchema/typeConverters/CoFieldSchemaInit.ts +8 -1
- package/src/tools/implementation/zodSchema/typeConverters/TypeOfZodSchema.ts +0 -1
- package/src/tools/implementation/zodSchema/unionUtils.ts +0 -1
- package/src/tools/testing.ts +1 -1
- package/src/tools/tests/coFeed.test.ts +33 -22
- package/src/tools/tests/coList.test-d.ts +17 -0
- package/src/tools/tests/coList.test.ts +6 -4
- package/src/tools/tests/coMap.record.test-d.ts +18 -0
- package/src/tools/tests/coMap.test-d.ts +15 -0
- package/src/tools/tests/coMap.test.ts +13 -5
- package/src/tools/tests/exportImport.test.ts +3 -1
- package/src/tools/tests/groupsAndAccounts.test.ts +56 -44
- package/src/tools/tests/inbox.test.ts +293 -31
- package/src/worker/index.ts +15 -5
- package/tsup.config.ts +1 -1
- package/dist/chunk-LHQQZH7I.js.map +0 -1
- package/dist/inspector/custom-element-WCY6D3QJ.js.map +0 -1
- package/src/inspector/index.ts +0 -23
@@ -4,6 +4,29 @@ import { symmetricDecrypt, symmetricEncrypt } from "better-auth/crypto";
|
|
4
4
|
import { BetterAuthPlugin, createAuthMiddleware } from "better-auth/plugins";
|
5
5
|
import type { Account, AuthCredentials, ID } from "jazz-tools";
|
6
6
|
|
7
|
+
// Define a type to have user fields mapped in the better-auth instance
|
8
|
+
// It should be automatic, but it needs an hard reference to BetterAuthPlugin type
|
9
|
+
// in order to be exported as library.
|
10
|
+
type JazzPlugin = BetterAuthPlugin & {
|
11
|
+
schema: {
|
12
|
+
user: {
|
13
|
+
fields: {
|
14
|
+
accountID: {
|
15
|
+
type: "string";
|
16
|
+
required: false;
|
17
|
+
input: false;
|
18
|
+
};
|
19
|
+
encryptedCredentials: {
|
20
|
+
type: "string";
|
21
|
+
required: false;
|
22
|
+
input: false;
|
23
|
+
returned: false;
|
24
|
+
};
|
25
|
+
};
|
26
|
+
};
|
27
|
+
};
|
28
|
+
};
|
29
|
+
|
7
30
|
/**
|
8
31
|
* @returns The BetterAuth server plugin.
|
9
32
|
*
|
@@ -15,7 +38,7 @@ import type { Account, AuthCredentials, ID } from "jazz-tools";
|
|
15
38
|
* });
|
16
39
|
* ```
|
17
40
|
*/
|
18
|
-
export const jazzPlugin = ()
|
41
|
+
export const jazzPlugin: () => JazzPlugin = () => {
|
19
42
|
return {
|
20
43
|
id: "jazz-plugin",
|
21
44
|
schema: {
|
@@ -46,15 +69,15 @@ export const jazzPlugin = (): BetterAuthPlugin => {
|
|
46
69
|
// If the user is created without a jazzAuth, it will throw an error.
|
47
70
|
if (!contextContainsJazzAuth(context)) {
|
48
71
|
throw new APIError(422, {
|
49
|
-
message: "JazzAuth is required",
|
72
|
+
message: "JazzAuth is required on user creation",
|
50
73
|
});
|
51
74
|
}
|
52
75
|
// Decorate the user with the jazz's credentials.
|
53
76
|
return {
|
54
77
|
data: {
|
55
|
-
accountID: context
|
78
|
+
accountID: context.jazzAuth.accountID,
|
56
79
|
encryptedCredentials:
|
57
|
-
context
|
80
|
+
context.jazzAuth.encryptedCredentials,
|
58
81
|
},
|
59
82
|
};
|
60
83
|
},
|
@@ -62,20 +85,23 @@ export const jazzPlugin = (): BetterAuthPlugin => {
|
|
62
85
|
},
|
63
86
|
verification: {
|
64
87
|
create: {
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
88
|
+
after: async (verification, context) => {
|
89
|
+
/**
|
90
|
+
* For: Email OTP plugin
|
91
|
+
* After a verification is created, if it is from the EmailOTP plugin,
|
92
|
+
* create a new verification value with the jazzAuth with the same expiration.
|
93
|
+
*/
|
94
|
+
if (
|
95
|
+
contextContainsJazzAuth(context) &&
|
96
|
+
verification.identifier.startsWith("sign-in-otp-")
|
97
|
+
) {
|
98
|
+
await context.context.internalAdapter.createVerificationValue(
|
99
|
+
{
|
100
|
+
value: JSON.stringify({ jazzAuth: context.jazzAuth }),
|
101
|
+
identifier: `${verification.identifier}-jazz-auth`,
|
102
|
+
expiresAt: verification.expiresAt,
|
77
103
|
},
|
78
|
-
|
104
|
+
);
|
79
105
|
}
|
80
106
|
},
|
81
107
|
},
|
@@ -124,6 +150,7 @@ export const jazzPlugin = (): BetterAuthPlugin => {
|
|
124
150
|
},
|
125
151
|
|
126
152
|
/**
|
153
|
+
* For: Social / OAuth2 plugin
|
127
154
|
* /callback is the endpoint that BetterAuth uses to authenticate the user coming from a social provider.
|
128
155
|
* 1. Catch the state
|
129
156
|
* 2. Find the verification value
|
@@ -131,22 +158,20 @@ export const jazzPlugin = (): BetterAuthPlugin => {
|
|
131
158
|
*/
|
132
159
|
{
|
133
160
|
matcher: (context) => {
|
134
|
-
return
|
161
|
+
return (
|
162
|
+
context.path.startsWith("/callback") ||
|
163
|
+
context.path.startsWith("/oauth2/callback")
|
164
|
+
);
|
135
165
|
},
|
136
166
|
handler: createAuthMiddleware(async (ctx) => {
|
137
167
|
const state = ctx.query?.state || ctx.body?.state;
|
138
168
|
|
139
|
-
const
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
value: state,
|
146
|
-
},
|
147
|
-
],
|
148
|
-
select: ["value"],
|
149
|
-
});
|
169
|
+
const identifier = `${state}-jazz-auth`;
|
170
|
+
|
171
|
+
const data =
|
172
|
+
await ctx.context.internalAdapter.findVerificationValue(
|
173
|
+
identifier,
|
174
|
+
);
|
150
175
|
|
151
176
|
// if not found, the social plugin will throw later anyway
|
152
177
|
if (!data) {
|
@@ -158,7 +183,12 @@ export const jazzPlugin = (): BetterAuthPlugin => {
|
|
158
183
|
const parsed = JSON.parse(data.value);
|
159
184
|
|
160
185
|
if (parsed && "jazzAuth" in parsed) {
|
161
|
-
|
186
|
+
return {
|
187
|
+
context: {
|
188
|
+
...ctx,
|
189
|
+
jazzAuth: parsed.jazzAuth,
|
190
|
+
},
|
191
|
+
};
|
162
192
|
} else {
|
163
193
|
throw new APIError(404, {
|
164
194
|
message: "JazzAuth not found in verification value",
|
@@ -166,6 +196,45 @@ export const jazzPlugin = (): BetterAuthPlugin => {
|
|
166
196
|
}
|
167
197
|
}),
|
168
198
|
},
|
199
|
+
/**
|
200
|
+
* For: Email OTP plugin
|
201
|
+
* When the user sends an OTP, we try to find the jazzAuth.
|
202
|
+
* If it isn't a sign-up, we expect to not find a verification value.
|
203
|
+
*/
|
204
|
+
{
|
205
|
+
matcher: (context) => {
|
206
|
+
return context.path.startsWith("/sign-in/email-otp");
|
207
|
+
},
|
208
|
+
handler: createAuthMiddleware(async (ctx) => {
|
209
|
+
const email = ctx.body.email;
|
210
|
+
const identifier = `sign-in-otp-${email}-jazz-auth`;
|
211
|
+
|
212
|
+
const data =
|
213
|
+
await ctx.context.internalAdapter.findVerificationValue(
|
214
|
+
identifier,
|
215
|
+
);
|
216
|
+
|
217
|
+
// if not found, it isn't a sign-up
|
218
|
+
if (!data || data.expiresAt < new Date()) {
|
219
|
+
return;
|
220
|
+
}
|
221
|
+
|
222
|
+
const parsed = JSON.parse(data.value);
|
223
|
+
|
224
|
+
if (parsed && "jazzAuth" in parsed) {
|
225
|
+
return {
|
226
|
+
context: {
|
227
|
+
...ctx,
|
228
|
+
jazzAuth: parsed.jazzAuth,
|
229
|
+
},
|
230
|
+
};
|
231
|
+
} else {
|
232
|
+
throw new APIError(500, {
|
233
|
+
message: "JazzAuth not found in verification value",
|
234
|
+
});
|
235
|
+
}
|
236
|
+
}),
|
237
|
+
},
|
169
238
|
],
|
170
239
|
after: [
|
171
240
|
/**
|
@@ -196,9 +265,41 @@ export const jazzPlugin = (): BetterAuthPlugin => {
|
|
196
265
|
});
|
197
266
|
}),
|
198
267
|
},
|
268
|
+
|
269
|
+
/**
|
270
|
+
* For: Social / OAuth2 plugin
|
271
|
+
* When the user sign-in via social, we create a verification value with the jazzAuth.
|
272
|
+
*/
|
273
|
+
{
|
274
|
+
matcher: (context) => {
|
275
|
+
return context.path.startsWith("/sign-in/social");
|
276
|
+
},
|
277
|
+
handler: createAuthMiddleware(async (ctx) => {
|
278
|
+
if (!contextContainsJazzAuth(ctx)) {
|
279
|
+
throw new APIError(500, {
|
280
|
+
message: "JazzAuth not found in context",
|
281
|
+
});
|
282
|
+
}
|
283
|
+
|
284
|
+
const returned = ctx.context.returned as { url: string };
|
285
|
+
|
286
|
+
const url = new URL(returned.url);
|
287
|
+
const state = url.searchParams.get("state");
|
288
|
+
|
289
|
+
const value = JSON.stringify({ jazzAuth: ctx.jazzAuth });
|
290
|
+
const expiresAt = new Date();
|
291
|
+
expiresAt.setMinutes(expiresAt.getMinutes() + 10);
|
292
|
+
|
293
|
+
await ctx.context.internalAdapter.createVerificationValue({
|
294
|
+
value,
|
295
|
+
identifier: `${state}-jazz-auth`,
|
296
|
+
expiresAt,
|
297
|
+
});
|
298
|
+
}),
|
299
|
+
},
|
199
300
|
],
|
200
301
|
},
|
201
|
-
};
|
302
|
+
} satisfies JazzPlugin;
|
202
303
|
};
|
203
304
|
|
204
305
|
function contextContainsJazzAuth(ctx: unknown): ctx is {
|
@@ -7,14 +7,19 @@ import {
|
|
7
7
|
} from "jazz-tools/testing";
|
8
8
|
import { assert, beforeEach, describe, expect, it, vi } from "vitest";
|
9
9
|
import { jazzPluginClient } from "../client.js";
|
10
|
+
import { emailOTPClient, genericOAuthClient } from "better-auth/client/plugins";
|
10
11
|
|
11
|
-
describe("
|
12
|
+
describe("Better-Auth client plugin", () => {
|
12
13
|
let account: Account;
|
13
14
|
let jazzContextManager: TestJazzContextManager<Account>;
|
14
15
|
let authSecretStorage: AuthSecretStorage;
|
15
16
|
let authClient: ReturnType<
|
16
17
|
typeof createAuthClient<{
|
17
|
-
plugins: ReturnType<
|
18
|
+
plugins: ReturnType<
|
19
|
+
| typeof jazzPluginClient
|
20
|
+
| typeof emailOTPClient
|
21
|
+
| typeof genericOAuthClient
|
22
|
+
>[];
|
18
23
|
}>
|
19
24
|
>;
|
20
25
|
let customFetchImpl = vi.fn();
|
@@ -31,7 +36,7 @@ describe("auth client", () => {
|
|
31
36
|
|
32
37
|
authClient = createAuthClient({
|
33
38
|
baseURL: "http://localhost:3000",
|
34
|
-
plugins: [jazzPluginClient()],
|
39
|
+
plugins: [jazzPluginClient(), emailOTPClient(), genericOAuthClient()],
|
35
40
|
fetchOptions: {
|
36
41
|
customFetchImpl,
|
37
42
|
},
|
@@ -245,5 +250,88 @@ describe("auth client", () => {
|
|
245
250
|
expect(anonymousCredentials).not.toMatchObject(credentials!);
|
246
251
|
});
|
247
252
|
|
248
|
-
it
|
253
|
+
it("should send Jazz credentials using social login", async () => {
|
254
|
+
const credentials = await authSecretStorage.get();
|
255
|
+
assert(credentials, "Jazz credentials are not available");
|
256
|
+
|
257
|
+
customFetchImpl.mockResolvedValue(new Response(JSON.stringify({})));
|
258
|
+
|
259
|
+
// Sign up
|
260
|
+
await authClient.signIn.social({
|
261
|
+
provider: "github",
|
262
|
+
});
|
263
|
+
|
264
|
+
expect(customFetchImpl).toHaveBeenCalledTimes(1);
|
265
|
+
expect(customFetchImpl.mock.calls[0]![0].toString()).toBe(
|
266
|
+
"http://localhost:3000/api/auth/sign-in/social",
|
267
|
+
);
|
268
|
+
|
269
|
+
// Verify the credentials have been injected in the request body
|
270
|
+
expect(
|
271
|
+
customFetchImpl.mock.calls[0]![1].headers.get("x-jazz-auth")!,
|
272
|
+
).toEqual(
|
273
|
+
JSON.stringify({
|
274
|
+
accountID: credentials!.accountID,
|
275
|
+
secretSeed: credentials!.secretSeed,
|
276
|
+
accountSecret: credentials!.accountSecret,
|
277
|
+
}),
|
278
|
+
);
|
279
|
+
});
|
280
|
+
|
281
|
+
it("should send Jazz credentials using oauth generic plugin", async () => {
|
282
|
+
const credentials = await authSecretStorage.get();
|
283
|
+
assert(credentials, "Jazz credentials are not available");
|
284
|
+
|
285
|
+
customFetchImpl.mockResolvedValue(new Response(JSON.stringify({})));
|
286
|
+
|
287
|
+
// Sign up
|
288
|
+
await authClient.signIn.oauth2({
|
289
|
+
providerId: "github",
|
290
|
+
});
|
291
|
+
|
292
|
+
expect(customFetchImpl).toHaveBeenCalledTimes(1);
|
293
|
+
expect(customFetchImpl.mock.calls[0]![0].toString()).toBe(
|
294
|
+
"http://localhost:3000/api/auth/sign-in/oauth2",
|
295
|
+
);
|
296
|
+
|
297
|
+
// Verify the credentials have been injected in the request body
|
298
|
+
expect(
|
299
|
+
customFetchImpl.mock.calls[0]![1].headers.get("x-jazz-auth")!,
|
300
|
+
).toEqual(
|
301
|
+
JSON.stringify({
|
302
|
+
accountID: credentials!.accountID,
|
303
|
+
secretSeed: credentials!.secretSeed,
|
304
|
+
accountSecret: credentials!.accountSecret,
|
305
|
+
}),
|
306
|
+
);
|
307
|
+
});
|
308
|
+
|
309
|
+
it("should send Jazz credentials using email OTP", async () => {
|
310
|
+
const credentials = await authSecretStorage.get();
|
311
|
+
assert(credentials, "Jazz credentials are not available");
|
312
|
+
|
313
|
+
customFetchImpl.mockResolvedValue(new Response(JSON.stringify({})));
|
314
|
+
|
315
|
+
// Sign up
|
316
|
+
await authClient.emailOtp.sendVerificationOtp({
|
317
|
+
email: "test@jazz.dev",
|
318
|
+
type: "sign-in",
|
319
|
+
});
|
320
|
+
|
321
|
+
expect(customFetchImpl).toHaveBeenCalledTimes(1);
|
322
|
+
expect(customFetchImpl.mock.calls[0]![0].toString()).toBe(
|
323
|
+
"http://localhost:3000/api/auth/email-otp/send-verification-otp",
|
324
|
+
);
|
325
|
+
|
326
|
+
// Verify the credentials have been injected in the request body
|
327
|
+
expect(
|
328
|
+
customFetchImpl.mock.calls[0]![1].headers.get("x-jazz-auth")!,
|
329
|
+
).toEqual(
|
330
|
+
JSON.stringify({
|
331
|
+
accountID: credentials!.accountID,
|
332
|
+
secretSeed: credentials!.secretSeed,
|
333
|
+
accountSecret: credentials!.accountSecret,
|
334
|
+
}),
|
335
|
+
);
|
336
|
+
});
|
249
337
|
});
|
@@ -0,0 +1,43 @@
|
|
1
|
+
// @vitest-environment jsdom
|
2
|
+
import { render } from "@testing-library/react";
|
3
|
+
import { describe, expect, it } from "vitest";
|
4
|
+
import { AuthProvider } from "../react";
|
5
|
+
import { createAuthClient } from "better-auth/client";
|
6
|
+
import { jazzPluginClient } from "../client";
|
7
|
+
import { JazzReactProvider } from "jazz-tools/react";
|
8
|
+
|
9
|
+
describe("AuthProvider", () => {
|
10
|
+
it("should throw if no JazzContext is set", () => {
|
11
|
+
const betterAuthClient = createAuthClient({
|
12
|
+
plugins: [jazzPluginClient()],
|
13
|
+
});
|
14
|
+
|
15
|
+
expect(() => {
|
16
|
+
render(
|
17
|
+
<AuthProvider betterAuthClient={betterAuthClient}>
|
18
|
+
<div />
|
19
|
+
</AuthProvider>,
|
20
|
+
);
|
21
|
+
}).toThrow(
|
22
|
+
"You need to set up a JazzProvider on top of your app to use this hook.",
|
23
|
+
);
|
24
|
+
});
|
25
|
+
|
26
|
+
it("should render with JazzReactProvider", () => {
|
27
|
+
const betterAuthClient = createAuthClient({
|
28
|
+
plugins: [jazzPluginClient()],
|
29
|
+
});
|
30
|
+
|
31
|
+
render(
|
32
|
+
<JazzReactProvider
|
33
|
+
// @ts-expect-error - no memory storage
|
34
|
+
storage={["memory"]}
|
35
|
+
sync={{ peer: "ws://", when: "never" }}
|
36
|
+
>
|
37
|
+
<AuthProvider betterAuthClient={betterAuthClient}>
|
38
|
+
<div />
|
39
|
+
</AuthProvider>
|
40
|
+
</JazzReactProvider>,
|
41
|
+
);
|
42
|
+
});
|
43
|
+
});
|