better-auth 1.2.0-beta.8 → 1.2.0-beta.9
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/adapters/mongodb.cjs +16 -3
- package/dist/adapters/mongodb.js +16 -3
- package/dist/{chunk-XKCN2VRG.cjs → chunk-45RBYQKI.cjs} +58 -2
- package/dist/{chunk-KAEQXMEV.cjs → chunk-GSEEAIRZ.cjs} +1 -1
- package/dist/{chunk-RMPI2YZ3.js → chunk-IQUHEVLH.js} +1 -1
- package/dist/{chunk-5AOJMBJI.js → chunk-J6VNT2T4.js} +8 -0
- package/dist/{chunk-75SPUXMY.cjs → chunk-QNLSMTXF.cjs} +9 -4
- package/dist/{chunk-GWJDRQJ3.js → chunk-QQGRNAM2.js} +9 -4
- package/dist/{chunk-OALLKKNX.cjs → chunk-SMZIOWVN.cjs} +8 -0
- package/dist/{chunk-FX2ZHENM.js → chunk-TZ34QTAV.js} +58 -2
- package/dist/{chunk-LIGJVOZ4.cjs → chunk-WLOSPOQJ.cjs} +45 -24
- package/dist/{chunk-V56HFQQX.js → chunk-YHW4KODD.js} +45 -24
- package/dist/client/plugins.cjs +45 -12
- package/dist/client/plugins.d.cts +42 -4
- package/dist/client/plugins.d.ts +42 -4
- package/dist/client/plugins.js +45 -12
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/next-js.cjs +5 -5
- package/dist/next-js.js +5 -5
- package/dist/plugins/admin.cjs +2 -2
- package/dist/plugins/admin.d.cts +6 -0
- package/dist/plugins/admin.d.ts +6 -0
- package/dist/plugins/admin.js +1 -1
- package/dist/plugins/oidc-provider.cjs +2 -2
- package/dist/plugins/oidc-provider.d.cts +1 -1
- package/dist/plugins/oidc-provider.d.ts +1 -1
- package/dist/plugins/oidc-provider.js +1 -1
- package/dist/plugins/one-tap.cjs +2 -2
- package/dist/plugins/one-tap.d.cts +7 -0
- package/dist/plugins/one-tap.d.ts +7 -0
- package/dist/plugins/one-tap.js +1 -1
- package/dist/plugins/open-api.cjs +2 -2
- package/dist/plugins/open-api.js +1 -1
- package/dist/plugins/username.cjs +2 -2
- package/dist/plugins/username.d.cts +21 -1
- package/dist/plugins/username.d.ts +21 -1
- package/dist/plugins/username.js +1 -1
- package/dist/plugins.cjs +10 -10
- package/dist/plugins.js +5 -5
- package/package.json +1 -1
|
@@ -15,7 +15,11 @@ var mongodb = require('mongodb');
|
|
|
15
15
|
|
|
16
16
|
var createTransform = (options) => {
|
|
17
17
|
const schema = chunkKAR56MJZ_cjs.getAuthTables(options);
|
|
18
|
+
const customIdGen = options.advanced?.generateId;
|
|
18
19
|
function serializeID(field, value, model) {
|
|
20
|
+
if (customIdGen) {
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
19
23
|
if (field === "id" || field === "_id" || schema[model].fields[field].references?.field === "id") {
|
|
20
24
|
if (typeof value !== "string") {
|
|
21
25
|
if (value instanceof mongodb.ObjectId) {
|
|
@@ -47,6 +51,9 @@ var createTransform = (options) => {
|
|
|
47
51
|
return value;
|
|
48
52
|
}
|
|
49
53
|
function deserializeID(field, value, model) {
|
|
54
|
+
if (customIdGen) {
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
50
57
|
if (field === "id" || schema[model].fields[field].references?.field === "id") {
|
|
51
58
|
if (value instanceof mongodb.ObjectId) {
|
|
52
59
|
return value.toHexString();
|
|
@@ -65,6 +72,9 @@ var createTransform = (options) => {
|
|
|
65
72
|
}
|
|
66
73
|
function getField(field, model) {
|
|
67
74
|
if (field === "id") {
|
|
75
|
+
if (customIdGen) {
|
|
76
|
+
return "id";
|
|
77
|
+
}
|
|
68
78
|
return "_id";
|
|
69
79
|
}
|
|
70
80
|
const f = schema[model].fields[field];
|
|
@@ -72,7 +82,9 @@ var createTransform = (options) => {
|
|
|
72
82
|
}
|
|
73
83
|
return {
|
|
74
84
|
transformInput(data, model, action) {
|
|
75
|
-
const transformedData = action === "update" ? {} : {
|
|
85
|
+
const transformedData = action === "update" ? {} : customIdGen ? {
|
|
86
|
+
id: customIdGen({ model })
|
|
87
|
+
} : {
|
|
76
88
|
_id: new mongodb.ObjectId()
|
|
77
89
|
};
|
|
78
90
|
const fields = schema[model].fields;
|
|
@@ -179,17 +191,18 @@ var createTransform = (options) => {
|
|
|
179
191
|
};
|
|
180
192
|
var mongodbAdapter = (db) => (options) => {
|
|
181
193
|
const transform = createTransform(options);
|
|
194
|
+
const hasCustomId = options.advanced?.generateId;
|
|
182
195
|
return {
|
|
183
196
|
id: "mongodb-adapter",
|
|
184
197
|
async create(data) {
|
|
185
198
|
const { model, data: values, select } = data;
|
|
186
199
|
const transformedData = transform.transformInput(values, model, "create");
|
|
187
|
-
if (transformedData.id) {
|
|
200
|
+
if (transformedData.id && !hasCustomId) {
|
|
188
201
|
delete transformedData.id;
|
|
189
202
|
}
|
|
190
203
|
const res = await db.collection(transform.getModelName(model)).insertOne(transformedData);
|
|
191
204
|
const id = res.insertedId;
|
|
192
|
-
const insertedData = {
|
|
205
|
+
const insertedData = { id: id.toString(), ...transformedData };
|
|
193
206
|
const t = transform.transformOutput(insertedData, model, select);
|
|
194
207
|
return t;
|
|
195
208
|
},
|
package/dist/adapters/mongodb.js
CHANGED
|
@@ -13,7 +13,11 @@ import { ObjectId } from 'mongodb';
|
|
|
13
13
|
|
|
14
14
|
var createTransform = (options) => {
|
|
15
15
|
const schema = getAuthTables(options);
|
|
16
|
+
const customIdGen = options.advanced?.generateId;
|
|
16
17
|
function serializeID(field, value, model) {
|
|
18
|
+
if (customIdGen) {
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
17
21
|
if (field === "id" || field === "_id" || schema[model].fields[field].references?.field === "id") {
|
|
18
22
|
if (typeof value !== "string") {
|
|
19
23
|
if (value instanceof ObjectId) {
|
|
@@ -45,6 +49,9 @@ var createTransform = (options) => {
|
|
|
45
49
|
return value;
|
|
46
50
|
}
|
|
47
51
|
function deserializeID(field, value, model) {
|
|
52
|
+
if (customIdGen) {
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
48
55
|
if (field === "id" || schema[model].fields[field].references?.field === "id") {
|
|
49
56
|
if (value instanceof ObjectId) {
|
|
50
57
|
return value.toHexString();
|
|
@@ -63,6 +70,9 @@ var createTransform = (options) => {
|
|
|
63
70
|
}
|
|
64
71
|
function getField(field, model) {
|
|
65
72
|
if (field === "id") {
|
|
73
|
+
if (customIdGen) {
|
|
74
|
+
return "id";
|
|
75
|
+
}
|
|
66
76
|
return "_id";
|
|
67
77
|
}
|
|
68
78
|
const f = schema[model].fields[field];
|
|
@@ -70,7 +80,9 @@ var createTransform = (options) => {
|
|
|
70
80
|
}
|
|
71
81
|
return {
|
|
72
82
|
transformInput(data, model, action) {
|
|
73
|
-
const transformedData = action === "update" ? {} : {
|
|
83
|
+
const transformedData = action === "update" ? {} : customIdGen ? {
|
|
84
|
+
id: customIdGen({ model })
|
|
85
|
+
} : {
|
|
74
86
|
_id: new ObjectId()
|
|
75
87
|
};
|
|
76
88
|
const fields = schema[model].fields;
|
|
@@ -177,17 +189,18 @@ var createTransform = (options) => {
|
|
|
177
189
|
};
|
|
178
190
|
var mongodbAdapter = (db) => (options) => {
|
|
179
191
|
const transform = createTransform(options);
|
|
192
|
+
const hasCustomId = options.advanced?.generateId;
|
|
180
193
|
return {
|
|
181
194
|
id: "mongodb-adapter",
|
|
182
195
|
async create(data) {
|
|
183
196
|
const { model, data: values, select } = data;
|
|
184
197
|
const transformedData = transform.transformInput(values, model, "create");
|
|
185
|
-
if (transformedData.id) {
|
|
198
|
+
if (transformedData.id && !hasCustomId) {
|
|
186
199
|
delete transformedData.id;
|
|
187
200
|
}
|
|
188
201
|
const res = await db.collection(transform.getModelName(model)).insertOne(transformedData);
|
|
189
202
|
const id = res.insertedId;
|
|
190
|
-
const insertedData = {
|
|
203
|
+
const insertedData = { id: id.toString(), ...transformedData };
|
|
191
204
|
const t = transform.transformOutput(insertedData, model, select);
|
|
192
205
|
return t;
|
|
193
206
|
},
|
|
@@ -32,12 +32,18 @@ var schema = {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
// src/plugins/username/index.ts
|
|
35
|
+
function defaultUsernameValidator(username2) {
|
|
36
|
+
return /^[a-zA-Z0-9_]+$/.test(username2);
|
|
37
|
+
}
|
|
35
38
|
var username = (options) => {
|
|
36
39
|
const ERROR_CODES = {
|
|
37
40
|
INVALID_USERNAME_OR_PASSWORD: "invalid username or password",
|
|
38
41
|
EMAIL_NOT_VERIFIED: "email not verified",
|
|
39
42
|
UNEXPECTED_ERROR: "unexpected error",
|
|
40
|
-
USERNAME_IS_ALREADY_TAKEN: "username is already taken. please try another."
|
|
43
|
+
USERNAME_IS_ALREADY_TAKEN: "username is already taken. please try another.",
|
|
44
|
+
USERNAME_TOO_SHORT: "username is too short",
|
|
45
|
+
USERNAME_TOO_LONG: "username is too long",
|
|
46
|
+
INVALID_USERNAME: "username is invalid"
|
|
41
47
|
};
|
|
42
48
|
return {
|
|
43
49
|
id: "username",
|
|
@@ -85,6 +91,36 @@ var username = (options) => {
|
|
|
85
91
|
}
|
|
86
92
|
},
|
|
87
93
|
async (ctx) => {
|
|
94
|
+
if (!ctx.body.username || !ctx.body.password) {
|
|
95
|
+
ctx.context.logger.error("Username or password not found");
|
|
96
|
+
throw new betterCall.APIError("UNAUTHORIZED", {
|
|
97
|
+
message: ERROR_CODES.INVALID_USERNAME_OR_PASSWORD
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
const minUsernameLength = options?.minUsernameLength || 3;
|
|
101
|
+
const maxUsernameLength = options?.maxUsernameLength || 30;
|
|
102
|
+
if (ctx.body.username.length < minUsernameLength) {
|
|
103
|
+
ctx.context.logger.error("Username too short", {
|
|
104
|
+
username: ctx.body.username
|
|
105
|
+
});
|
|
106
|
+
throw new betterCall.APIError("UNPROCESSABLE_ENTITY", {
|
|
107
|
+
message: ERROR_CODES.USERNAME_TOO_SHORT
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
if (ctx.body.username.length > maxUsernameLength) {
|
|
111
|
+
ctx.context.logger.error("Username too long", {
|
|
112
|
+
username: ctx.body.username
|
|
113
|
+
});
|
|
114
|
+
throw new betterCall.APIError("UNPROCESSABLE_ENTITY", {
|
|
115
|
+
message: ERROR_CODES.USERNAME_TOO_LONG
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
const validator = options?.usernameValidator || defaultUsernameValidator;
|
|
119
|
+
if (!validator(ctx.body.username)) {
|
|
120
|
+
throw new betterCall.APIError("UNPROCESSABLE_ENTITY", {
|
|
121
|
+
message: ERROR_CODES.INVALID_USERNAME
|
|
122
|
+
});
|
|
123
|
+
}
|
|
88
124
|
const user = await ctx.context.adapter.findOne({
|
|
89
125
|
model: "user",
|
|
90
126
|
where: [
|
|
@@ -166,6 +202,7 @@ var username = (options) => {
|
|
|
166
202
|
id: user.id,
|
|
167
203
|
email: user.email,
|
|
168
204
|
emailVerified: user.emailVerified,
|
|
205
|
+
username: user.username,
|
|
169
206
|
name: user.name,
|
|
170
207
|
image: user.image,
|
|
171
208
|
createdAt: user.createdAt,
|
|
@@ -180,11 +217,30 @@ var username = (options) => {
|
|
|
180
217
|
before: [
|
|
181
218
|
{
|
|
182
219
|
matcher(context) {
|
|
183
|
-
return context.path === "/sign-up/email";
|
|
220
|
+
return context.path === "/sign-up/email" || context.path === "/update-user";
|
|
184
221
|
},
|
|
185
222
|
handler: chunkN7UWN6ND_cjs.createAuthMiddleware(async (ctx) => {
|
|
186
223
|
const username2 = ctx.body.username;
|
|
187
224
|
if (username2) {
|
|
225
|
+
const minUsernameLength = options?.minUsernameLength || 3;
|
|
226
|
+
const maxUsernameLength = options?.maxUsernameLength || 30;
|
|
227
|
+
if (username2.length < minUsernameLength) {
|
|
228
|
+
throw new betterCall.APIError("UNPROCESSABLE_ENTITY", {
|
|
229
|
+
message: ERROR_CODES.USERNAME_TOO_SHORT
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
if (username2.length > maxUsernameLength) {
|
|
233
|
+
throw new betterCall.APIError("UNPROCESSABLE_ENTITY", {
|
|
234
|
+
message: ERROR_CODES.USERNAME_TOO_LONG
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
const validator = options?.usernameValidator || defaultUsernameValidator;
|
|
238
|
+
const valid = await validator(username2);
|
|
239
|
+
if (!valid) {
|
|
240
|
+
throw new betterCall.APIError("UNPROCESSABLE_ENTITY", {
|
|
241
|
+
message: ERROR_CODES.INVALID_USERNAME
|
|
242
|
+
});
|
|
243
|
+
}
|
|
188
244
|
const user = await ctx.context.adapter.findOne({
|
|
189
245
|
model: "user",
|
|
190
246
|
where: [
|
|
@@ -332,7 +332,7 @@ var getMetadata = (ctx, options) => {
|
|
|
332
332
|
issuer,
|
|
333
333
|
authorization_endpoint: `${baseURL}/oauth2/authorize`,
|
|
334
334
|
token_endpoint: `${baseURL}/oauth2/token`,
|
|
335
|
-
|
|
335
|
+
userinfo_endpoint: `${baseURL}/oauth2/userinfo`,
|
|
336
336
|
jwks_uri: `${baseURL}/jwks`,
|
|
337
337
|
registration_endpoint: `${baseURL}/oauth2/register`,
|
|
338
338
|
scopes_supported: ["openid", "profile", "email", "offline_access"],
|
|
@@ -330,7 +330,7 @@ var getMetadata = (ctx, options) => {
|
|
|
330
330
|
issuer,
|
|
331
331
|
authorization_endpoint: `${baseURL}/oauth2/authorize`,
|
|
332
332
|
token_endpoint: `${baseURL}/oauth2/token`,
|
|
333
|
-
|
|
333
|
+
userinfo_endpoint: `${baseURL}/oauth2/userinfo`,
|
|
334
334
|
jwks_uri: `${baseURL}/jwks`,
|
|
335
335
|
registration_endpoint: `${baseURL}/oauth2/register`,
|
|
336
336
|
scopes_supported: ["openid", "profile", "email", "offline_access"],
|
|
@@ -24,6 +24,14 @@ var admin = (options) => {
|
|
|
24
24
|
throw new APIError("UNAUTHORIZED");
|
|
25
25
|
}
|
|
26
26
|
const user = session.user;
|
|
27
|
+
if (options?.adminUserIds?.includes(user.id)) {
|
|
28
|
+
return {
|
|
29
|
+
session: {
|
|
30
|
+
user,
|
|
31
|
+
session: session.session
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
27
35
|
if (!user.role || (Array.isArray(opts.adminRole) ? !opts.adminRole.includes(user.role) : user.role !== opts.adminRole)) {
|
|
28
36
|
throw new APIError("FORBIDDEN", {
|
|
29
37
|
message: "Only admins can access this endpoint"
|
|
@@ -179,6 +179,9 @@ function getResponse(responses) {
|
|
|
179
179
|
...responses
|
|
180
180
|
};
|
|
181
181
|
}
|
|
182
|
+
function toOpenApiPath(path) {
|
|
183
|
+
return path.split("/").map((part) => part.startsWith(":") ? `{${part.slice(1)}}` : part).join("/");
|
|
184
|
+
}
|
|
182
185
|
async function generator(ctx, options) {
|
|
183
186
|
const baseEndpoints = chunkN7UWN6ND_cjs.getEndpoints(ctx, {
|
|
184
187
|
...options,
|
|
@@ -209,8 +212,9 @@ async function generator(ctx, options) {
|
|
|
209
212
|
Object.entries(baseEndpoints.api).forEach(([_, value]) => {
|
|
210
213
|
const options2 = value.options;
|
|
211
214
|
if (options2.metadata?.SERVER_ONLY) return;
|
|
215
|
+
const path = toOpenApiPath(value.path);
|
|
212
216
|
if (options2.method === "GET") {
|
|
213
|
-
paths[
|
|
217
|
+
paths[path] = {
|
|
214
218
|
get: {
|
|
215
219
|
tags: ["Default", ...options2.metadata?.openapi?.tags || []],
|
|
216
220
|
description: options2.metadata?.openapi?.description,
|
|
@@ -227,7 +231,7 @@ async function generator(ctx, options) {
|
|
|
227
231
|
}
|
|
228
232
|
if (options2.method === "POST") {
|
|
229
233
|
const body = getRequestBody(options2);
|
|
230
|
-
paths[
|
|
234
|
+
paths[path] = {
|
|
231
235
|
post: {
|
|
232
236
|
tags: ["Default", ...options2.metadata?.openapi?.tags || []],
|
|
233
237
|
description: options2.metadata?.openapi?.description,
|
|
@@ -273,8 +277,9 @@ async function generator(ctx, options) {
|
|
|
273
277
|
Object.entries(api).forEach(([key, value]) => {
|
|
274
278
|
const options2 = value.options;
|
|
275
279
|
if (options2.metadata?.SERVER_ONLY) return;
|
|
280
|
+
const path = toOpenApiPath(value.path);
|
|
276
281
|
if (options2.method === "GET") {
|
|
277
|
-
paths[
|
|
282
|
+
paths[path] = {
|
|
278
283
|
get: {
|
|
279
284
|
tags: options2.metadata?.openapi?.tags || [
|
|
280
285
|
plugin.id.charAt(0).toUpperCase() + plugin.id.slice(1)
|
|
@@ -292,7 +297,7 @@ async function generator(ctx, options) {
|
|
|
292
297
|
};
|
|
293
298
|
}
|
|
294
299
|
if (options2.method === "POST") {
|
|
295
|
-
paths[
|
|
300
|
+
paths[path] = {
|
|
296
301
|
post: {
|
|
297
302
|
tags: options2.metadata?.openapi?.tags || [
|
|
298
303
|
plugin.id.charAt(0).toUpperCase() + plugin.id.slice(1)
|
|
@@ -177,6 +177,9 @@ function getResponse(responses) {
|
|
|
177
177
|
...responses
|
|
178
178
|
};
|
|
179
179
|
}
|
|
180
|
+
function toOpenApiPath(path) {
|
|
181
|
+
return path.split("/").map((part) => part.startsWith(":") ? `{${part.slice(1)}}` : part).join("/");
|
|
182
|
+
}
|
|
180
183
|
async function generator(ctx, options) {
|
|
181
184
|
const baseEndpoints = getEndpoints(ctx, {
|
|
182
185
|
...options,
|
|
@@ -207,8 +210,9 @@ async function generator(ctx, options) {
|
|
|
207
210
|
Object.entries(baseEndpoints.api).forEach(([_, value]) => {
|
|
208
211
|
const options2 = value.options;
|
|
209
212
|
if (options2.metadata?.SERVER_ONLY) return;
|
|
213
|
+
const path = toOpenApiPath(value.path);
|
|
210
214
|
if (options2.method === "GET") {
|
|
211
|
-
paths[
|
|
215
|
+
paths[path] = {
|
|
212
216
|
get: {
|
|
213
217
|
tags: ["Default", ...options2.metadata?.openapi?.tags || []],
|
|
214
218
|
description: options2.metadata?.openapi?.description,
|
|
@@ -225,7 +229,7 @@ async function generator(ctx, options) {
|
|
|
225
229
|
}
|
|
226
230
|
if (options2.method === "POST") {
|
|
227
231
|
const body = getRequestBody(options2);
|
|
228
|
-
paths[
|
|
232
|
+
paths[path] = {
|
|
229
233
|
post: {
|
|
230
234
|
tags: ["Default", ...options2.metadata?.openapi?.tags || []],
|
|
231
235
|
description: options2.metadata?.openapi?.description,
|
|
@@ -271,8 +275,9 @@ async function generator(ctx, options) {
|
|
|
271
275
|
Object.entries(api).forEach(([key, value]) => {
|
|
272
276
|
const options2 = value.options;
|
|
273
277
|
if (options2.metadata?.SERVER_ONLY) return;
|
|
278
|
+
const path = toOpenApiPath(value.path);
|
|
274
279
|
if (options2.method === "GET") {
|
|
275
|
-
paths[
|
|
280
|
+
paths[path] = {
|
|
276
281
|
get: {
|
|
277
282
|
tags: options2.metadata?.openapi?.tags || [
|
|
278
283
|
plugin.id.charAt(0).toUpperCase() + plugin.id.slice(1)
|
|
@@ -290,7 +295,7 @@ async function generator(ctx, options) {
|
|
|
290
295
|
};
|
|
291
296
|
}
|
|
292
297
|
if (options2.method === "POST") {
|
|
293
|
-
paths[
|
|
298
|
+
paths[path] = {
|
|
294
299
|
post: {
|
|
295
300
|
tags: options2.metadata?.openapi?.tags || [
|
|
296
301
|
plugin.id.charAt(0).toUpperCase() + plugin.id.slice(1)
|
|
@@ -26,6 +26,14 @@ var admin = (options) => {
|
|
|
26
26
|
throw new chunkN7UWN6ND_cjs.APIError("UNAUTHORIZED");
|
|
27
27
|
}
|
|
28
28
|
const user = session.user;
|
|
29
|
+
if (options?.adminUserIds?.includes(user.id)) {
|
|
30
|
+
return {
|
|
31
|
+
session: {
|
|
32
|
+
user,
|
|
33
|
+
session: session.session
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
29
37
|
if (!user.role || (Array.isArray(opts.adminRole) ? !opts.adminRole.includes(user.role) : user.role !== opts.adminRole)) {
|
|
30
38
|
throw new chunkN7UWN6ND_cjs.APIError("FORBIDDEN", {
|
|
31
39
|
message: "Only admins can access this endpoint"
|
|
@@ -30,12 +30,18 @@ var schema = {
|
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
// src/plugins/username/index.ts
|
|
33
|
+
function defaultUsernameValidator(username2) {
|
|
34
|
+
return /^[a-zA-Z0-9_]+$/.test(username2);
|
|
35
|
+
}
|
|
33
36
|
var username = (options) => {
|
|
34
37
|
const ERROR_CODES = {
|
|
35
38
|
INVALID_USERNAME_OR_PASSWORD: "invalid username or password",
|
|
36
39
|
EMAIL_NOT_VERIFIED: "email not verified",
|
|
37
40
|
UNEXPECTED_ERROR: "unexpected error",
|
|
38
|
-
USERNAME_IS_ALREADY_TAKEN: "username is already taken. please try another."
|
|
41
|
+
USERNAME_IS_ALREADY_TAKEN: "username is already taken. please try another.",
|
|
42
|
+
USERNAME_TOO_SHORT: "username is too short",
|
|
43
|
+
USERNAME_TOO_LONG: "username is too long",
|
|
44
|
+
INVALID_USERNAME: "username is invalid"
|
|
39
45
|
};
|
|
40
46
|
return {
|
|
41
47
|
id: "username",
|
|
@@ -83,6 +89,36 @@ var username = (options) => {
|
|
|
83
89
|
}
|
|
84
90
|
},
|
|
85
91
|
async (ctx) => {
|
|
92
|
+
if (!ctx.body.username || !ctx.body.password) {
|
|
93
|
+
ctx.context.logger.error("Username or password not found");
|
|
94
|
+
throw new APIError("UNAUTHORIZED", {
|
|
95
|
+
message: ERROR_CODES.INVALID_USERNAME_OR_PASSWORD
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const minUsernameLength = options?.minUsernameLength || 3;
|
|
99
|
+
const maxUsernameLength = options?.maxUsernameLength || 30;
|
|
100
|
+
if (ctx.body.username.length < minUsernameLength) {
|
|
101
|
+
ctx.context.logger.error("Username too short", {
|
|
102
|
+
username: ctx.body.username
|
|
103
|
+
});
|
|
104
|
+
throw new APIError("UNPROCESSABLE_ENTITY", {
|
|
105
|
+
message: ERROR_CODES.USERNAME_TOO_SHORT
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if (ctx.body.username.length > maxUsernameLength) {
|
|
109
|
+
ctx.context.logger.error("Username too long", {
|
|
110
|
+
username: ctx.body.username
|
|
111
|
+
});
|
|
112
|
+
throw new APIError("UNPROCESSABLE_ENTITY", {
|
|
113
|
+
message: ERROR_CODES.USERNAME_TOO_LONG
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const validator = options?.usernameValidator || defaultUsernameValidator;
|
|
117
|
+
if (!validator(ctx.body.username)) {
|
|
118
|
+
throw new APIError("UNPROCESSABLE_ENTITY", {
|
|
119
|
+
message: ERROR_CODES.INVALID_USERNAME
|
|
120
|
+
});
|
|
121
|
+
}
|
|
86
122
|
const user = await ctx.context.adapter.findOne({
|
|
87
123
|
model: "user",
|
|
88
124
|
where: [
|
|
@@ -164,6 +200,7 @@ var username = (options) => {
|
|
|
164
200
|
id: user.id,
|
|
165
201
|
email: user.email,
|
|
166
202
|
emailVerified: user.emailVerified,
|
|
203
|
+
username: user.username,
|
|
167
204
|
name: user.name,
|
|
168
205
|
image: user.image,
|
|
169
206
|
createdAt: user.createdAt,
|
|
@@ -178,11 +215,30 @@ var username = (options) => {
|
|
|
178
215
|
before: [
|
|
179
216
|
{
|
|
180
217
|
matcher(context) {
|
|
181
|
-
return context.path === "/sign-up/email";
|
|
218
|
+
return context.path === "/sign-up/email" || context.path === "/update-user";
|
|
182
219
|
},
|
|
183
220
|
handler: createAuthMiddleware(async (ctx) => {
|
|
184
221
|
const username2 = ctx.body.username;
|
|
185
222
|
if (username2) {
|
|
223
|
+
const minUsernameLength = options?.minUsernameLength || 3;
|
|
224
|
+
const maxUsernameLength = options?.maxUsernameLength || 30;
|
|
225
|
+
if (username2.length < minUsernameLength) {
|
|
226
|
+
throw new APIError("UNPROCESSABLE_ENTITY", {
|
|
227
|
+
message: ERROR_CODES.USERNAME_TOO_SHORT
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
if (username2.length > maxUsernameLength) {
|
|
231
|
+
throw new APIError("UNPROCESSABLE_ENTITY", {
|
|
232
|
+
message: ERROR_CODES.USERNAME_TOO_LONG
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
const validator = options?.usernameValidator || defaultUsernameValidator;
|
|
236
|
+
const valid = await validator(username2);
|
|
237
|
+
if (!valid) {
|
|
238
|
+
throw new APIError("UNPROCESSABLE_ENTITY", {
|
|
239
|
+
message: ERROR_CODES.INVALID_USERNAME
|
|
240
|
+
});
|
|
241
|
+
}
|
|
186
242
|
const user = await ctx.context.adapter.findOne({
|
|
187
243
|
model: "user",
|
|
188
244
|
where: [
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
var chunkN7UWN6ND_cjs = require('./chunk-N7UWN6ND.cjs');
|
|
4
4
|
var chunkV6HE2MP7_cjs = require('./chunk-V6HE2MP7.cjs');
|
|
5
5
|
var zod = require('zod');
|
|
6
|
-
var
|
|
6
|
+
var jose = require('jose');
|
|
7
7
|
|
|
8
8
|
// src/utils/boolean.ts
|
|
9
9
|
function toBoolean(value) {
|
|
@@ -55,59 +55,80 @@ var oneTap = (options) => ({
|
|
|
55
55
|
},
|
|
56
56
|
async (ctx) => {
|
|
57
57
|
const { idToken } = ctx.body;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
let payload;
|
|
59
|
+
try {
|
|
60
|
+
const JWKS = jose.createRemoteJWKSet(
|
|
61
|
+
new URL("https://www.googleapis.com/oauth2/v3/certs")
|
|
62
|
+
);
|
|
63
|
+
const { payload: verifiedPayload } = await jose.jwtVerify(
|
|
64
|
+
idToken,
|
|
65
|
+
JWKS,
|
|
66
|
+
{
|
|
67
|
+
issuer: ["https://accounts.google.com", "accounts.google.com"],
|
|
68
|
+
audience: options?.clientId || ctx.context.options.socialProviders?.google?.clientId
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
payload = verifiedPayload;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
throw new chunkN7UWN6ND_cjs.APIError("BAD_REQUEST", {
|
|
74
|
+
message: "invalid id token"
|
|
62
75
|
});
|
|
63
76
|
}
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
77
|
+
const { email, email_verified, name, picture, sub } = payload;
|
|
78
|
+
if (!email) {
|
|
79
|
+
return ctx.json({ error: "Email not available in token" });
|
|
80
|
+
}
|
|
81
|
+
const user = await ctx.context.internalAdapter.findUserByEmail(email);
|
|
67
82
|
if (!user) {
|
|
68
83
|
if (options?.disableSignup) {
|
|
69
84
|
throw new chunkN7UWN6ND_cjs.APIError("BAD_GATEWAY", {
|
|
70
85
|
message: "User not found"
|
|
71
86
|
});
|
|
72
87
|
}
|
|
73
|
-
const
|
|
88
|
+
const newUser = await ctx.context.internalAdapter.createOAuthUser(
|
|
74
89
|
{
|
|
75
|
-
email
|
|
76
|
-
emailVerified: toBoolean(
|
|
77
|
-
name
|
|
78
|
-
image:
|
|
90
|
+
email,
|
|
91
|
+
emailVerified: typeof email_verified === "boolean" ? email_verified : toBoolean(email_verified),
|
|
92
|
+
name,
|
|
93
|
+
image: picture
|
|
79
94
|
},
|
|
80
95
|
{
|
|
81
96
|
providerId: "google",
|
|
82
|
-
accountId:
|
|
97
|
+
accountId: sub
|
|
83
98
|
}
|
|
84
99
|
);
|
|
85
|
-
if (!
|
|
100
|
+
if (!newUser) {
|
|
86
101
|
throw new chunkN7UWN6ND_cjs.APIError("INTERNAL_SERVER_ERROR", {
|
|
87
102
|
message: "Could not create user"
|
|
88
103
|
});
|
|
89
104
|
}
|
|
90
105
|
const session2 = await ctx.context.internalAdapter.createSession(
|
|
91
|
-
|
|
106
|
+
newUser.user.id,
|
|
92
107
|
ctx.request
|
|
93
108
|
);
|
|
94
109
|
await chunkV6HE2MP7_cjs.setSessionCookie(ctx, {
|
|
95
|
-
user:
|
|
110
|
+
user: newUser.user,
|
|
96
111
|
session: session2
|
|
97
112
|
});
|
|
98
113
|
return ctx.json({
|
|
99
114
|
token: session2.token,
|
|
100
115
|
user: {
|
|
101
|
-
id:
|
|
102
|
-
email:
|
|
103
|
-
emailVerified:
|
|
104
|
-
name:
|
|
105
|
-
image:
|
|
106
|
-
createdAt:
|
|
107
|
-
updatedAt:
|
|
116
|
+
id: newUser.user.id,
|
|
117
|
+
email: newUser.user.email,
|
|
118
|
+
emailVerified: newUser.user.emailVerified,
|
|
119
|
+
name: newUser.user.name,
|
|
120
|
+
image: newUser.user.image,
|
|
121
|
+
createdAt: newUser.user.createdAt,
|
|
122
|
+
updatedAt: newUser.user.updatedAt
|
|
108
123
|
}
|
|
109
124
|
});
|
|
110
125
|
}
|
|
126
|
+
const account = await ctx.context.internalAdapter.findAccount(sub);
|
|
127
|
+
if (!account) {
|
|
128
|
+
throw new chunkN7UWN6ND_cjs.APIError("UNAUTHORIZED", {
|
|
129
|
+
message: "Google sub doesn't match"
|
|
130
|
+
});
|
|
131
|
+
}
|
|
111
132
|
const session = await ctx.context.internalAdapter.createSession(
|
|
112
133
|
user.user.id,
|
|
113
134
|
ctx.request
|