better-auth 0.2.3-beta.8 → 0.2.4
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/access.js +125 -1
- package/dist/adapters.d.ts +1 -2
- package/dist/adapters.js +1155 -14
- package/dist/api.js +2458 -4
- package/dist/cli.js +1010 -3
- package/dist/client/plugins.js +527 -2
- package/dist/client.js +382 -1
- package/dist/index.js +3670 -5
- package/dist/next-js.js +34 -1
- package/dist/node.js +8 -1
- package/dist/plugins.js +5529 -4
- package/dist/react.js +393 -1
- package/dist/social.js +586 -2
- package/dist/solid-start.js +13 -1
- package/dist/solid.js +389 -1
- package/dist/svelte-kit.js +34 -1
- package/dist/svelte.js +380 -1
- package/dist/utils.js +453 -2
- package/dist/vue.js +389 -1
- package/package.json +5 -3
package/dist/client/plugins.js
CHANGED
|
@@ -1,2 +1,527 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// src/plugins/organization/client.ts
|
|
2
|
+
import { atom as atom3 } from "nanostores";
|
|
3
|
+
|
|
4
|
+
// src/plugins/organization/access/src/access.ts
|
|
5
|
+
var ParsingError = class extends Error {
|
|
6
|
+
path;
|
|
7
|
+
constructor(message, path) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.path = path;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var AccessControl = class {
|
|
13
|
+
constructor(s) {
|
|
14
|
+
this.s = s;
|
|
15
|
+
this.statements = s;
|
|
16
|
+
}
|
|
17
|
+
statements;
|
|
18
|
+
newRole(statements) {
|
|
19
|
+
return new Role(statements);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var Role = class _Role {
|
|
23
|
+
statements;
|
|
24
|
+
constructor(statements) {
|
|
25
|
+
this.statements = statements;
|
|
26
|
+
}
|
|
27
|
+
authorize(request, connector) {
|
|
28
|
+
for (const [requestedResource, requestedActions] of Object.entries(
|
|
29
|
+
request
|
|
30
|
+
)) {
|
|
31
|
+
const allowedActions = this.statements[requestedResource];
|
|
32
|
+
if (!allowedActions) {
|
|
33
|
+
return {
|
|
34
|
+
success: false,
|
|
35
|
+
error: `You are not allowed to access resource: ${requestedResource}`
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const success = connector === "OR" ? requestedActions.some(
|
|
39
|
+
(requestedAction) => allowedActions.includes(requestedAction)
|
|
40
|
+
) : requestedActions.every(
|
|
41
|
+
(requestedAction) => allowedActions.includes(requestedAction)
|
|
42
|
+
);
|
|
43
|
+
if (success) {
|
|
44
|
+
return { success };
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
success: false,
|
|
48
|
+
error: `unauthorized to access resource "${requestedResource}"`
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
success: false,
|
|
53
|
+
error: "Not authorized"
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
static fromString(s) {
|
|
57
|
+
const statements = JSON.parse(s);
|
|
58
|
+
if (typeof statements !== "object") {
|
|
59
|
+
throw new ParsingError("statements is not an object", ".");
|
|
60
|
+
}
|
|
61
|
+
for (const [resource, actions] of Object.entries(statements)) {
|
|
62
|
+
if (typeof resource !== "string") {
|
|
63
|
+
throw new ParsingError("invalid resource identifier", resource);
|
|
64
|
+
}
|
|
65
|
+
if (!Array.isArray(actions)) {
|
|
66
|
+
throw new ParsingError("actions is not an array", resource);
|
|
67
|
+
}
|
|
68
|
+
for (let i = 0; i < actions.length; i++) {
|
|
69
|
+
if (typeof actions[i] !== "string") {
|
|
70
|
+
throw new ParsingError("action is not a string", `${resource}[${i}]`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return new _Role(statements);
|
|
75
|
+
}
|
|
76
|
+
toString() {
|
|
77
|
+
return JSON.stringify(this.statements);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// src/plugins/organization/access/statement.ts
|
|
82
|
+
var createAccessControl = (statements) => {
|
|
83
|
+
return new AccessControl(statements);
|
|
84
|
+
};
|
|
85
|
+
var defaultStatements = {
|
|
86
|
+
organization: ["update", "delete"],
|
|
87
|
+
member: ["create", "update", "delete"],
|
|
88
|
+
invitation: ["create", "cancel"]
|
|
89
|
+
};
|
|
90
|
+
var defaultAc = createAccessControl(defaultStatements);
|
|
91
|
+
var adminAc = defaultAc.newRole({
|
|
92
|
+
organization: ["update"],
|
|
93
|
+
invitation: ["create", "cancel"],
|
|
94
|
+
member: ["create", "update", "delete"]
|
|
95
|
+
});
|
|
96
|
+
var ownerAc = defaultAc.newRole({
|
|
97
|
+
organization: ["update", "delete"],
|
|
98
|
+
member: ["create", "update", "delete"],
|
|
99
|
+
invitation: ["create", "cancel"]
|
|
100
|
+
});
|
|
101
|
+
var memberAc = defaultAc.newRole({
|
|
102
|
+
organization: [],
|
|
103
|
+
member: [],
|
|
104
|
+
invitation: []
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// src/client/config.ts
|
|
108
|
+
import { createFetch } from "@better-fetch/fetch";
|
|
109
|
+
import "nanostores";
|
|
110
|
+
|
|
111
|
+
// src/client/fetch-plugins.ts
|
|
112
|
+
import { betterFetch } from "@better-fetch/fetch";
|
|
113
|
+
|
|
114
|
+
// src/client/session-atom.ts
|
|
115
|
+
import { atom as atom2 } from "nanostores";
|
|
116
|
+
|
|
117
|
+
// src/client/query.ts
|
|
118
|
+
import "@better-fetch/fetch";
|
|
119
|
+
import { atom, onMount } from "nanostores";
|
|
120
|
+
var useAuthQuery = (initializedAtom, path, $fetch, options) => {
|
|
121
|
+
const value = atom({
|
|
122
|
+
data: null,
|
|
123
|
+
error: null,
|
|
124
|
+
isPending: false
|
|
125
|
+
});
|
|
126
|
+
const fn = () => {
|
|
127
|
+
const opts = typeof options === "function" ? options({
|
|
128
|
+
data: value.get().data,
|
|
129
|
+
error: value.get().error,
|
|
130
|
+
isPending: value.get().isPending
|
|
131
|
+
}) : options;
|
|
132
|
+
return $fetch(path, {
|
|
133
|
+
...opts,
|
|
134
|
+
onSuccess: async (context) => {
|
|
135
|
+
value.set({
|
|
136
|
+
data: context.data,
|
|
137
|
+
error: null,
|
|
138
|
+
isPending: false
|
|
139
|
+
});
|
|
140
|
+
await opts?.onSuccess?.(context);
|
|
141
|
+
},
|
|
142
|
+
async onError(context) {
|
|
143
|
+
value.set({
|
|
144
|
+
error: context.error,
|
|
145
|
+
data: null,
|
|
146
|
+
isPending: false
|
|
147
|
+
});
|
|
148
|
+
await opts?.onError?.(context);
|
|
149
|
+
},
|
|
150
|
+
async onRequest(context) {
|
|
151
|
+
const currentValue = value.get();
|
|
152
|
+
value.set({
|
|
153
|
+
isPending: true,
|
|
154
|
+
data: currentValue.data,
|
|
155
|
+
error: currentValue.error
|
|
156
|
+
});
|
|
157
|
+
await opts?.onRequest?.(context);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
initializedAtom = Array.isArray(initializedAtom) ? initializedAtom : [initializedAtom];
|
|
162
|
+
let isMounted = false;
|
|
163
|
+
for (const initAtom of initializedAtom) {
|
|
164
|
+
initAtom.subscribe(() => {
|
|
165
|
+
if (isMounted) {
|
|
166
|
+
fn();
|
|
167
|
+
} else {
|
|
168
|
+
onMount(value, () => {
|
|
169
|
+
fn();
|
|
170
|
+
isMounted = true;
|
|
171
|
+
return () => {
|
|
172
|
+
value.off();
|
|
173
|
+
initAtom.off();
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return value;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// src/plugins/organization/client.ts
|
|
183
|
+
var organizationClient = (options) => {
|
|
184
|
+
const activeOrgId = atom3(void 0);
|
|
185
|
+
const _listOrg = atom3(false);
|
|
186
|
+
const _activeOrgSignal = atom3(false);
|
|
187
|
+
return {
|
|
188
|
+
id: "organization",
|
|
189
|
+
$InferServerPlugin: {},
|
|
190
|
+
getActions: ($fetch) => ({
|
|
191
|
+
$Infer: {
|
|
192
|
+
ActiveOrganization: {},
|
|
193
|
+
Organization: {},
|
|
194
|
+
Invitation: {},
|
|
195
|
+
Member: {}
|
|
196
|
+
},
|
|
197
|
+
organization: {
|
|
198
|
+
setActive(orgId) {
|
|
199
|
+
activeOrgId.set(orgId);
|
|
200
|
+
},
|
|
201
|
+
hasPermission: async (data) => {
|
|
202
|
+
return await $fetch("/organization/has-permission", {
|
|
203
|
+
method: "POST",
|
|
204
|
+
body: {
|
|
205
|
+
permission: data.permission
|
|
206
|
+
},
|
|
207
|
+
...data.fetchOptions
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}),
|
|
212
|
+
getAtoms: ($fetch) => {
|
|
213
|
+
const listOrganizations = useAuthQuery(
|
|
214
|
+
_listOrg,
|
|
215
|
+
"/organization/list",
|
|
216
|
+
$fetch,
|
|
217
|
+
{
|
|
218
|
+
method: "GET"
|
|
219
|
+
}
|
|
220
|
+
);
|
|
221
|
+
const activeOrganization = useAuthQuery(
|
|
222
|
+
[activeOrgId, _activeOrgSignal],
|
|
223
|
+
"/organization/activate",
|
|
224
|
+
$fetch,
|
|
225
|
+
() => ({
|
|
226
|
+
method: "POST",
|
|
227
|
+
credentials: "include",
|
|
228
|
+
body: {
|
|
229
|
+
orgId: activeOrgId.get()
|
|
230
|
+
}
|
|
231
|
+
})
|
|
232
|
+
);
|
|
233
|
+
return {
|
|
234
|
+
_listOrg,
|
|
235
|
+
_activeOrgSignal,
|
|
236
|
+
activeOrganization,
|
|
237
|
+
listOrganizations
|
|
238
|
+
};
|
|
239
|
+
},
|
|
240
|
+
atomListeners: [
|
|
241
|
+
{
|
|
242
|
+
matcher(path) {
|
|
243
|
+
return path === "/organization/create" || path === "/organization/delete";
|
|
244
|
+
},
|
|
245
|
+
signal: "_listOrg"
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
matcher(path) {
|
|
249
|
+
return path.startsWith("/organization");
|
|
250
|
+
},
|
|
251
|
+
signal: "_activeOrgSignal"
|
|
252
|
+
}
|
|
253
|
+
]
|
|
254
|
+
};
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
// src/plugins/username/client.ts
|
|
258
|
+
var usernameClient = () => {
|
|
259
|
+
return {
|
|
260
|
+
id: "username",
|
|
261
|
+
$InferServerPlugin: {}
|
|
262
|
+
};
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
// src/plugins/passkey/client.ts
|
|
266
|
+
import {
|
|
267
|
+
WebAuthnError,
|
|
268
|
+
startAuthentication,
|
|
269
|
+
startRegistration
|
|
270
|
+
} from "@simplewebauthn/browser";
|
|
271
|
+
|
|
272
|
+
// src/utils/logger.ts
|
|
273
|
+
import { createConsola } from "consola";
|
|
274
|
+
var consola = createConsola({
|
|
275
|
+
formatOptions: {
|
|
276
|
+
date: false,
|
|
277
|
+
colors: true,
|
|
278
|
+
compact: true
|
|
279
|
+
},
|
|
280
|
+
defaults: {
|
|
281
|
+
tag: "Better Auth"
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
var createLogger = (options) => {
|
|
285
|
+
return {
|
|
286
|
+
log: (...args) => {
|
|
287
|
+
!options?.disabled && consola.log("", ...args);
|
|
288
|
+
},
|
|
289
|
+
error: (...args) => {
|
|
290
|
+
!options?.disabled && consola.error("", ...args);
|
|
291
|
+
},
|
|
292
|
+
warn: (...args) => {
|
|
293
|
+
!options?.disabled && consola.warn("", ...args);
|
|
294
|
+
},
|
|
295
|
+
info: (...args) => {
|
|
296
|
+
!options?.disabled && consola.info("", ...args);
|
|
297
|
+
},
|
|
298
|
+
debug: (...args) => {
|
|
299
|
+
!options?.disabled && consola.debug("", ...args);
|
|
300
|
+
},
|
|
301
|
+
box: (...args) => {
|
|
302
|
+
!options?.disabled && consola.box("", ...args);
|
|
303
|
+
},
|
|
304
|
+
success: (...args) => {
|
|
305
|
+
!options?.disabled && consola.success("", ...args);
|
|
306
|
+
},
|
|
307
|
+
break: (...args) => {
|
|
308
|
+
!options?.disabled && console.log("\n");
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
};
|
|
312
|
+
var logger = createLogger();
|
|
313
|
+
|
|
314
|
+
// src/plugins/passkey/client.ts
|
|
315
|
+
import { atom as atom4 } from "nanostores";
|
|
316
|
+
var getPasskeyActions = ($fetch, {
|
|
317
|
+
_listPasskeys
|
|
318
|
+
}) => {
|
|
319
|
+
const signInPasskey = async (opts, options) => {
|
|
320
|
+
const response = await $fetch(
|
|
321
|
+
"/passkey/generate-authenticate-options",
|
|
322
|
+
{
|
|
323
|
+
method: "POST",
|
|
324
|
+
body: {
|
|
325
|
+
email: opts?.email,
|
|
326
|
+
callbackURL: opts?.callbackURL
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
);
|
|
330
|
+
if (!response.data) {
|
|
331
|
+
return response;
|
|
332
|
+
}
|
|
333
|
+
try {
|
|
334
|
+
const res = await startAuthentication(
|
|
335
|
+
response.data,
|
|
336
|
+
opts?.autoFill || false
|
|
337
|
+
);
|
|
338
|
+
const verified = await $fetch("/passkey/verify-authentication", {
|
|
339
|
+
body: {
|
|
340
|
+
response: res
|
|
341
|
+
},
|
|
342
|
+
...opts?.fetchOptions,
|
|
343
|
+
...options
|
|
344
|
+
});
|
|
345
|
+
if (!verified.data) {
|
|
346
|
+
return verified;
|
|
347
|
+
}
|
|
348
|
+
} catch (e) {
|
|
349
|
+
console.log(e);
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
const registerPasskey = async (opts, fetchOpts) => {
|
|
353
|
+
const options = await $fetch(
|
|
354
|
+
"/passkey/generate-register-options",
|
|
355
|
+
{
|
|
356
|
+
method: "GET"
|
|
357
|
+
}
|
|
358
|
+
);
|
|
359
|
+
if (!options.data) {
|
|
360
|
+
return options;
|
|
361
|
+
}
|
|
362
|
+
try {
|
|
363
|
+
const res = await startRegistration(options.data);
|
|
364
|
+
const verified = await $fetch("/passkey/verify-registration", {
|
|
365
|
+
...opts?.fetchOptions,
|
|
366
|
+
...fetchOpts,
|
|
367
|
+
body: {
|
|
368
|
+
response: res,
|
|
369
|
+
name: opts?.name
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
if (!verified.data) {
|
|
373
|
+
return verified;
|
|
374
|
+
}
|
|
375
|
+
_listPasskeys.set(Math.random());
|
|
376
|
+
} catch (e) {
|
|
377
|
+
if (e instanceof WebAuthnError) {
|
|
378
|
+
if (e.code === "ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED") {
|
|
379
|
+
return {
|
|
380
|
+
data: null,
|
|
381
|
+
error: {
|
|
382
|
+
message: "previously registered",
|
|
383
|
+
status: 400,
|
|
384
|
+
statusText: "BAD_REQUEST"
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
if (e.code === "ERROR_CEREMONY_ABORTED") {
|
|
389
|
+
return {
|
|
390
|
+
data: null,
|
|
391
|
+
error: {
|
|
392
|
+
message: "registration cancelled",
|
|
393
|
+
status: 400,
|
|
394
|
+
statusText: "BAD_REQUEST"
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
return {
|
|
399
|
+
data: null,
|
|
400
|
+
error: {
|
|
401
|
+
message: e.message,
|
|
402
|
+
status: 400,
|
|
403
|
+
statusText: "BAD_REQUEST"
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
return {
|
|
408
|
+
data: null,
|
|
409
|
+
error: {
|
|
410
|
+
message: e instanceof Error ? e.message : "unknown error",
|
|
411
|
+
status: 500,
|
|
412
|
+
statusText: "INTERNAL_SERVER_ERROR"
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
return {
|
|
418
|
+
signIn: {
|
|
419
|
+
/**
|
|
420
|
+
* Sign in with a registered passkey
|
|
421
|
+
*/
|
|
422
|
+
passkey: signInPasskey
|
|
423
|
+
},
|
|
424
|
+
passkey: {
|
|
425
|
+
/**
|
|
426
|
+
* Add a passkey to the user account
|
|
427
|
+
*/
|
|
428
|
+
addPasskey: registerPasskey
|
|
429
|
+
},
|
|
430
|
+
/**
|
|
431
|
+
* Inferred Internal Types
|
|
432
|
+
*/
|
|
433
|
+
$Infer: {}
|
|
434
|
+
};
|
|
435
|
+
};
|
|
436
|
+
var passkeyClient = () => {
|
|
437
|
+
const _listPasskeys = atom4();
|
|
438
|
+
return {
|
|
439
|
+
id: "passkey",
|
|
440
|
+
$InferServerPlugin: {},
|
|
441
|
+
getActions: ($fetch) => getPasskeyActions($fetch, {
|
|
442
|
+
_listPasskeys
|
|
443
|
+
}),
|
|
444
|
+
getAtoms($fetch) {
|
|
445
|
+
const listPasskeys = useAuthQuery(
|
|
446
|
+
_listPasskeys,
|
|
447
|
+
"/passkey/list-user-passkeys",
|
|
448
|
+
$fetch,
|
|
449
|
+
{
|
|
450
|
+
method: "GET",
|
|
451
|
+
credentials: "include"
|
|
452
|
+
}
|
|
453
|
+
);
|
|
454
|
+
return {
|
|
455
|
+
listPasskeys,
|
|
456
|
+
_listPasskeys
|
|
457
|
+
};
|
|
458
|
+
},
|
|
459
|
+
pathMethods: {
|
|
460
|
+
"/passkey/register": "POST",
|
|
461
|
+
"/passkey/authenticate": "POST"
|
|
462
|
+
},
|
|
463
|
+
atomListeners: [
|
|
464
|
+
{
|
|
465
|
+
matcher(path) {
|
|
466
|
+
return path === "/passkey/verify-registration" || path === "/passkey/delete-passkey";
|
|
467
|
+
},
|
|
468
|
+
signal: "_listPasskeys"
|
|
469
|
+
}
|
|
470
|
+
]
|
|
471
|
+
};
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
// src/plugins/two-factor/client.ts
|
|
475
|
+
var twoFactorClient = (options = {
|
|
476
|
+
redirect: true,
|
|
477
|
+
twoFactorPage: "/"
|
|
478
|
+
}) => {
|
|
479
|
+
return {
|
|
480
|
+
id: "two-factor",
|
|
481
|
+
$InferServerPlugin: {},
|
|
482
|
+
atomListeners: [
|
|
483
|
+
{
|
|
484
|
+
matcher: (path) => path === "/two-factor/enable" || path === "/two-factor/send-otp" || path === "/two-factor/disable",
|
|
485
|
+
signal: "_sessionSignal"
|
|
486
|
+
}
|
|
487
|
+
],
|
|
488
|
+
pathMethods: {
|
|
489
|
+
"/two-factor/disable": "POST",
|
|
490
|
+
"/two-factor/enable": "POST",
|
|
491
|
+
"/two-factor/send-otp": "POST"
|
|
492
|
+
},
|
|
493
|
+
fetchPlugins: [
|
|
494
|
+
{
|
|
495
|
+
id: "two-factor",
|
|
496
|
+
name: "two-factor",
|
|
497
|
+
hooks: {
|
|
498
|
+
async onSuccess(context) {
|
|
499
|
+
if (context.data?.twoFactorRedirect) {
|
|
500
|
+
if (options.redirect || options.twoFactorPage) {
|
|
501
|
+
if (typeof window !== "undefined") {
|
|
502
|
+
window.location.href = options.twoFactorPage;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
]
|
|
510
|
+
};
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
// src/plugins/magic-link/client.ts
|
|
514
|
+
var magicLinkClient = () => {
|
|
515
|
+
return {
|
|
516
|
+
id: "magic-link",
|
|
517
|
+
$InferServerPlugin: {}
|
|
518
|
+
};
|
|
519
|
+
};
|
|
520
|
+
export {
|
|
521
|
+
getPasskeyActions,
|
|
522
|
+
magicLinkClient,
|
|
523
|
+
organizationClient,
|
|
524
|
+
passkeyClient,
|
|
525
|
+
twoFactorClient,
|
|
526
|
+
usernameClient
|
|
527
|
+
};
|