better-auth 1.2.0-beta.6 → 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/drizzle.d.cts +1 -1
- package/dist/adapters/drizzle.d.ts +1 -1
- package/dist/adapters/kysely.d.cts +1 -1
- package/dist/adapters/kysely.d.ts +1 -1
- package/dist/adapters/memory.d.cts +1 -1
- package/dist/adapters/memory.d.ts +1 -1
- package/dist/adapters/mongodb.cjs +16 -3
- package/dist/adapters/mongodb.d.cts +1 -1
- package/dist/adapters/mongodb.d.ts +1 -1
- package/dist/adapters/mongodb.js +16 -3
- package/dist/adapters/prisma.d.cts +1 -1
- package/dist/adapters/prisma.d.ts +1 -1
- package/dist/adapters/test.d.cts +1 -1
- package/dist/adapters/test.d.ts +1 -1
- package/dist/api.d.cts +2 -2
- package/dist/api.d.ts +2 -2
- package/dist/{auth-CC4kpwks.d.cts → auth-BLwS7t0V.d.cts} +7266 -3010
- package/dist/{auth-BfbTouIV.d.ts → auth-C42JqOR9.d.ts} +7266 -3010
- 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 +44 -6
- package/dist/client/plugins.d.ts +44 -6
- package/dist/client/plugins.js +45 -12
- package/dist/client.d.cts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/cookies.d.cts +1 -1
- package/dist/cookies.d.ts +1 -1
- package/dist/db.d.cts +2 -2
- package/dist/db.d.ts +2 -2
- package/dist/index-Bszx4yMW.d.cts +7755 -0
- package/dist/index-DbIhOM3X.d.ts +7755 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/next-js.cjs +5 -5
- package/dist/next-js.d.cts +1 -1
- package/dist/next-js.d.ts +1 -1
- package/dist/next-js.js +5 -5
- package/dist/node.d.cts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/oauth2.d.cts +2 -2
- package/dist/oauth2.d.ts +2 -2
- package/dist/plugins/admin.cjs +2 -2
- package/dist/plugins/admin.d.cts +79 -25
- package/dist/plugins/admin.d.ts +79 -25
- package/dist/plugins/admin.js +1 -1
- package/dist/plugins/anonymous.d.cts +7 -3
- package/dist/plugins/anonymous.d.ts +7 -3
- package/dist/plugins/bearer.d.cts +1 -1
- package/dist/plugins/bearer.d.ts +1 -1
- package/dist/plugins/custom-session.d.cts +7 -3
- package/dist/plugins/custom-session.d.ts +7 -3
- package/dist/plugins/email-otp.d.cts +43 -15
- package/dist/plugins/email-otp.d.ts +43 -15
- package/dist/plugins/generic-oauth.d.cts +19 -7
- package/dist/plugins/generic-oauth.d.ts +19 -7
- package/dist/plugins/jwt.d.cts +13 -5
- package/dist/plugins/jwt.d.ts +13 -5
- package/dist/plugins/magic-link.d.cts +12 -4
- package/dist/plugins/magic-link.d.ts +12 -4
- package/dist/plugins/multi-session.d.cts +19 -7
- package/dist/plugins/multi-session.d.ts +19 -7
- package/dist/plugins/oidc-provider.cjs +2 -2
- package/dist/plugins/oidc-provider.d.cts +43 -15
- package/dist/plugins/oidc-provider.d.ts +43 -15
- package/dist/plugins/oidc-provider.js +1 -1
- package/dist/plugins/one-tap.cjs +2 -2
- package/dist/plugins/one-tap.d.cts +13 -2
- package/dist/plugins/one-tap.d.ts +13 -2
- package/dist/plugins/one-tap.js +1 -1
- package/dist/plugins/open-api.cjs +2 -2
- package/dist/plugins/open-api.d.cts +13 -5
- package/dist/plugins/open-api.d.ts +13 -5
- package/dist/plugins/open-api.js +1 -1
- package/dist/plugins/organization.d.cts +2 -2
- package/dist/plugins/organization.d.ts +2 -2
- package/dist/plugins/passkey.d.cts +43 -15
- package/dist/plugins/passkey.d.ts +43 -15
- package/dist/plugins/phone-number.d.cts +31 -11
- package/dist/plugins/phone-number.d.ts +31 -11
- package/dist/plugins/sso.d.cts +19 -7
- package/dist/plugins/sso.d.ts +19 -7
- package/dist/plugins/two-factor.d.cts +61 -21
- package/dist/plugins/two-factor.d.ts +61 -21
- package/dist/plugins/username.cjs +2 -2
- package/dist/plugins/username.d.cts +28 -4
- package/dist/plugins/username.d.ts +28 -4
- package/dist/plugins/username.js +1 -1
- package/dist/plugins.cjs +10 -10
- package/dist/plugins.d.cts +9 -5
- package/dist/plugins.d.ts +9 -5
- package/dist/plugins.js +5 -5
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/solid.d.cts +1 -1
- package/dist/solid.d.ts +1 -1
- package/dist/{state-BlyN0kTT.d.ts → state-BlQHfd32.d.ts} +1 -1
- package/dist/{state-ClV1x8my.d.cts → state-lA7cG1Em.d.cts} +1 -1
- package/dist/svelte-kit.d.cts +1 -1
- package/dist/svelte-kit.d.ts +1 -1
- package/dist/svelte.d.cts +1 -1
- package/dist/svelte.d.ts +1 -1
- package/dist/types.d.cts +2 -2
- package/dist/types.d.ts +2 -2
- package/dist/vue.d.cts +1 -1
- package/dist/vue.d.ts +1 -1
- package/package.json +3 -3
- package/dist/index-CtbEb8Ls.d.ts +0 -4211
- package/dist/index-F5NLq5XF.d.cts +0 -4211
|
@@ -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
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createAuthEndpoint, APIError } from './chunk-SCXZXQTZ.js';
|
|
2
2
|
import { setSessionCookie } from './chunk-QQGZ3XGI.js';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import {
|
|
4
|
+
import { createRemoteJWKSet, jwtVerify } from 'jose';
|
|
5
5
|
|
|
6
6
|
// src/utils/boolean.ts
|
|
7
7
|
function toBoolean(value) {
|
|
@@ -53,59 +53,80 @@ var oneTap = (options) => ({
|
|
|
53
53
|
},
|
|
54
54
|
async (ctx) => {
|
|
55
55
|
const { idToken } = ctx.body;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
let payload;
|
|
57
|
+
try {
|
|
58
|
+
const JWKS = createRemoteJWKSet(
|
|
59
|
+
new URL("https://www.googleapis.com/oauth2/v3/certs")
|
|
60
|
+
);
|
|
61
|
+
const { payload: verifiedPayload } = await jwtVerify(
|
|
62
|
+
idToken,
|
|
63
|
+
JWKS,
|
|
64
|
+
{
|
|
65
|
+
issuer: ["https://accounts.google.com", "accounts.google.com"],
|
|
66
|
+
audience: options?.clientId || ctx.context.options.socialProviders?.google?.clientId
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
payload = verifiedPayload;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
throw new APIError("BAD_REQUEST", {
|
|
72
|
+
message: "invalid id token"
|
|
60
73
|
});
|
|
61
74
|
}
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
75
|
+
const { email, email_verified, name, picture, sub } = payload;
|
|
76
|
+
if (!email) {
|
|
77
|
+
return ctx.json({ error: "Email not available in token" });
|
|
78
|
+
}
|
|
79
|
+
const user = await ctx.context.internalAdapter.findUserByEmail(email);
|
|
65
80
|
if (!user) {
|
|
66
81
|
if (options?.disableSignup) {
|
|
67
82
|
throw new APIError("BAD_GATEWAY", {
|
|
68
83
|
message: "User not found"
|
|
69
84
|
});
|
|
70
85
|
}
|
|
71
|
-
const
|
|
86
|
+
const newUser = await ctx.context.internalAdapter.createOAuthUser(
|
|
72
87
|
{
|
|
73
|
-
email
|
|
74
|
-
emailVerified: toBoolean(
|
|
75
|
-
name
|
|
76
|
-
image:
|
|
88
|
+
email,
|
|
89
|
+
emailVerified: typeof email_verified === "boolean" ? email_verified : toBoolean(email_verified),
|
|
90
|
+
name,
|
|
91
|
+
image: picture
|
|
77
92
|
},
|
|
78
93
|
{
|
|
79
94
|
providerId: "google",
|
|
80
|
-
accountId:
|
|
95
|
+
accountId: sub
|
|
81
96
|
}
|
|
82
97
|
);
|
|
83
|
-
if (!
|
|
98
|
+
if (!newUser) {
|
|
84
99
|
throw new APIError("INTERNAL_SERVER_ERROR", {
|
|
85
100
|
message: "Could not create user"
|
|
86
101
|
});
|
|
87
102
|
}
|
|
88
103
|
const session2 = await ctx.context.internalAdapter.createSession(
|
|
89
|
-
|
|
104
|
+
newUser.user.id,
|
|
90
105
|
ctx.request
|
|
91
106
|
);
|
|
92
107
|
await setSessionCookie(ctx, {
|
|
93
|
-
user:
|
|
108
|
+
user: newUser.user,
|
|
94
109
|
session: session2
|
|
95
110
|
});
|
|
96
111
|
return ctx.json({
|
|
97
112
|
token: session2.token,
|
|
98
113
|
user: {
|
|
99
|
-
id:
|
|
100
|
-
email:
|
|
101
|
-
emailVerified:
|
|
102
|
-
name:
|
|
103
|
-
image:
|
|
104
|
-
createdAt:
|
|
105
|
-
updatedAt:
|
|
114
|
+
id: newUser.user.id,
|
|
115
|
+
email: newUser.user.email,
|
|
116
|
+
emailVerified: newUser.user.emailVerified,
|
|
117
|
+
name: newUser.user.name,
|
|
118
|
+
image: newUser.user.image,
|
|
119
|
+
createdAt: newUser.user.createdAt,
|
|
120
|
+
updatedAt: newUser.user.updatedAt
|
|
106
121
|
}
|
|
107
122
|
});
|
|
108
123
|
}
|
|
124
|
+
const account = await ctx.context.internalAdapter.findAccount(sub);
|
|
125
|
+
if (!account) {
|
|
126
|
+
throw new APIError("UNAUTHORIZED", {
|
|
127
|
+
message: "Google sub doesn't match"
|
|
128
|
+
});
|
|
129
|
+
}
|
|
109
130
|
const session = await ctx.context.internalAdapter.createSession(
|
|
110
131
|
user.user.id,
|
|
111
132
|
ctx.request
|
package/dist/client/plugins.cjs
CHANGED
|
@@ -8,6 +8,7 @@ var chunkKIQWU6Z4_cjs = require('../chunk-KIQWU6Z4.cjs');
|
|
|
8
8
|
require('../chunk-U4I57HJ4.cjs');
|
|
9
9
|
require('../chunk-S5UORXJH.cjs');
|
|
10
10
|
require('../chunk-CCKQSGIR.cjs');
|
|
11
|
+
var chunkG2LZ73E2_cjs = require('../chunk-G2LZ73E2.cjs');
|
|
11
12
|
require('../chunk-VXYIYABQ.cjs');
|
|
12
13
|
var chunkPEZRSDZS_cjs = require('../chunk-PEZRSDZS.cjs');
|
|
13
14
|
var nanostores = require('nanostores');
|
|
@@ -235,26 +236,58 @@ var oneTapClient = (options) => {
|
|
|
235
236
|
const { autoSelect, cancelOnTapOutside, context } = opts ?? {};
|
|
236
237
|
const contextValue = context ?? options.context ?? "signin";
|
|
237
238
|
await loadGoogleScript();
|
|
238
|
-
await new Promise((resolve) => {
|
|
239
|
+
await new Promise((resolve, reject) => {
|
|
240
|
+
let isResolved = false;
|
|
241
|
+
const baseDelay = options.promptOptions?.baseDelay ?? 1e3;
|
|
242
|
+
const maxAttempts = options.promptOptions?.maxAttempts ?? 5;
|
|
239
243
|
window.google?.accounts.id.initialize({
|
|
240
244
|
client_id: options.clientId,
|
|
241
245
|
callback: async (response) => {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
246
|
+
isResolved = true;
|
|
247
|
+
try {
|
|
248
|
+
await $fetch("/one-tap/callback", {
|
|
249
|
+
method: "POST",
|
|
250
|
+
body: { idToken: response.credential },
|
|
251
|
+
...opts?.fetchOptions,
|
|
252
|
+
...fetchOptions
|
|
253
|
+
});
|
|
254
|
+
if (!opts?.fetchOptions && !fetchOptions || opts?.callbackURL) {
|
|
255
|
+
window.location.href = opts?.callbackURL ?? "/";
|
|
256
|
+
}
|
|
257
|
+
resolve();
|
|
258
|
+
} catch (error) {
|
|
259
|
+
console.error("Error during One Tap callback:", error);
|
|
260
|
+
reject(error);
|
|
250
261
|
}
|
|
251
|
-
resolve();
|
|
252
262
|
},
|
|
253
263
|
auto_select: autoSelect,
|
|
254
264
|
cancel_on_tap_outside: cancelOnTapOutside,
|
|
255
|
-
context: contextValue
|
|
265
|
+
context: contextValue,
|
|
266
|
+
nonce: chunkG2LZ73E2_cjs.generateRandomString(16),
|
|
267
|
+
...options.additionalOptions
|
|
256
268
|
});
|
|
257
|
-
|
|
269
|
+
const handlePrompt = (attempt) => {
|
|
270
|
+
if (isResolved) return;
|
|
271
|
+
window.google?.accounts.id.prompt((notification) => {
|
|
272
|
+
if (isResolved) return;
|
|
273
|
+
if (notification.isDismissedMoment && notification.isDismissedMoment()) {
|
|
274
|
+
if (attempt < maxAttempts) {
|
|
275
|
+
const delay = Math.pow(2, attempt) * baseDelay;
|
|
276
|
+
setTimeout(() => handlePrompt(attempt + 1), delay);
|
|
277
|
+
} else {
|
|
278
|
+
opts?.onPromptNotification?.(notification);
|
|
279
|
+
}
|
|
280
|
+
} else if (notification.isSkippedMoment && notification.isSkippedMoment()) {
|
|
281
|
+
if (attempt < maxAttempts) {
|
|
282
|
+
const delay = Math.pow(2, attempt) * baseDelay;
|
|
283
|
+
setTimeout(() => handlePrompt(attempt + 1), delay);
|
|
284
|
+
} else {
|
|
285
|
+
opts?.onPromptNotification?.(notification);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
};
|
|
290
|
+
handlePrompt(0);
|
|
258
291
|
});
|
|
259
292
|
} catch (error) {
|
|
260
293
|
console.error("Error during Google One Tap flow:", error);
|