better-auth 1.1.18-beta.2 → 1.1.18
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/{chunk-2VHVLRC7.cjs → chunk-5JET74FS.cjs} +50 -1
- package/dist/{chunk-CFERT2JN.js → chunk-HA5MENQC.js} +110 -2
- package/dist/{chunk-ZQFLCL7G.js → chunk-KFSLYLGZ.js} +50 -1
- package/dist/{chunk-FS4Q5N2E.js → chunk-PB7U7ZCO.js} +57 -2
- package/dist/{chunk-7TKSNJYH.js → chunk-RPETDYVK.js} +23 -0
- package/dist/{chunk-G4LNWL5L.cjs → chunk-TSWYQ5PN.cjs} +57 -2
- package/dist/{chunk-OLYRJHRO.cjs → chunk-W2IU5LNT.cjs} +23 -0
- package/dist/{chunk-KM2MFBY2.cjs → chunk-XW6BECDW.cjs} +109 -1
- package/dist/client/plugins.d.cts +1 -1
- package/dist/client/plugins.d.ts +1 -1
- package/dist/{index-BAdTrrkO.d.ts → index-Cqgianwx.d.ts} +107 -0
- package/dist/{index-CvVfvvKF.d.cts → index-D2rGzLac.d.cts} +107 -0
- package/dist/plugins/admin.cjs +2 -2
- package/dist/plugins/admin.d.cts +65 -0
- package/dist/plugins/admin.d.ts +65 -0
- package/dist/plugins/admin.js +1 -1
- package/dist/plugins/generic-oauth.cjs +2 -2
- package/dist/plugins/generic-oauth.d.cts +82 -0
- package/dist/plugins/generic-oauth.d.ts +82 -0
- package/dist/plugins/generic-oauth.js +1 -1
- package/dist/plugins/organization.cjs +2 -2
- package/dist/plugins/organization.d.cts +1 -1
- package/dist/plugins/organization.d.ts +1 -1
- package/dist/plugins/organization.js +1 -1
- package/dist/plugins/username.cjs +2 -2
- package/dist/plugins/username.d.cts +18 -0
- package/dist/plugins/username.d.ts +18 -0
- package/dist/plugins/username.js +1 -1
- package/dist/plugins.cjs +8 -8
- package/dist/plugins.d.cts +1 -1
- package/dist/plugins.d.ts +1 -1
- package/dist/plugins.js +4 -4
- package/package.json +1 -1
|
@@ -1351,6 +1351,54 @@ var getActiveMember = chunkCQNPMJJ3_cjs.createAuthEndpoint(
|
|
|
1351
1351
|
return ctx.json(member);
|
|
1352
1352
|
}
|
|
1353
1353
|
);
|
|
1354
|
+
var leaveOrganization = chunkCQNPMJJ3_cjs.createAuthEndpoint(
|
|
1355
|
+
"/organization/leave",
|
|
1356
|
+
{
|
|
1357
|
+
method: "POST",
|
|
1358
|
+
body: zod.z.object({
|
|
1359
|
+
organizationId: zod.z.string()
|
|
1360
|
+
}),
|
|
1361
|
+
use: [chunkCQNPMJJ3_cjs.sessionMiddleware, orgMiddleware]
|
|
1362
|
+
},
|
|
1363
|
+
async (ctx) => {
|
|
1364
|
+
const session = ctx.context.session;
|
|
1365
|
+
const adapter = getOrgAdapter(ctx.context);
|
|
1366
|
+
const member = await adapter.findMemberByOrgId({
|
|
1367
|
+
userId: session.user.id,
|
|
1368
|
+
organizationId: ctx.body.organizationId
|
|
1369
|
+
});
|
|
1370
|
+
if (!member) {
|
|
1371
|
+
throw new betterCall.APIError("BAD_REQUEST", {
|
|
1372
|
+
message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
const isOwnerLeaving = member.role === (ctx.context.orgOptions?.creatorRole || "owner");
|
|
1376
|
+
if (isOwnerLeaving) {
|
|
1377
|
+
const members = await ctx.context.adapter.findMany({
|
|
1378
|
+
model: "member",
|
|
1379
|
+
where: [
|
|
1380
|
+
{
|
|
1381
|
+
field: "organizationId",
|
|
1382
|
+
value: ctx.body.organizationId
|
|
1383
|
+
}
|
|
1384
|
+
]
|
|
1385
|
+
});
|
|
1386
|
+
const owners = members.filter(
|
|
1387
|
+
(member2) => member2.role === (ctx.context.orgOptions?.creatorRole || "owner")
|
|
1388
|
+
);
|
|
1389
|
+
if (owners.length <= 1) {
|
|
1390
|
+
throw new betterCall.APIError("BAD_REQUEST", {
|
|
1391
|
+
message: ORGANIZATION_ERROR_CODES.YOU_CANNOT_LEAVE_THE_ORGANIZATION_AS_THE_ONLY_OWNER
|
|
1392
|
+
});
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
await adapter.deleteMember(member.id);
|
|
1396
|
+
if (session.session.activeOrganizationId === ctx.body.organizationId) {
|
|
1397
|
+
await adapter.setActiveOrganization(session.session.token, null);
|
|
1398
|
+
}
|
|
1399
|
+
return ctx.json(member);
|
|
1400
|
+
}
|
|
1401
|
+
);
|
|
1354
1402
|
var createOrganization = chunkCQNPMJJ3_cjs.createAuthEndpoint(
|
|
1355
1403
|
"/organization/create",
|
|
1356
1404
|
{
|
|
@@ -1860,7 +1908,8 @@ var organization = (options) => {
|
|
|
1860
1908
|
addMember: addMember(),
|
|
1861
1909
|
removeMember,
|
|
1862
1910
|
updateMemberRole: updateMemberRole(),
|
|
1863
|
-
getActiveMember
|
|
1911
|
+
getActiveMember,
|
|
1912
|
+
leaveOrganization
|
|
1864
1913
|
};
|
|
1865
1914
|
const roles = {
|
|
1866
1915
|
...chunk2X5G64P2_cjs.defaultRoles,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createAuthEndpoint, handleOAuthUserInfo } from './chunk-KALC2G3Y.js';
|
|
1
|
+
import { createAuthEndpoint, handleOAuthUserInfo, sessionMiddleware, BASE_ERROR_CODES } from './chunk-KALC2G3Y.js';
|
|
2
2
|
import { createAuthorizationURL, validateAuthorizationCode } from './chunk-GTQM7JU7.js';
|
|
3
3
|
import { setSessionCookie } from './chunk-QQGZ3XGI.js';
|
|
4
4
|
import { generateState, parseState } from './chunk-NPO64SVN.js';
|
|
@@ -306,7 +306,7 @@ var genericOAuth = (options) => {
|
|
|
306
306
|
}
|
|
307
307
|
let tokens = void 0;
|
|
308
308
|
const parsedState = await parseState(ctx);
|
|
309
|
-
const { callbackURL, codeVerifier, errorURL, newUserURL } = parsedState;
|
|
309
|
+
const { callbackURL, codeVerifier, errorURL, newUserURL, link } = parsedState;
|
|
310
310
|
const code = ctx.query.code;
|
|
311
311
|
let finalTokenUrl = provider.tokenUrl;
|
|
312
312
|
let finalUserInfoUrl = provider.userInfoUrl;
|
|
@@ -358,6 +358,27 @@ var genericOAuth = (options) => {
|
|
|
358
358
|
);
|
|
359
359
|
}
|
|
360
360
|
const mapUser = provider.mapProfileToUser ? await provider.mapProfileToUser(userInfo) : null;
|
|
361
|
+
if (link) {
|
|
362
|
+
if (link.email !== userInfo.email.toLowerCase()) {
|
|
363
|
+
return redirectOnError("email_doesn't_match");
|
|
364
|
+
}
|
|
365
|
+
const newAccount = await ctx.context.internalAdapter.createAccount({
|
|
366
|
+
userId: link.userId,
|
|
367
|
+
providerId: provider.providerId,
|
|
368
|
+
accountId: userInfo.id
|
|
369
|
+
});
|
|
370
|
+
if (!newAccount) {
|
|
371
|
+
return redirectOnError("unable_to_link_account");
|
|
372
|
+
}
|
|
373
|
+
let toRedirectTo2;
|
|
374
|
+
try {
|
|
375
|
+
const url = callbackURL;
|
|
376
|
+
toRedirectTo2 = url.toString();
|
|
377
|
+
} catch {
|
|
378
|
+
toRedirectTo2 = callbackURL;
|
|
379
|
+
}
|
|
380
|
+
throw ctx.redirect(toRedirectTo2);
|
|
381
|
+
}
|
|
361
382
|
const result = await handleOAuthUserInfo(ctx, {
|
|
362
383
|
userInfo: {
|
|
363
384
|
...userInfo,
|
|
@@ -392,6 +413,93 @@ var genericOAuth = (options) => {
|
|
|
392
413
|
}
|
|
393
414
|
throw ctx.redirect(toRedirectTo);
|
|
394
415
|
}
|
|
416
|
+
),
|
|
417
|
+
oAuth2LinkAccount: createAuthEndpoint(
|
|
418
|
+
"/oauth2/link",
|
|
419
|
+
{
|
|
420
|
+
method: "POST",
|
|
421
|
+
body: z.object({
|
|
422
|
+
providerId: z.string(),
|
|
423
|
+
callbackURL: z.string()
|
|
424
|
+
}),
|
|
425
|
+
use: [sessionMiddleware]
|
|
426
|
+
},
|
|
427
|
+
async (c) => {
|
|
428
|
+
const session = c.context.session;
|
|
429
|
+
const account = await c.context.internalAdapter.findAccounts(
|
|
430
|
+
session.user.id
|
|
431
|
+
);
|
|
432
|
+
const existingAccount = account.find(
|
|
433
|
+
(a) => a.providerId === c.body.providerId
|
|
434
|
+
);
|
|
435
|
+
if (existingAccount) {
|
|
436
|
+
throw new APIError("BAD_REQUEST", {
|
|
437
|
+
message: BASE_ERROR_CODES.SOCIAL_ACCOUNT_ALREADY_LINKED
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
const provider = options.config.find(
|
|
441
|
+
(p) => p.providerId === c.body.providerId
|
|
442
|
+
);
|
|
443
|
+
if (!provider) {
|
|
444
|
+
throw new APIError("NOT_FOUND", {
|
|
445
|
+
message: BASE_ERROR_CODES.PROVIDER_NOT_FOUND
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
const {
|
|
449
|
+
providerId,
|
|
450
|
+
clientId,
|
|
451
|
+
clientSecret,
|
|
452
|
+
redirectURI,
|
|
453
|
+
authorizationUrl,
|
|
454
|
+
discoveryUrl,
|
|
455
|
+
pkce,
|
|
456
|
+
scopes
|
|
457
|
+
} = provider;
|
|
458
|
+
let finalAuthUrl = authorizationUrl;
|
|
459
|
+
if (!finalAuthUrl) {
|
|
460
|
+
if (!discoveryUrl) {
|
|
461
|
+
throw new APIError("BAD_REQUEST", {
|
|
462
|
+
message: ERROR_CODES.INVALID_OAUTH_CONFIGURATION
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
const discovery = await betterFetch(discoveryUrl, {
|
|
466
|
+
onError(context) {
|
|
467
|
+
c.context.logger.error(context.error.message, context.error, {
|
|
468
|
+
discoveryUrl
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
if (discovery.data) {
|
|
473
|
+
finalAuthUrl = discovery.data.authorization_endpoint;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
if (!finalAuthUrl) {
|
|
477
|
+
throw new APIError("BAD_REQUEST", {
|
|
478
|
+
message: ERROR_CODES.INVALID_OAUTH_CONFIGURATION
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
const state = await generateState(c, {
|
|
482
|
+
userId: session.user.id,
|
|
483
|
+
email: session.user.email
|
|
484
|
+
});
|
|
485
|
+
const url = await createAuthorizationURL({
|
|
486
|
+
id: providerId,
|
|
487
|
+
options: {
|
|
488
|
+
clientId,
|
|
489
|
+
clientSecret,
|
|
490
|
+
redirectURI: redirectURI || `${c.context.baseURL}/oauth2/callback`
|
|
491
|
+
},
|
|
492
|
+
authorizationEndpoint: finalAuthUrl,
|
|
493
|
+
state: state.state,
|
|
494
|
+
codeVerifier: pkce ? state.codeVerifier : void 0,
|
|
495
|
+
scopes: scopes || [],
|
|
496
|
+
redirectURI: `${c.context.baseURL}/oauth2/callback/${providerId}`
|
|
497
|
+
});
|
|
498
|
+
return c.json({
|
|
499
|
+
url: url.toString(),
|
|
500
|
+
redirect: true
|
|
501
|
+
});
|
|
502
|
+
}
|
|
395
503
|
)
|
|
396
504
|
},
|
|
397
505
|
$ERROR_CODES: ERROR_CODES
|
|
@@ -1349,6 +1349,54 @@ var getActiveMember = createAuthEndpoint(
|
|
|
1349
1349
|
return ctx.json(member);
|
|
1350
1350
|
}
|
|
1351
1351
|
);
|
|
1352
|
+
var leaveOrganization = createAuthEndpoint(
|
|
1353
|
+
"/organization/leave",
|
|
1354
|
+
{
|
|
1355
|
+
method: "POST",
|
|
1356
|
+
body: z.object({
|
|
1357
|
+
organizationId: z.string()
|
|
1358
|
+
}),
|
|
1359
|
+
use: [sessionMiddleware, orgMiddleware]
|
|
1360
|
+
},
|
|
1361
|
+
async (ctx) => {
|
|
1362
|
+
const session = ctx.context.session;
|
|
1363
|
+
const adapter = getOrgAdapter(ctx.context);
|
|
1364
|
+
const member = await adapter.findMemberByOrgId({
|
|
1365
|
+
userId: session.user.id,
|
|
1366
|
+
organizationId: ctx.body.organizationId
|
|
1367
|
+
});
|
|
1368
|
+
if (!member) {
|
|
1369
|
+
throw new APIError("BAD_REQUEST", {
|
|
1370
|
+
message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
const isOwnerLeaving = member.role === (ctx.context.orgOptions?.creatorRole || "owner");
|
|
1374
|
+
if (isOwnerLeaving) {
|
|
1375
|
+
const members = await ctx.context.adapter.findMany({
|
|
1376
|
+
model: "member",
|
|
1377
|
+
where: [
|
|
1378
|
+
{
|
|
1379
|
+
field: "organizationId",
|
|
1380
|
+
value: ctx.body.organizationId
|
|
1381
|
+
}
|
|
1382
|
+
]
|
|
1383
|
+
});
|
|
1384
|
+
const owners = members.filter(
|
|
1385
|
+
(member2) => member2.role === (ctx.context.orgOptions?.creatorRole || "owner")
|
|
1386
|
+
);
|
|
1387
|
+
if (owners.length <= 1) {
|
|
1388
|
+
throw new APIError("BAD_REQUEST", {
|
|
1389
|
+
message: ORGANIZATION_ERROR_CODES.YOU_CANNOT_LEAVE_THE_ORGANIZATION_AS_THE_ONLY_OWNER
|
|
1390
|
+
});
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
await adapter.deleteMember(member.id);
|
|
1394
|
+
if (session.session.activeOrganizationId === ctx.body.organizationId) {
|
|
1395
|
+
await adapter.setActiveOrganization(session.session.token, null);
|
|
1396
|
+
}
|
|
1397
|
+
return ctx.json(member);
|
|
1398
|
+
}
|
|
1399
|
+
);
|
|
1352
1400
|
var createOrganization = createAuthEndpoint(
|
|
1353
1401
|
"/organization/create",
|
|
1354
1402
|
{
|
|
@@ -1858,7 +1906,8 @@ var organization = (options) => {
|
|
|
1858
1906
|
addMember: addMember(),
|
|
1859
1907
|
removeMember,
|
|
1860
1908
|
updateMemberRole: updateMemberRole(),
|
|
1861
|
-
getActiveMember
|
|
1909
|
+
getActiveMember,
|
|
1910
|
+
leaveOrganization
|
|
1862
1911
|
};
|
|
1863
1912
|
const roles = {
|
|
1864
1913
|
...defaultRoles,
|
|
@@ -26,12 +26,18 @@ var schema = {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
// src/plugins/username/index.ts
|
|
29
|
+
function defaultUsernameValidator(username2) {
|
|
30
|
+
return /^[a-zA-Z0-9_]+$/.test(username2);
|
|
31
|
+
}
|
|
29
32
|
var username = (options) => {
|
|
30
33
|
const ERROR_CODES = {
|
|
31
34
|
INVALID_USERNAME_OR_PASSWORD: "invalid username or password",
|
|
32
35
|
EMAIL_NOT_VERIFIED: "email not verified",
|
|
33
36
|
UNEXPECTED_ERROR: "unexpected error",
|
|
34
|
-
USERNAME_IS_ALREADY_TAKEN: "username is already taken. please try another."
|
|
37
|
+
USERNAME_IS_ALREADY_TAKEN: "username is already taken. please try another.",
|
|
38
|
+
USERNAME_TOO_SHORT: "username is too short",
|
|
39
|
+
USERNAME_TOO_LONG: "username is too long",
|
|
40
|
+
INVALID_USERNAME: "username is invalid"
|
|
35
41
|
};
|
|
36
42
|
return {
|
|
37
43
|
id: "username",
|
|
@@ -79,6 +85,36 @@ var username = (options) => {
|
|
|
79
85
|
}
|
|
80
86
|
},
|
|
81
87
|
async (ctx) => {
|
|
88
|
+
if (!ctx.body.username || !ctx.body.password) {
|
|
89
|
+
ctx.context.logger.error("Username or password not found");
|
|
90
|
+
throw new APIError("UNAUTHORIZED", {
|
|
91
|
+
message: ERROR_CODES.INVALID_USERNAME_OR_PASSWORD
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
const minUsernameLength = options?.minUsernameLength || 3;
|
|
95
|
+
const maxUsernameLength = options?.maxUsernameLength || 30;
|
|
96
|
+
if (ctx.body.username.length < minUsernameLength) {
|
|
97
|
+
ctx.context.logger.error("Username too short", {
|
|
98
|
+
username: ctx.body.username
|
|
99
|
+
});
|
|
100
|
+
throw new APIError("UNPROCESSABLE_ENTITY", {
|
|
101
|
+
message: ERROR_CODES.USERNAME_TOO_SHORT
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
if (ctx.body.username.length > maxUsernameLength) {
|
|
105
|
+
ctx.context.logger.error("Username too long", {
|
|
106
|
+
username: ctx.body.username
|
|
107
|
+
});
|
|
108
|
+
throw new APIError("UNPROCESSABLE_ENTITY", {
|
|
109
|
+
message: ERROR_CODES.USERNAME_TOO_LONG
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
const validator = options?.usernameValidator || defaultUsernameValidator;
|
|
113
|
+
if (!validator(ctx.body.username)) {
|
|
114
|
+
throw new APIError("UNPROCESSABLE_ENTITY", {
|
|
115
|
+
message: ERROR_CODES.INVALID_USERNAME
|
|
116
|
+
});
|
|
117
|
+
}
|
|
82
118
|
const user = await ctx.context.adapter.findOne({
|
|
83
119
|
model: "user",
|
|
84
120
|
where: [
|
|
@@ -175,11 +211,30 @@ var username = (options) => {
|
|
|
175
211
|
before: [
|
|
176
212
|
{
|
|
177
213
|
matcher(context) {
|
|
178
|
-
return context.path === "/sign-up/email";
|
|
214
|
+
return context.path === "/sign-up/email" || context.path === "/update-user";
|
|
179
215
|
},
|
|
180
216
|
async handler(ctx) {
|
|
181
217
|
const username2 = ctx.body.username;
|
|
182
218
|
if (username2) {
|
|
219
|
+
const minUsernameLength = options?.minUsernameLength || 3;
|
|
220
|
+
const maxUsernameLength = options?.maxUsernameLength || 30;
|
|
221
|
+
if (username2.length < minUsernameLength) {
|
|
222
|
+
throw new APIError("UNPROCESSABLE_ENTITY", {
|
|
223
|
+
message: ERROR_CODES.USERNAME_TOO_SHORT
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
if (username2.length > maxUsernameLength) {
|
|
227
|
+
throw new APIError("UNPROCESSABLE_ENTITY", {
|
|
228
|
+
message: ERROR_CODES.USERNAME_TOO_LONG
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
const validator = options?.usernameValidator || defaultUsernameValidator;
|
|
232
|
+
const valid = await validator(username2);
|
|
233
|
+
if (!valid) {
|
|
234
|
+
throw new APIError("UNPROCESSABLE_ENTITY", {
|
|
235
|
+
message: ERROR_CODES.INVALID_USERNAME
|
|
236
|
+
});
|
|
237
|
+
}
|
|
183
238
|
const user = await ctx.context.adapter.findOne({
|
|
184
239
|
model: "user",
|
|
185
240
|
where: [
|
|
@@ -769,6 +769,29 @@ var admin = (options) => {
|
|
|
769
769
|
success: true
|
|
770
770
|
});
|
|
771
771
|
}
|
|
772
|
+
),
|
|
773
|
+
setUserPassword: createAuthEndpoint(
|
|
774
|
+
"/admin/set-user-password",
|
|
775
|
+
{
|
|
776
|
+
method: "POST",
|
|
777
|
+
body: z.object({
|
|
778
|
+
newPassword: z.string(),
|
|
779
|
+
userId: z.string()
|
|
780
|
+
}),
|
|
781
|
+
use: [adminMiddleware]
|
|
782
|
+
},
|
|
783
|
+
async (ctx) => {
|
|
784
|
+
const hashedPassword = await ctx.context.password.hash(
|
|
785
|
+
ctx.body.newPassword
|
|
786
|
+
);
|
|
787
|
+
await ctx.context.internalAdapter.updatePassword(
|
|
788
|
+
ctx.body.userId,
|
|
789
|
+
hashedPassword
|
|
790
|
+
);
|
|
791
|
+
return ctx.json({
|
|
792
|
+
status: true
|
|
793
|
+
});
|
|
794
|
+
}
|
|
772
795
|
)
|
|
773
796
|
},
|
|
774
797
|
$ERROR_CODES: ERROR_CODES,
|
|
@@ -28,12 +28,18 @@ var schema = {
|
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
// src/plugins/username/index.ts
|
|
31
|
+
function defaultUsernameValidator(username2) {
|
|
32
|
+
return /^[a-zA-Z0-9_]+$/.test(username2);
|
|
33
|
+
}
|
|
31
34
|
var username = (options) => {
|
|
32
35
|
const ERROR_CODES = {
|
|
33
36
|
INVALID_USERNAME_OR_PASSWORD: "invalid username or password",
|
|
34
37
|
EMAIL_NOT_VERIFIED: "email not verified",
|
|
35
38
|
UNEXPECTED_ERROR: "unexpected error",
|
|
36
|
-
USERNAME_IS_ALREADY_TAKEN: "username is already taken. please try another."
|
|
39
|
+
USERNAME_IS_ALREADY_TAKEN: "username is already taken. please try another.",
|
|
40
|
+
USERNAME_TOO_SHORT: "username is too short",
|
|
41
|
+
USERNAME_TOO_LONG: "username is too long",
|
|
42
|
+
INVALID_USERNAME: "username is invalid"
|
|
37
43
|
};
|
|
38
44
|
return {
|
|
39
45
|
id: "username",
|
|
@@ -81,6 +87,36 @@ var username = (options) => {
|
|
|
81
87
|
}
|
|
82
88
|
},
|
|
83
89
|
async (ctx) => {
|
|
90
|
+
if (!ctx.body.username || !ctx.body.password) {
|
|
91
|
+
ctx.context.logger.error("Username or password not found");
|
|
92
|
+
throw new betterCall.APIError("UNAUTHORIZED", {
|
|
93
|
+
message: ERROR_CODES.INVALID_USERNAME_OR_PASSWORD
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
const minUsernameLength = options?.minUsernameLength || 3;
|
|
97
|
+
const maxUsernameLength = options?.maxUsernameLength || 30;
|
|
98
|
+
if (ctx.body.username.length < minUsernameLength) {
|
|
99
|
+
ctx.context.logger.error("Username too short", {
|
|
100
|
+
username: ctx.body.username
|
|
101
|
+
});
|
|
102
|
+
throw new betterCall.APIError("UNPROCESSABLE_ENTITY", {
|
|
103
|
+
message: ERROR_CODES.USERNAME_TOO_SHORT
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
if (ctx.body.username.length > maxUsernameLength) {
|
|
107
|
+
ctx.context.logger.error("Username too long", {
|
|
108
|
+
username: ctx.body.username
|
|
109
|
+
});
|
|
110
|
+
throw new betterCall.APIError("UNPROCESSABLE_ENTITY", {
|
|
111
|
+
message: ERROR_CODES.USERNAME_TOO_LONG
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
const validator = options?.usernameValidator || defaultUsernameValidator;
|
|
115
|
+
if (!validator(ctx.body.username)) {
|
|
116
|
+
throw new betterCall.APIError("UNPROCESSABLE_ENTITY", {
|
|
117
|
+
message: ERROR_CODES.INVALID_USERNAME
|
|
118
|
+
});
|
|
119
|
+
}
|
|
84
120
|
const user = await ctx.context.adapter.findOne({
|
|
85
121
|
model: "user",
|
|
86
122
|
where: [
|
|
@@ -177,11 +213,30 @@ var username = (options) => {
|
|
|
177
213
|
before: [
|
|
178
214
|
{
|
|
179
215
|
matcher(context) {
|
|
180
|
-
return context.path === "/sign-up/email";
|
|
216
|
+
return context.path === "/sign-up/email" || context.path === "/update-user";
|
|
181
217
|
},
|
|
182
218
|
async handler(ctx) {
|
|
183
219
|
const username2 = ctx.body.username;
|
|
184
220
|
if (username2) {
|
|
221
|
+
const minUsernameLength = options?.minUsernameLength || 3;
|
|
222
|
+
const maxUsernameLength = options?.maxUsernameLength || 30;
|
|
223
|
+
if (username2.length < minUsernameLength) {
|
|
224
|
+
throw new betterCall.APIError("UNPROCESSABLE_ENTITY", {
|
|
225
|
+
message: ERROR_CODES.USERNAME_TOO_SHORT
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
if (username2.length > maxUsernameLength) {
|
|
229
|
+
throw new betterCall.APIError("UNPROCESSABLE_ENTITY", {
|
|
230
|
+
message: ERROR_CODES.USERNAME_TOO_LONG
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
const validator = options?.usernameValidator || defaultUsernameValidator;
|
|
234
|
+
const valid = await validator(username2);
|
|
235
|
+
if (!valid) {
|
|
236
|
+
throw new betterCall.APIError("UNPROCESSABLE_ENTITY", {
|
|
237
|
+
message: ERROR_CODES.INVALID_USERNAME
|
|
238
|
+
});
|
|
239
|
+
}
|
|
185
240
|
const user = await ctx.context.adapter.findOne({
|
|
186
241
|
model: "user",
|
|
187
242
|
where: [
|
|
@@ -771,6 +771,29 @@ var admin = (options) => {
|
|
|
771
771
|
success: true
|
|
772
772
|
});
|
|
773
773
|
}
|
|
774
|
+
),
|
|
775
|
+
setUserPassword: chunkCQNPMJJ3_cjs.createAuthEndpoint(
|
|
776
|
+
"/admin/set-user-password",
|
|
777
|
+
{
|
|
778
|
+
method: "POST",
|
|
779
|
+
body: zod.z.object({
|
|
780
|
+
newPassword: zod.z.string(),
|
|
781
|
+
userId: zod.z.string()
|
|
782
|
+
}),
|
|
783
|
+
use: [adminMiddleware]
|
|
784
|
+
},
|
|
785
|
+
async (ctx) => {
|
|
786
|
+
const hashedPassword = await ctx.context.password.hash(
|
|
787
|
+
ctx.body.newPassword
|
|
788
|
+
);
|
|
789
|
+
await ctx.context.internalAdapter.updatePassword(
|
|
790
|
+
ctx.body.userId,
|
|
791
|
+
hashedPassword
|
|
792
|
+
);
|
|
793
|
+
return ctx.json({
|
|
794
|
+
status: true
|
|
795
|
+
});
|
|
796
|
+
}
|
|
774
797
|
)
|
|
775
798
|
},
|
|
776
799
|
$ERROR_CODES: ERROR_CODES,
|
|
@@ -308,7 +308,7 @@ var genericOAuth = (options) => {
|
|
|
308
308
|
}
|
|
309
309
|
let tokens = void 0;
|
|
310
310
|
const parsedState = await chunk5E75URIA_cjs.parseState(ctx);
|
|
311
|
-
const { callbackURL, codeVerifier, errorURL, newUserURL } = parsedState;
|
|
311
|
+
const { callbackURL, codeVerifier, errorURL, newUserURL, link } = parsedState;
|
|
312
312
|
const code = ctx.query.code;
|
|
313
313
|
let finalTokenUrl = provider.tokenUrl;
|
|
314
314
|
let finalUserInfoUrl = provider.userInfoUrl;
|
|
@@ -360,6 +360,27 @@ var genericOAuth = (options) => {
|
|
|
360
360
|
);
|
|
361
361
|
}
|
|
362
362
|
const mapUser = provider.mapProfileToUser ? await provider.mapProfileToUser(userInfo) : null;
|
|
363
|
+
if (link) {
|
|
364
|
+
if (link.email !== userInfo.email.toLowerCase()) {
|
|
365
|
+
return redirectOnError("email_doesn't_match");
|
|
366
|
+
}
|
|
367
|
+
const newAccount = await ctx.context.internalAdapter.createAccount({
|
|
368
|
+
userId: link.userId,
|
|
369
|
+
providerId: provider.providerId,
|
|
370
|
+
accountId: userInfo.id
|
|
371
|
+
});
|
|
372
|
+
if (!newAccount) {
|
|
373
|
+
return redirectOnError("unable_to_link_account");
|
|
374
|
+
}
|
|
375
|
+
let toRedirectTo2;
|
|
376
|
+
try {
|
|
377
|
+
const url = callbackURL;
|
|
378
|
+
toRedirectTo2 = url.toString();
|
|
379
|
+
} catch {
|
|
380
|
+
toRedirectTo2 = callbackURL;
|
|
381
|
+
}
|
|
382
|
+
throw ctx.redirect(toRedirectTo2);
|
|
383
|
+
}
|
|
363
384
|
const result = await chunkCQNPMJJ3_cjs.handleOAuthUserInfo(ctx, {
|
|
364
385
|
userInfo: {
|
|
365
386
|
...userInfo,
|
|
@@ -394,6 +415,93 @@ var genericOAuth = (options) => {
|
|
|
394
415
|
}
|
|
395
416
|
throw ctx.redirect(toRedirectTo);
|
|
396
417
|
}
|
|
418
|
+
),
|
|
419
|
+
oAuth2LinkAccount: chunkCQNPMJJ3_cjs.createAuthEndpoint(
|
|
420
|
+
"/oauth2/link",
|
|
421
|
+
{
|
|
422
|
+
method: "POST",
|
|
423
|
+
body: zod.z.object({
|
|
424
|
+
providerId: zod.z.string(),
|
|
425
|
+
callbackURL: zod.z.string()
|
|
426
|
+
}),
|
|
427
|
+
use: [chunkCQNPMJJ3_cjs.sessionMiddleware]
|
|
428
|
+
},
|
|
429
|
+
async (c) => {
|
|
430
|
+
const session = c.context.session;
|
|
431
|
+
const account = await c.context.internalAdapter.findAccounts(
|
|
432
|
+
session.user.id
|
|
433
|
+
);
|
|
434
|
+
const existingAccount = account.find(
|
|
435
|
+
(a) => a.providerId === c.body.providerId
|
|
436
|
+
);
|
|
437
|
+
if (existingAccount) {
|
|
438
|
+
throw new betterCall.APIError("BAD_REQUEST", {
|
|
439
|
+
message: chunkCQNPMJJ3_cjs.BASE_ERROR_CODES.SOCIAL_ACCOUNT_ALREADY_LINKED
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
const provider = options.config.find(
|
|
443
|
+
(p) => p.providerId === c.body.providerId
|
|
444
|
+
);
|
|
445
|
+
if (!provider) {
|
|
446
|
+
throw new betterCall.APIError("NOT_FOUND", {
|
|
447
|
+
message: chunkCQNPMJJ3_cjs.BASE_ERROR_CODES.PROVIDER_NOT_FOUND
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
const {
|
|
451
|
+
providerId,
|
|
452
|
+
clientId,
|
|
453
|
+
clientSecret,
|
|
454
|
+
redirectURI,
|
|
455
|
+
authorizationUrl,
|
|
456
|
+
discoveryUrl,
|
|
457
|
+
pkce,
|
|
458
|
+
scopes
|
|
459
|
+
} = provider;
|
|
460
|
+
let finalAuthUrl = authorizationUrl;
|
|
461
|
+
if (!finalAuthUrl) {
|
|
462
|
+
if (!discoveryUrl) {
|
|
463
|
+
throw new betterCall.APIError("BAD_REQUEST", {
|
|
464
|
+
message: ERROR_CODES.INVALID_OAUTH_CONFIGURATION
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
const discovery = await fetch.betterFetch(discoveryUrl, {
|
|
468
|
+
onError(context) {
|
|
469
|
+
c.context.logger.error(context.error.message, context.error, {
|
|
470
|
+
discoveryUrl
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
if (discovery.data) {
|
|
475
|
+
finalAuthUrl = discovery.data.authorization_endpoint;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (!finalAuthUrl) {
|
|
479
|
+
throw new betterCall.APIError("BAD_REQUEST", {
|
|
480
|
+
message: ERROR_CODES.INVALID_OAUTH_CONFIGURATION
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
const state = await chunk5E75URIA_cjs.generateState(c, {
|
|
484
|
+
userId: session.user.id,
|
|
485
|
+
email: session.user.email
|
|
486
|
+
});
|
|
487
|
+
const url = await chunkV6RGXSG4_cjs.createAuthorizationURL({
|
|
488
|
+
id: providerId,
|
|
489
|
+
options: {
|
|
490
|
+
clientId,
|
|
491
|
+
clientSecret,
|
|
492
|
+
redirectURI: redirectURI || `${c.context.baseURL}/oauth2/callback`
|
|
493
|
+
},
|
|
494
|
+
authorizationEndpoint: finalAuthUrl,
|
|
495
|
+
state: state.state,
|
|
496
|
+
codeVerifier: pkce ? state.codeVerifier : void 0,
|
|
497
|
+
scopes: scopes || [],
|
|
498
|
+
redirectURI: `${c.context.baseURL}/oauth2/callback/${providerId}`
|
|
499
|
+
});
|
|
500
|
+
return c.json({
|
|
501
|
+
url: url.toString(),
|
|
502
|
+
redirect: true
|
|
503
|
+
});
|
|
504
|
+
}
|
|
397
505
|
)
|
|
398
506
|
},
|
|
399
507
|
$ERROR_CODES: ERROR_CODES
|
|
@@ -2,7 +2,7 @@ import * as nanostores from 'nanostores';
|
|
|
2
2
|
import { AccessControl, StatementsPrimitive, Role } from '../plugins/access.cjs';
|
|
3
3
|
import * as _better_fetch_fetch from '@better-fetch/fetch';
|
|
4
4
|
import { BetterFetchOption } from '@better-fetch/fetch';
|
|
5
|
-
import { o as organization, a as Organization, M as Member, I as Invitation } from '../index-
|
|
5
|
+
import { o as organization, a as Organization, M as Member, I as Invitation } from '../index-D2rGzLac.cjs';
|
|
6
6
|
import { b as Prettify } from '../helper-Bi8FQwDD.cjs';
|
|
7
7
|
import { username } from '../plugins/username.cjs';
|
|
8
8
|
export { getPasskeyActions, passkeyClient } from '../plugins/passkey.cjs';
|
package/dist/client/plugins.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as nanostores from 'nanostores';
|
|
|
2
2
|
import { AccessControl, StatementsPrimitive, Role } from '../plugins/access.js';
|
|
3
3
|
import * as _better_fetch_fetch from '@better-fetch/fetch';
|
|
4
4
|
import { BetterFetchOption } from '@better-fetch/fetch';
|
|
5
|
-
import { o as organization, a as Organization, M as Member, I as Invitation } from '../index-
|
|
5
|
+
import { o as organization, a as Organization, M as Member, I as Invitation } from '../index-Cqgianwx.js';
|
|
6
6
|
import { b as Prettify } from '../helper-Bi8FQwDD.js';
|
|
7
7
|
import { username } from '../plugins/username.js';
|
|
8
8
|
export { getPasskeyActions, passkeyClient } from '../plugins/passkey.js';
|