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.
Files changed (41) hide show
  1. package/dist/adapters/mongodb.cjs +16 -3
  2. package/dist/adapters/mongodb.js +16 -3
  3. package/dist/{chunk-XKCN2VRG.cjs → chunk-45RBYQKI.cjs} +58 -2
  4. package/dist/{chunk-KAEQXMEV.cjs → chunk-GSEEAIRZ.cjs} +1 -1
  5. package/dist/{chunk-RMPI2YZ3.js → chunk-IQUHEVLH.js} +1 -1
  6. package/dist/{chunk-5AOJMBJI.js → chunk-J6VNT2T4.js} +8 -0
  7. package/dist/{chunk-75SPUXMY.cjs → chunk-QNLSMTXF.cjs} +9 -4
  8. package/dist/{chunk-GWJDRQJ3.js → chunk-QQGRNAM2.js} +9 -4
  9. package/dist/{chunk-OALLKKNX.cjs → chunk-SMZIOWVN.cjs} +8 -0
  10. package/dist/{chunk-FX2ZHENM.js → chunk-TZ34QTAV.js} +58 -2
  11. package/dist/{chunk-LIGJVOZ4.cjs → chunk-WLOSPOQJ.cjs} +45 -24
  12. package/dist/{chunk-V56HFQQX.js → chunk-YHW4KODD.js} +45 -24
  13. package/dist/client/plugins.cjs +45 -12
  14. package/dist/client/plugins.d.cts +42 -4
  15. package/dist/client/plugins.d.ts +42 -4
  16. package/dist/client/plugins.js +45 -12
  17. package/dist/index.cjs +1 -1
  18. package/dist/index.js +1 -1
  19. package/dist/next-js.cjs +5 -5
  20. package/dist/next-js.js +5 -5
  21. package/dist/plugins/admin.cjs +2 -2
  22. package/dist/plugins/admin.d.cts +6 -0
  23. package/dist/plugins/admin.d.ts +6 -0
  24. package/dist/plugins/admin.js +1 -1
  25. package/dist/plugins/oidc-provider.cjs +2 -2
  26. package/dist/plugins/oidc-provider.d.cts +1 -1
  27. package/dist/plugins/oidc-provider.d.ts +1 -1
  28. package/dist/plugins/oidc-provider.js +1 -1
  29. package/dist/plugins/one-tap.cjs +2 -2
  30. package/dist/plugins/one-tap.d.cts +7 -0
  31. package/dist/plugins/one-tap.d.ts +7 -0
  32. package/dist/plugins/one-tap.js +1 -1
  33. package/dist/plugins/open-api.cjs +2 -2
  34. package/dist/plugins/open-api.js +1 -1
  35. package/dist/plugins/username.cjs +2 -2
  36. package/dist/plugins/username.d.cts +21 -1
  37. package/dist/plugins/username.d.ts +21 -1
  38. package/dist/plugins/username.js +1 -1
  39. package/dist/plugins.cjs +10 -10
  40. package/dist/plugins.js +5 -5
  41. 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 = { ...transformedData, id: id.toString() };
205
+ const insertedData = { id: id.toString(), ...transformedData };
193
206
  const t = transform.transformOutput(insertedData, model, select);
194
207
  return t;
195
208
  },
@@ -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 = { ...transformedData, id: id.toString() };
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
- userInfo_endpoint: `${baseURL}/oauth2/userinfo`,
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
- userInfo_endpoint: `${baseURL}/oauth2/userinfo`,
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[value.path] = {
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[value.path] = {
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[value.path] = {
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[value.path] = {
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[value.path] = {
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[value.path] = {
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[value.path] = {
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[value.path] = {
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 fetch = require('@better-fetch/fetch');
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
- const { data, error } = await fetch.betterFetch("https://oauth2.googleapis.com/tokeninfo?id_token=" + idToken);
59
- if (error) {
60
- return ctx.json({
61
- error: "Invalid token"
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 user = await ctx.context.internalAdapter.findUserByEmail(
65
- data.email
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 user2 = await ctx.context.internalAdapter.createOAuthUser(
88
+ const newUser = await ctx.context.internalAdapter.createOAuthUser(
74
89
  {
75
- email: data.email,
76
- emailVerified: toBoolean(data.email_verified),
77
- name: data.name,
78
- image: data.picture
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: data.sub
97
+ accountId: sub
83
98
  }
84
99
  );
85
- if (!user2) {
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
- user2?.user.id,
106
+ newUser.user.id,
92
107
  ctx.request
93
108
  );
94
109
  await chunkV6HE2MP7_cjs.setSessionCookie(ctx, {
95
- user: user2.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: user2.user.id,
102
- email: user2.user.email,
103
- emailVerified: user2.user.emailVerified,
104
- name: user2.user.name,
105
- image: user2.user.image,
106
- createdAt: user2.user.createdAt,
107
- updatedAt: user2.user.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