better-auth 0.0.8-beta.2 → 0.0.8-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access.d.ts +3 -4
- package/dist/access.js +3 -13
- package/dist/access.js.map +1 -1
- package/dist/cli.d.ts +1 -2
- package/dist/cli.js +36 -44
- package/dist/cli.js.map +1 -1
- package/dist/client/plugins.d.ts +97 -2407
- package/dist/client/plugins.js +143 -203
- package/dist/client/plugins.js.map +1 -1
- package/dist/client.d.ts +159 -1213
- package/dist/client.js +125 -527
- package/dist/client.js.map +1 -1
- package/dist/{helper-B5_2Vzba.d.ts → helper-D8dhRz72.d.ts} +1 -4
- package/dist/{index-Dg4eEXZW.d.ts → index-B9jOjqnF.d.ts} +1 -1
- package/dist/{schema-BOszzrbQ.d.ts → index-CcxejJTH.d.ts} +172 -142
- package/dist/{client-CaF9eUcv.d.ts → index-Dwhjsk4l.d.ts} +2014 -1971
- package/dist/index.d.ts +1124 -6
- package/dist/index.js +737 -707
- package/dist/index.js.map +1 -1
- package/dist/internal-adapter-CVKQ4XR9.d.ts +637 -0
- package/dist/next-js.d.ts +17 -7
- package/dist/next-js.js +20 -3
- package/dist/next-js.js.map +1 -1
- package/dist/plugins.d.ts +12 -883
- package/dist/plugins.js +743 -679
- package/dist/plugins.js.map +1 -1
- package/dist/react.d.ts +312 -12
- package/dist/react.js +138 -148
- package/dist/react.js.map +1 -1
- package/dist/social.d.ts +2 -2
- package/dist/social.js +179 -151
- package/dist/social.js.map +1 -1
- package/dist/solid-start.d.ts +7 -6
- package/dist/solid-start.js +3 -3
- package/dist/solid-start.js.map +1 -1
- package/dist/solid.d.ts +91 -2713
- package/dist/solid.js +130 -139
- package/dist/solid.js.map +1 -1
- package/dist/{statement-COylZd3J.d.ts → statement-D6SPoYOh.d.ts} +7 -7
- package/dist/svelte-kit.d.ts +6 -5
- package/dist/svelte-kit.js +10 -9
- package/dist/svelte-kit.js.map +1 -1
- package/dist/svelte.d.ts +89 -2713
- package/dist/svelte.js +124 -138
- package/dist/svelte.js.map +1 -1
- package/dist/types-D4WrjKeJ.d.ts +81 -0
- package/dist/types.d.ts +31 -5
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -1
- package/dist/vue.d.ts +313 -12
- package/dist/vue.js +131 -145
- package/dist/vue.js.map +1 -1
- package/package.json +8 -3
- package/dist/index-CGeV0d2g.d.ts +0 -1498
- package/dist/preact.d.ts +0 -8
- package/dist/preact.js +0 -291
- package/dist/preact.js.map +0 -1
- package/dist/type-tYx_kmry.d.ts +0 -5724
package/dist/index.js
CHANGED
|
@@ -1,89 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
});
|
|
21
|
-
var userSchema = z.object({
|
|
22
|
-
id: z.string(),
|
|
23
|
-
email: z.string().transform((val) => val.toLowerCase()),
|
|
24
|
-
emailVerified: z.boolean().default(false),
|
|
25
|
-
name: z.string(),
|
|
26
|
-
image: z.string().optional(),
|
|
27
|
-
createdAt: z.date().default(/* @__PURE__ */ new Date()),
|
|
28
|
-
updatedAt: z.date().default(/* @__PURE__ */ new Date())
|
|
29
|
-
});
|
|
30
|
-
var sessionSchema = z.object({
|
|
31
|
-
id: z.string(),
|
|
32
|
-
userId: z.string(),
|
|
33
|
-
expiresAt: z.date(),
|
|
34
|
-
ipAddress: z.string().optional(),
|
|
35
|
-
userAgent: z.string().optional()
|
|
36
|
-
});
|
|
37
|
-
function parseData(data, schema) {
|
|
38
|
-
const fields = schema.fields;
|
|
39
|
-
const parsedData = {};
|
|
40
|
-
for (const key in data) {
|
|
41
|
-
const field = fields[key];
|
|
42
|
-
if (!field) {
|
|
43
|
-
parsedData[key] = data[key];
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
if (field.returned === false) {
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
parsedData[key] = data[key];
|
|
50
|
-
}
|
|
51
|
-
return parsedData;
|
|
52
|
-
}
|
|
53
|
-
function getAllFields(options, table) {
|
|
54
|
-
let schema = {};
|
|
55
|
-
for (const plugin of options.plugins || []) {
|
|
56
|
-
if (plugin.schema && plugin.schema[table]) {
|
|
57
|
-
schema = {
|
|
58
|
-
...schema,
|
|
59
|
-
...plugin.schema[table].fields
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return schema;
|
|
64
|
-
}
|
|
65
|
-
function parseUser(options, user) {
|
|
66
|
-
const schema = getAllFields(options, "user");
|
|
67
|
-
return parseData(user, { fields: schema });
|
|
68
|
-
}
|
|
69
|
-
function parseAccount(options, account) {
|
|
70
|
-
const schema = getAllFields(options, "account");
|
|
71
|
-
return parseData(account, { fields: schema });
|
|
72
|
-
}
|
|
73
|
-
function parseSession(options, session) {
|
|
74
|
-
const schema = getAllFields(options, "session");
|
|
75
|
-
return parseData(session, { fields: schema });
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// src/api/middlewares/csrf.ts
|
|
79
|
-
import { APIError } from "better-call";
|
|
80
|
-
import { z as z2 } from "zod";
|
|
1
|
+
import { createMiddleware, createMiddlewareCreator, createEndpointCreator, APIError, createRouter } from 'better-call';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import '@noble/ciphers/chacha';
|
|
4
|
+
import '@noble/ciphers/utils';
|
|
5
|
+
import '@noble/ciphers/webcrypto';
|
|
6
|
+
import '@noble/hashes/sha256';
|
|
7
|
+
import { generateCodeVerifier, generateState as generateState$1 } from 'oslo/oauth2';
|
|
8
|
+
import { Discord, Facebook, GitHub, Google, Spotify, Twitch, Twitter } from 'arctic';
|
|
9
|
+
import { createJWT, validateJWT, parseJWT } from 'oslo/jwt';
|
|
10
|
+
import { betterFetch } from '@better-fetch/fetch';
|
|
11
|
+
import { createOAuth2Request, sendTokenRequest } from 'arctic/dist/request';
|
|
12
|
+
import { createConsola } from 'consola';
|
|
13
|
+
import { TimeSpan } from 'oslo';
|
|
14
|
+
import { generateRandomString, alphabet } from 'oslo/crypto';
|
|
15
|
+
import Database from 'better-sqlite3';
|
|
16
|
+
import { Kysely, MysqlDialect, PostgresDialect, SqliteDialect } from 'kysely';
|
|
17
|
+
import { createPool } from 'mysql2';
|
|
18
|
+
import pg from 'pg';
|
|
19
|
+
import * as argon2 from 'argon2';
|
|
81
20
|
|
|
82
|
-
// src/
|
|
83
|
-
import { xchacha20poly1305 } from "@noble/ciphers/chacha";
|
|
84
|
-
import { bytesToHex, hexToBytes, utf8ToBytes } from "@noble/ciphers/utils";
|
|
85
|
-
import { managedNonce } from "@noble/ciphers/webcrypto";
|
|
86
|
-
import { sha256 } from "@noble/hashes/sha256";
|
|
21
|
+
// src/api/index.ts
|
|
87
22
|
async function hs256(secretKey, message) {
|
|
88
23
|
const enc = new TextEncoder();
|
|
89
24
|
const algorithm = { name: "HMAC", hash: "SHA-256" };
|
|
@@ -101,18 +36,20 @@ async function hs256(secretKey, message) {
|
|
|
101
36
|
);
|
|
102
37
|
return btoa(String.fromCharCode(...new Uint8Array(signature)));
|
|
103
38
|
}
|
|
104
|
-
|
|
105
|
-
// src/api/call.ts
|
|
106
|
-
import {
|
|
107
|
-
createEndpointCreator,
|
|
108
|
-
createMiddleware,
|
|
109
|
-
createMiddlewareCreator
|
|
110
|
-
} from "better-call";
|
|
111
39
|
var optionsMiddleware = createMiddleware(async () => {
|
|
112
40
|
return {};
|
|
113
41
|
});
|
|
114
42
|
var createAuthMiddleware = createMiddlewareCreator({
|
|
115
|
-
use: [
|
|
43
|
+
use: [
|
|
44
|
+
optionsMiddleware,
|
|
45
|
+
/**
|
|
46
|
+
* This of for hooks. to tell ts there will a
|
|
47
|
+
* return response object
|
|
48
|
+
*/
|
|
49
|
+
createMiddleware(async () => {
|
|
50
|
+
return {};
|
|
51
|
+
})
|
|
52
|
+
]
|
|
116
53
|
});
|
|
117
54
|
var createAuthEndpoint = createEndpointCreator({
|
|
118
55
|
use: [optionsMiddleware]
|
|
@@ -121,8 +58,8 @@ var createAuthEndpoint = createEndpointCreator({
|
|
|
121
58
|
// src/api/middlewares/csrf.ts
|
|
122
59
|
var csrfMiddleware = createAuthMiddleware(
|
|
123
60
|
{
|
|
124
|
-
body:
|
|
125
|
-
csrfToken:
|
|
61
|
+
body: z.object({
|
|
62
|
+
csrfToken: z.string().optional()
|
|
126
63
|
}).optional()
|
|
127
64
|
},
|
|
128
65
|
async (ctx) => {
|
|
@@ -130,8 +67,7 @@ var csrfMiddleware = createAuthMiddleware(
|
|
|
130
67
|
return;
|
|
131
68
|
}
|
|
132
69
|
const url = new URL(ctx.request.url);
|
|
133
|
-
|
|
134
|
-
if (url.origin === ctx.context.options.baseURL || ctx.context.options.trustedOrigins?.includes(url.origin)) {
|
|
70
|
+
if (url.origin === new URL(ctx.context.baseURL).origin || ctx.context.options.trustedOrigins?.includes(url.origin)) {
|
|
135
71
|
return;
|
|
136
72
|
}
|
|
137
73
|
const csrfToken = ctx.body?.csrfToken;
|
|
@@ -139,8 +75,8 @@ var csrfMiddleware = createAuthMiddleware(
|
|
|
139
75
|
ctx.context.authCookies.csrfToken.name,
|
|
140
76
|
ctx.context.secret
|
|
141
77
|
);
|
|
142
|
-
const [token,
|
|
143
|
-
if (!csrfToken || !csrfCookie || !token || !
|
|
78
|
+
const [token, hash2] = csrfCookie?.split("!") || [null, null];
|
|
79
|
+
if (!csrfToken || !csrfCookie || !token || !hash2 || csrfCookie !== csrfToken) {
|
|
144
80
|
ctx.setCookie(ctx.context.authCookies.csrfToken.name, "", {
|
|
145
81
|
maxAge: 0
|
|
146
82
|
});
|
|
@@ -149,7 +85,7 @@ var csrfMiddleware = createAuthMiddleware(
|
|
|
149
85
|
});
|
|
150
86
|
}
|
|
151
87
|
const expectedHash = await hs256(ctx.context.secret, token);
|
|
152
|
-
if (
|
|
88
|
+
if (hash2 !== expectedHash) {
|
|
153
89
|
ctx.setCookie(ctx.context.authCookies.csrfToken.name, "", {
|
|
154
90
|
maxAge: 0
|
|
155
91
|
});
|
|
@@ -160,17 +96,6 @@ var csrfMiddleware = createAuthMiddleware(
|
|
|
160
96
|
}
|
|
161
97
|
);
|
|
162
98
|
|
|
163
|
-
// src/api/routes/sign-in.ts
|
|
164
|
-
import { APIError as APIError2 } from "better-call";
|
|
165
|
-
import { generateCodeVerifier } from "oslo/oauth2";
|
|
166
|
-
import { Argon2id } from "oslo/password";
|
|
167
|
-
import { z as z3 } from "zod";
|
|
168
|
-
|
|
169
|
-
// src/social-providers/apple.ts
|
|
170
|
-
import "arctic";
|
|
171
|
-
import { parseJWT } from "oslo/jwt";
|
|
172
|
-
import { betterFetch } from "@better-fetch/fetch";
|
|
173
|
-
|
|
174
99
|
// src/error/better-auth-error.ts
|
|
175
100
|
var BetterAuthError = class extends Error {
|
|
176
101
|
constructor(message) {
|
|
@@ -191,41 +116,47 @@ function checkHasPath(url) {
|
|
|
191
116
|
function withPath(url, path = "/api/auth") {
|
|
192
117
|
const hasPath = checkHasPath(url);
|
|
193
118
|
if (hasPath) {
|
|
194
|
-
return
|
|
195
|
-
baseURL: new URL(url).origin,
|
|
196
|
-
withPath: url
|
|
197
|
-
};
|
|
119
|
+
return url;
|
|
198
120
|
}
|
|
199
121
|
path = path.startsWith("/") ? path : `/${path}`;
|
|
200
|
-
return {
|
|
201
|
-
baseURL: url,
|
|
202
|
-
withPath: `${url}${path}`
|
|
203
|
-
};
|
|
122
|
+
return `${url}${path}`;
|
|
204
123
|
}
|
|
205
124
|
function getBaseURL(url, path) {
|
|
206
125
|
if (url) {
|
|
207
126
|
return withPath(url, path);
|
|
208
127
|
}
|
|
209
128
|
const env = typeof process !== "undefined" ? process.env : {};
|
|
210
|
-
const fromEnv = env.BETTER_AUTH_URL || env.
|
|
129
|
+
const fromEnv = env.BETTER_AUTH_URL || env.NEXT_PUBLIC_BETTER_AUTH_URL || env.PUBLIC_BETTER_AUTH_URL || env.NUXT_PUBLIC_BETTER_AUTH_URL || env.NUXT_PUBLIC_AUTH_URL;
|
|
211
130
|
if (fromEnv) {
|
|
212
131
|
return withPath(fromEnv, path);
|
|
213
132
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
return {
|
|
217
|
-
baseURL: "http://localhost:3000",
|
|
218
|
-
withPath: "http://localhost:3000/api/auth"
|
|
219
|
-
};
|
|
133
|
+
if (typeof window !== "undefined") {
|
|
134
|
+
return withPath(window.location.origin, path);
|
|
220
135
|
}
|
|
221
|
-
|
|
222
|
-
"Could not infer baseURL from environment variables"
|
|
223
|
-
);
|
|
136
|
+
return void 0;
|
|
224
137
|
}
|
|
225
138
|
|
|
226
139
|
// src/social-providers/utils.ts
|
|
227
140
|
function getRedirectURI(providerId, redirectURI) {
|
|
228
|
-
return redirectURI || `${getBaseURL()}/
|
|
141
|
+
return redirectURI || `${getBaseURL()}/callback/${providerId}`;
|
|
142
|
+
}
|
|
143
|
+
async function validateAuthorizationCode({
|
|
144
|
+
code,
|
|
145
|
+
codeVerifier,
|
|
146
|
+
redirectURI,
|
|
147
|
+
options,
|
|
148
|
+
tokenEndpoint
|
|
149
|
+
}) {
|
|
150
|
+
const body = new URLSearchParams();
|
|
151
|
+
body.set("grant_type", "authorization_code");
|
|
152
|
+
body.set("code", code);
|
|
153
|
+
body.set("code_verifier", codeVerifier || "");
|
|
154
|
+
body.set("redirect_uri", redirectURI);
|
|
155
|
+
body.set("client_id", options.clientId);
|
|
156
|
+
body.set("client_secret", options.clientSecret);
|
|
157
|
+
const request = createOAuth2Request(tokenEndpoint, body);
|
|
158
|
+
const tokens = await sendTokenRequest(request);
|
|
159
|
+
return tokens;
|
|
229
160
|
}
|
|
230
161
|
|
|
231
162
|
// src/social-providers/apple.ts
|
|
@@ -282,19 +213,11 @@ var apple = ({
|
|
|
282
213
|
}
|
|
283
214
|
};
|
|
284
215
|
};
|
|
285
|
-
|
|
286
|
-
// src/social-providers/discord.ts
|
|
287
|
-
import { betterFetch as betterFetch2 } from "@better-fetch/fetch";
|
|
288
|
-
import { Discord } from "arctic";
|
|
289
|
-
var discord = ({
|
|
290
|
-
clientId,
|
|
291
|
-
clientSecret,
|
|
292
|
-
redirectURI
|
|
293
|
-
}) => {
|
|
216
|
+
var discord = (options) => {
|
|
294
217
|
const discordArctic = new Discord(
|
|
295
|
-
clientId,
|
|
296
|
-
clientSecret,
|
|
297
|
-
getRedirectURI("discord", redirectURI)
|
|
218
|
+
options.clientId,
|
|
219
|
+
options.clientSecret,
|
|
220
|
+
getRedirectURI("discord", options.redirectURI)
|
|
298
221
|
);
|
|
299
222
|
return {
|
|
300
223
|
id: "discord",
|
|
@@ -303,14 +226,22 @@ var discord = ({
|
|
|
303
226
|
const _scope = scopes || ["email"];
|
|
304
227
|
return discordArctic.createAuthorizationURL(state, _scope);
|
|
305
228
|
},
|
|
306
|
-
validateAuthorizationCode:
|
|
229
|
+
validateAuthorizationCode: async (code, codeVerifier, redirectURI) => {
|
|
230
|
+
return validateAuthorizationCode({
|
|
231
|
+
code,
|
|
232
|
+
codeVerifier,
|
|
233
|
+
redirectURI: redirectURI || getRedirectURI("discord", options.redirectURI),
|
|
234
|
+
options,
|
|
235
|
+
tokenEndpoint: "https://discord.com/api/oauth2/token"
|
|
236
|
+
});
|
|
237
|
+
},
|
|
307
238
|
async getUserInfo(token) {
|
|
308
|
-
const { data: profile, error: error2 } = await
|
|
239
|
+
const { data: profile, error: error2 } = await betterFetch(
|
|
309
240
|
"https://discord.com/api/users/@me",
|
|
310
241
|
{
|
|
311
242
|
auth: {
|
|
312
243
|
type: "Bearer",
|
|
313
|
-
token: token.accessToken
|
|
244
|
+
token: token.accessToken()
|
|
314
245
|
}
|
|
315
246
|
}
|
|
316
247
|
);
|
|
@@ -329,19 +260,11 @@ var discord = ({
|
|
|
329
260
|
}
|
|
330
261
|
};
|
|
331
262
|
};
|
|
332
|
-
|
|
333
|
-
// src/social-providers/facebook.ts
|
|
334
|
-
import { betterFetch as betterFetch3 } from "@better-fetch/fetch";
|
|
335
|
-
import { Facebook } from "arctic";
|
|
336
|
-
var facebook = ({
|
|
337
|
-
clientId,
|
|
338
|
-
clientSecret,
|
|
339
|
-
redirectURI
|
|
340
|
-
}) => {
|
|
263
|
+
var facebook = (options) => {
|
|
341
264
|
const facebookArctic = new Facebook(
|
|
342
|
-
clientId,
|
|
343
|
-
clientSecret,
|
|
344
|
-
getRedirectURI("facebook", redirectURI)
|
|
265
|
+
options.clientId,
|
|
266
|
+
options.clientSecret,
|
|
267
|
+
getRedirectURI("facebook", options.redirectURI)
|
|
345
268
|
);
|
|
346
269
|
return {
|
|
347
270
|
id: "facebook",
|
|
@@ -350,14 +273,22 @@ var facebook = ({
|
|
|
350
273
|
const _scopes = scopes || ["email", "public_profile"];
|
|
351
274
|
return facebookArctic.createAuthorizationURL(state, _scopes);
|
|
352
275
|
},
|
|
353
|
-
validateAuthorizationCode:
|
|
276
|
+
validateAuthorizationCode: async (code, codeVerifier, redirectURI) => {
|
|
277
|
+
return validateAuthorizationCode({
|
|
278
|
+
code,
|
|
279
|
+
codeVerifier,
|
|
280
|
+
redirectURI: redirectURI || getRedirectURI("facebook", options.redirectURI),
|
|
281
|
+
options,
|
|
282
|
+
tokenEndpoint: "https://graph.facebook.com/v16.0/oauth/access_token"
|
|
283
|
+
});
|
|
284
|
+
},
|
|
354
285
|
async getUserInfo(token) {
|
|
355
|
-
const { data: profile, error: error2 } = await
|
|
286
|
+
const { data: profile, error: error2 } = await betterFetch(
|
|
356
287
|
"https://graph.facebook.com/me",
|
|
357
288
|
{
|
|
358
289
|
auth: {
|
|
359
290
|
type: "Bearer",
|
|
360
|
-
token: token.accessToken
|
|
291
|
+
token: token.accessToken()
|
|
361
292
|
}
|
|
362
293
|
}
|
|
363
294
|
);
|
|
@@ -376,10 +307,6 @@ var facebook = ({
|
|
|
376
307
|
}
|
|
377
308
|
};
|
|
378
309
|
};
|
|
379
|
-
|
|
380
|
-
// src/social-providers/github.ts
|
|
381
|
-
import { betterFetch as betterFetch4 } from "@better-fetch/fetch";
|
|
382
|
-
import { GitHub } from "arctic";
|
|
383
310
|
var github = ({
|
|
384
311
|
clientId,
|
|
385
312
|
clientSecret,
|
|
@@ -397,14 +324,17 @@ var github = ({
|
|
|
397
324
|
const _scopes = scopes || ["user:email"];
|
|
398
325
|
return githubArctic.createAuthorizationURL(state, _scopes);
|
|
399
326
|
},
|
|
400
|
-
validateAuthorizationCode:
|
|
327
|
+
validateAuthorizationCode: async (state) => {
|
|
328
|
+
return await githubArctic.validateAuthorizationCode(state);
|
|
329
|
+
},
|
|
401
330
|
async getUserInfo(token) {
|
|
402
|
-
|
|
331
|
+
console.log(`Bearer ${token.accessToken()}`);
|
|
332
|
+
const { data: profile, error: error2 } = await betterFetch(
|
|
403
333
|
"https://api.github.com/user",
|
|
404
334
|
{
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
335
|
+
auth: {
|
|
336
|
+
type: "Bearer",
|
|
337
|
+
token: token.accessToken()
|
|
408
338
|
}
|
|
409
339
|
}
|
|
410
340
|
);
|
|
@@ -413,10 +343,10 @@ var github = ({
|
|
|
413
343
|
}
|
|
414
344
|
let emailVerified = false;
|
|
415
345
|
if (!profile.email) {
|
|
416
|
-
const { data, error: error3 } = await
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
346
|
+
const { data, error: error3 } = await betterFetch("https://api.github.com/user/emails", {
|
|
347
|
+
auth: {
|
|
348
|
+
type: "Bearer",
|
|
349
|
+
token: token.accessToken()
|
|
420
350
|
}
|
|
421
351
|
});
|
|
422
352
|
if (!error3) {
|
|
@@ -439,41 +369,81 @@ var github = ({
|
|
|
439
369
|
}
|
|
440
370
|
};
|
|
441
371
|
};
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
}
|
|
372
|
+
var consola = createConsola({
|
|
373
|
+
formatOptions: {
|
|
374
|
+
date: false,
|
|
375
|
+
colors: true,
|
|
376
|
+
compact: true
|
|
377
|
+
},
|
|
378
|
+
defaults: {
|
|
379
|
+
tag: "Better Auth"
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
var createLogger = (options) => {
|
|
383
|
+
return {
|
|
384
|
+
log: (...args) => {
|
|
385
|
+
!options?.disabled && consola.log("", ...args);
|
|
386
|
+
},
|
|
387
|
+
error: (...args) => {
|
|
388
|
+
!options?.disabled && consola.error("", ...args);
|
|
389
|
+
},
|
|
390
|
+
warn: (...args) => {
|
|
391
|
+
!options?.disabled && consola.warn("", ...args);
|
|
392
|
+
},
|
|
393
|
+
info: (...args) => {
|
|
394
|
+
!options?.disabled && consola.info("", ...args);
|
|
395
|
+
},
|
|
396
|
+
debug: (...args) => {
|
|
397
|
+
!options?.disabled && consola.debug("", ...args);
|
|
398
|
+
},
|
|
399
|
+
box: (...args) => {
|
|
400
|
+
!options?.disabled && consola.box("", ...args);
|
|
401
|
+
},
|
|
402
|
+
success: (...args) => {
|
|
403
|
+
!options?.disabled && consola.success("", ...args);
|
|
404
|
+
},
|
|
405
|
+
break: (...args) => {
|
|
406
|
+
!options?.disabled && console.log("\n");
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
};
|
|
410
|
+
var logger = createLogger();
|
|
411
|
+
var google = (options) => {
|
|
451
412
|
const googleArctic = new Google(
|
|
452
|
-
clientId,
|
|
453
|
-
clientSecret,
|
|
454
|
-
getRedirectURI("google", redirectURI)
|
|
413
|
+
options.clientId,
|
|
414
|
+
options.clientSecret,
|
|
415
|
+
getRedirectURI("google", options.redirectURI)
|
|
455
416
|
);
|
|
456
417
|
return {
|
|
457
418
|
id: "google",
|
|
458
419
|
name: "Google",
|
|
459
|
-
createAuthorizationURL({ state, scopes, codeVerifier }) {
|
|
420
|
+
createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
|
|
421
|
+
if (!options.clientId || !options.clientSecret) {
|
|
422
|
+
logger.error(
|
|
423
|
+
"clientId and clientSecret is required for Google. Make sure to you have provided them in the options"
|
|
424
|
+
);
|
|
425
|
+
throw new BetterAuthError("clientId is required for Google");
|
|
426
|
+
}
|
|
460
427
|
if (!codeVerifier) {
|
|
461
428
|
throw new BetterAuthError("codeVerifier is required for Google");
|
|
462
429
|
}
|
|
463
430
|
const _scopes = scopes || ["email", "profile"];
|
|
464
431
|
return googleArctic.createAuthorizationURL(state, codeVerifier, _scopes);
|
|
465
432
|
},
|
|
466
|
-
validateAuthorizationCode: async (code, codeVerifier) => {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
433
|
+
validateAuthorizationCode: async (code, codeVerifier, redirectURI) => {
|
|
434
|
+
return validateAuthorizationCode({
|
|
435
|
+
code,
|
|
436
|
+
codeVerifier,
|
|
437
|
+
redirectURI: redirectURI || getRedirectURI("google", options.redirectURI),
|
|
438
|
+
options,
|
|
439
|
+
tokenEndpoint: "https://oauth2.googleapis.com/token"
|
|
440
|
+
});
|
|
471
441
|
},
|
|
472
442
|
async getUserInfo(token) {
|
|
473
443
|
if (!token.idToken) {
|
|
474
444
|
return null;
|
|
475
445
|
}
|
|
476
|
-
const user =
|
|
446
|
+
const user = parseJWT(token.idToken())?.payload;
|
|
477
447
|
return {
|
|
478
448
|
user: {
|
|
479
449
|
id: user.sub,
|
|
@@ -487,19 +457,11 @@ var google = ({
|
|
|
487
457
|
}
|
|
488
458
|
};
|
|
489
459
|
};
|
|
490
|
-
|
|
491
|
-
// src/social-providers/spotify.ts
|
|
492
|
-
import { betterFetch as betterFetch5 } from "@better-fetch/fetch";
|
|
493
|
-
import { Spotify } from "arctic";
|
|
494
|
-
var spotify = ({
|
|
495
|
-
clientId,
|
|
496
|
-
clientSecret,
|
|
497
|
-
redirectURI
|
|
498
|
-
}) => {
|
|
460
|
+
var spotify = (options) => {
|
|
499
461
|
const spotifyArctic = new Spotify(
|
|
500
|
-
clientId,
|
|
501
|
-
clientSecret,
|
|
502
|
-
getRedirectURI("spotify", redirectURI)
|
|
462
|
+
options.clientId,
|
|
463
|
+
options.clientSecret,
|
|
464
|
+
getRedirectURI("spotify", options.redirectURI)
|
|
503
465
|
);
|
|
504
466
|
return {
|
|
505
467
|
id: "spotify",
|
|
@@ -508,14 +470,22 @@ var spotify = ({
|
|
|
508
470
|
const _scopes = scopes || ["user-read-email"];
|
|
509
471
|
return spotifyArctic.createAuthorizationURL(state, _scopes);
|
|
510
472
|
},
|
|
511
|
-
validateAuthorizationCode:
|
|
473
|
+
validateAuthorizationCode: async (code, codeVerifier, redirectURI) => {
|
|
474
|
+
return validateAuthorizationCode({
|
|
475
|
+
code,
|
|
476
|
+
codeVerifier,
|
|
477
|
+
redirectURI: redirectURI || getRedirectURI("spotify", options.redirectURI),
|
|
478
|
+
options,
|
|
479
|
+
tokenEndpoint: "https://accounts.spotify.com/api/token"
|
|
480
|
+
});
|
|
481
|
+
},
|
|
512
482
|
async getUserInfo(token) {
|
|
513
|
-
const { data: profile, error: error2 } = await
|
|
483
|
+
const { data: profile, error: error2 } = await betterFetch(
|
|
514
484
|
"https://api.spotify.com/v1/me",
|
|
515
485
|
{
|
|
516
486
|
method: "GET",
|
|
517
487
|
headers: {
|
|
518
|
-
Authorization: `Bearer ${token.accessToken}`
|
|
488
|
+
Authorization: `Bearer ${token.accessToken()}`
|
|
519
489
|
}
|
|
520
490
|
}
|
|
521
491
|
);
|
|
@@ -535,19 +505,11 @@ var spotify = ({
|
|
|
535
505
|
}
|
|
536
506
|
};
|
|
537
507
|
};
|
|
538
|
-
|
|
539
|
-
// src/social-providers/twitch.ts
|
|
540
|
-
import { betterFetch as betterFetch6 } from "@better-fetch/fetch";
|
|
541
|
-
import { Twitch } from "arctic";
|
|
542
|
-
var twitch = ({
|
|
543
|
-
clientId,
|
|
544
|
-
clientSecret,
|
|
545
|
-
redirectURI
|
|
546
|
-
}) => {
|
|
508
|
+
var twitch = (options) => {
|
|
547
509
|
const twitchArctic = new Twitch(
|
|
548
|
-
clientId,
|
|
549
|
-
clientSecret,
|
|
550
|
-
getRedirectURI("twitch", redirectURI)
|
|
510
|
+
options.clientId,
|
|
511
|
+
options.clientSecret,
|
|
512
|
+
getRedirectURI("twitch", options.redirectURI)
|
|
551
513
|
);
|
|
552
514
|
return {
|
|
553
515
|
id: "twitch",
|
|
@@ -556,14 +518,22 @@ var twitch = ({
|
|
|
556
518
|
const _scopes = scopes || ["activity:write", "read"];
|
|
557
519
|
return twitchArctic.createAuthorizationURL(state, _scopes);
|
|
558
520
|
},
|
|
559
|
-
validateAuthorizationCode:
|
|
521
|
+
validateAuthorizationCode: async (code, codeVerifier, redirectURI) => {
|
|
522
|
+
return validateAuthorizationCode({
|
|
523
|
+
code,
|
|
524
|
+
codeVerifier,
|
|
525
|
+
redirectURI: redirectURI || getRedirectURI("twitch", options.redirectURI),
|
|
526
|
+
options,
|
|
527
|
+
tokenEndpoint: "https://id.twitch.tv/oauth2/token"
|
|
528
|
+
});
|
|
529
|
+
},
|
|
560
530
|
async getUserInfo(token) {
|
|
561
|
-
const { data: profile, error: error2 } = await
|
|
531
|
+
const { data: profile, error: error2 } = await betterFetch(
|
|
562
532
|
"https://api.twitch.tv/helix/users",
|
|
563
533
|
{
|
|
564
534
|
method: "GET",
|
|
565
535
|
headers: {
|
|
566
|
-
Authorization: `Bearer ${token.accessToken}`
|
|
536
|
+
Authorization: `Bearer ${token.accessToken()}`
|
|
567
537
|
}
|
|
568
538
|
}
|
|
569
539
|
);
|
|
@@ -583,19 +553,11 @@ var twitch = ({
|
|
|
583
553
|
}
|
|
584
554
|
};
|
|
585
555
|
};
|
|
586
|
-
|
|
587
|
-
// src/social-providers/twitter.ts
|
|
588
|
-
import { betterFetch as betterFetch7 } from "@better-fetch/fetch";
|
|
589
|
-
import { Twitter } from "arctic";
|
|
590
|
-
var twitter = ({
|
|
591
|
-
clientId,
|
|
592
|
-
clientSecret,
|
|
593
|
-
redirectURI
|
|
594
|
-
}) => {
|
|
556
|
+
var twitter = (options) => {
|
|
595
557
|
const twitterArctic = new Twitter(
|
|
596
|
-
clientId,
|
|
597
|
-
clientSecret,
|
|
598
|
-
getRedirectURI("twitter", redirectURI)
|
|
558
|
+
options.clientId,
|
|
559
|
+
options.clientSecret,
|
|
560
|
+
getRedirectURI("twitter", options.redirectURI)
|
|
599
561
|
);
|
|
600
562
|
return {
|
|
601
563
|
id: "twitter",
|
|
@@ -608,19 +570,22 @@ var twitter = ({
|
|
|
608
570
|
_scopes
|
|
609
571
|
);
|
|
610
572
|
},
|
|
611
|
-
validateAuthorizationCode: async (code, codeVerifier) => {
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
573
|
+
validateAuthorizationCode: async (code, codeVerifier, redirectURI) => {
|
|
574
|
+
return validateAuthorizationCode({
|
|
575
|
+
code,
|
|
576
|
+
codeVerifier,
|
|
577
|
+
redirectURI: redirectURI || getRedirectURI("twitch", options.redirectURI),
|
|
578
|
+
options,
|
|
579
|
+
tokenEndpoint: "https://id.twitch.tv/oauth2/token"
|
|
580
|
+
});
|
|
616
581
|
},
|
|
617
582
|
async getUserInfo(token) {
|
|
618
|
-
const { data: profile, error: error2 } = await
|
|
583
|
+
const { data: profile, error: error2 } = await betterFetch(
|
|
619
584
|
"https://api.x.com/2/users/me?user.fields=profile_image_url",
|
|
620
585
|
{
|
|
621
586
|
method: "GET",
|
|
622
587
|
headers: {
|
|
623
|
-
Authorization: `Bearer ${token.accessToken}`
|
|
588
|
+
Authorization: `Bearer ${token.accessToken()}`
|
|
624
589
|
}
|
|
625
590
|
}
|
|
626
591
|
);
|
|
@@ -644,9 +609,6 @@ var twitter = ({
|
|
|
644
609
|
};
|
|
645
610
|
};
|
|
646
611
|
|
|
647
|
-
// src/types/provider.ts
|
|
648
|
-
import "arctic";
|
|
649
|
-
|
|
650
612
|
// src/social-providers/index.ts
|
|
651
613
|
var oAuthProviders = {
|
|
652
614
|
apple,
|
|
@@ -659,17 +621,151 @@ var oAuthProviders = {
|
|
|
659
621
|
twitter
|
|
660
622
|
};
|
|
661
623
|
var oAuthProviderList = Object.keys(oAuthProviders);
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
624
|
+
function generateState(callbackURL, currentURL, dontRememberMe) {
|
|
625
|
+
const code = generateState$1();
|
|
626
|
+
const state = JSON.stringify({
|
|
627
|
+
code,
|
|
628
|
+
callbackURL,
|
|
629
|
+
currentURL,
|
|
630
|
+
dontRememberMe
|
|
631
|
+
});
|
|
668
632
|
return { state, code };
|
|
669
633
|
}
|
|
670
634
|
function parseState(state) {
|
|
671
|
-
const
|
|
672
|
-
|
|
635
|
+
const data = z.object({
|
|
636
|
+
code: z.string(),
|
|
637
|
+
callbackURL: z.string().optional(),
|
|
638
|
+
currentURL: z.string().optional(),
|
|
639
|
+
dontRememberMe: z.boolean().optional()
|
|
640
|
+
}).safeParse(JSON.parse(state));
|
|
641
|
+
return data;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// src/utils/date.ts
|
|
645
|
+
var getDate = (span, isSeconds = false) => {
|
|
646
|
+
const date = /* @__PURE__ */ new Date();
|
|
647
|
+
return new Date(date.getTime() + (isSeconds ? span * 1e3 : span));
|
|
648
|
+
};
|
|
649
|
+
function getCookies(options) {
|
|
650
|
+
const secure = !!options.advanced?.useSecureCookies || process.env.NODE_ENV === "production";
|
|
651
|
+
const secureCookiePrefix = secure ? "__Secure-" : "";
|
|
652
|
+
const cookiePrefix = "better-auth";
|
|
653
|
+
const sessionMaxAge = new TimeSpan(7, "d").seconds();
|
|
654
|
+
return {
|
|
655
|
+
sessionToken: {
|
|
656
|
+
name: `${secureCookiePrefix}${cookiePrefix}.session_token`,
|
|
657
|
+
options: {
|
|
658
|
+
httpOnly: true,
|
|
659
|
+
sameSite: "lax",
|
|
660
|
+
path: "/",
|
|
661
|
+
secure,
|
|
662
|
+
maxAge: sessionMaxAge
|
|
663
|
+
}
|
|
664
|
+
},
|
|
665
|
+
csrfToken: {
|
|
666
|
+
name: `${secureCookiePrefix ? "__Host-" : ""}${cookiePrefix}.csrf_token`,
|
|
667
|
+
options: {
|
|
668
|
+
httpOnly: true,
|
|
669
|
+
sameSite: "lax",
|
|
670
|
+
path: "/",
|
|
671
|
+
secure,
|
|
672
|
+
maxAge: 60 * 60 * 24 * 7
|
|
673
|
+
}
|
|
674
|
+
},
|
|
675
|
+
state: {
|
|
676
|
+
name: `${secureCookiePrefix}${cookiePrefix}.state`,
|
|
677
|
+
options: {
|
|
678
|
+
httpOnly: true,
|
|
679
|
+
sameSite: "lax",
|
|
680
|
+
path: "/",
|
|
681
|
+
secure,
|
|
682
|
+
maxAge: 60 * 15
|
|
683
|
+
// 15 minutes in seconds
|
|
684
|
+
}
|
|
685
|
+
},
|
|
686
|
+
pkCodeVerifier: {
|
|
687
|
+
name: `${secureCookiePrefix}${cookiePrefix}.pk_code_verifier`,
|
|
688
|
+
options: {
|
|
689
|
+
httpOnly: true,
|
|
690
|
+
sameSite: "lax",
|
|
691
|
+
path: "/",
|
|
692
|
+
secure,
|
|
693
|
+
maxAge: 60 * 15
|
|
694
|
+
// 15 minutes in seconds
|
|
695
|
+
}
|
|
696
|
+
},
|
|
697
|
+
dontRememberToken: {
|
|
698
|
+
name: `${secureCookiePrefix}${cookiePrefix}.dont_remember`,
|
|
699
|
+
options: {
|
|
700
|
+
httpOnly: true,
|
|
701
|
+
sameSite: "lax",
|
|
702
|
+
path: "/",
|
|
703
|
+
secure
|
|
704
|
+
//no max age so it expires when the browser closes
|
|
705
|
+
}
|
|
706
|
+
},
|
|
707
|
+
nonce: {
|
|
708
|
+
name: `${secureCookiePrefix}${cookiePrefix}.nonce`,
|
|
709
|
+
options: {
|
|
710
|
+
httpOnly: true,
|
|
711
|
+
sameSite: "lax",
|
|
712
|
+
path: "/",
|
|
713
|
+
secure,
|
|
714
|
+
maxAge: 60 * 15
|
|
715
|
+
// 15 minutes in seconds
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
function createCookieGetter(options) {
|
|
721
|
+
const secure = !!options.advanced?.useSecureCookies || process.env.NODE_ENV === "production";
|
|
722
|
+
const secureCookiePrefix = secure ? "__Secure-" : "";
|
|
723
|
+
const cookiePrefix = "better-auth";
|
|
724
|
+
function getCookie(cookieName, options2) {
|
|
725
|
+
return {
|
|
726
|
+
name: process.env.NODE_ENV === "production" ? `${secureCookiePrefix}${cookiePrefix}.${cookieName}` : `${cookiePrefix}.${cookieName}`,
|
|
727
|
+
options: {
|
|
728
|
+
secure,
|
|
729
|
+
sameSite: "lax",
|
|
730
|
+
path: "/",
|
|
731
|
+
maxAge: 60 * 15,
|
|
732
|
+
// 15 minutes in seconds
|
|
733
|
+
...options2
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
return getCookie;
|
|
738
|
+
}
|
|
739
|
+
async function setSessionCookie(ctx, sessionToken, dontRememberMe, overrides) {
|
|
740
|
+
await ctx.setSignedCookie(
|
|
741
|
+
ctx.context.authCookies.sessionToken.name,
|
|
742
|
+
sessionToken,
|
|
743
|
+
ctx.context.secret,
|
|
744
|
+
dontRememberMe ? {
|
|
745
|
+
...ctx.context.authCookies.sessionToken.options,
|
|
746
|
+
maxAge: void 0,
|
|
747
|
+
...overrides
|
|
748
|
+
} : {
|
|
749
|
+
...ctx.context.authCookies.sessionToken.options,
|
|
750
|
+
...overrides
|
|
751
|
+
}
|
|
752
|
+
);
|
|
753
|
+
if (dontRememberMe) {
|
|
754
|
+
await ctx.setSignedCookie(
|
|
755
|
+
ctx.context.authCookies.dontRememberToken.name,
|
|
756
|
+
"true",
|
|
757
|
+
ctx.context.secret,
|
|
758
|
+
ctx.context.authCookies.dontRememberToken.options
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
function deleteSessionCookie(ctx) {
|
|
763
|
+
ctx.setCookie(ctx.context.authCookies.sessionToken.name, "", {
|
|
764
|
+
maxAge: 0
|
|
765
|
+
});
|
|
766
|
+
ctx.setCookie(ctx.context.authCookies.dontRememberToken.name, "", {
|
|
767
|
+
maxAge: 0
|
|
768
|
+
});
|
|
673
769
|
}
|
|
674
770
|
|
|
675
771
|
// src/api/routes/session.ts
|
|
@@ -680,45 +776,62 @@ var getSession = createAuthEndpoint(
|
|
|
680
776
|
requireHeaders: true
|
|
681
777
|
},
|
|
682
778
|
async (ctx) => {
|
|
683
|
-
|
|
684
|
-
ctx.
|
|
685
|
-
ctx.context.secret
|
|
686
|
-
);
|
|
687
|
-
if (!sessionCookieToken) {
|
|
688
|
-
return ctx.json(null, {
|
|
689
|
-
status: 401
|
|
690
|
-
});
|
|
691
|
-
}
|
|
692
|
-
const session = await ctx.context.internalAdapter.findSession(sessionCookieToken);
|
|
693
|
-
if (!session || session.session.expiresAt < /* @__PURE__ */ new Date()) {
|
|
694
|
-
ctx.setSignedCookie(
|
|
779
|
+
try {
|
|
780
|
+
const sessionCookieToken = await ctx.getSignedCookie(
|
|
695
781
|
ctx.context.authCookies.sessionToken.name,
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
782
|
+
ctx.context.secret
|
|
783
|
+
);
|
|
784
|
+
if (!sessionCookieToken) {
|
|
785
|
+
return ctx.json(null, {
|
|
786
|
+
status: 401
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
const session = await ctx.context.internalAdapter.findSession(sessionCookieToken);
|
|
790
|
+
if (!session || session.session.expiresAt < /* @__PURE__ */ new Date()) {
|
|
791
|
+
deleteSessionCookie(ctx);
|
|
792
|
+
if (session) {
|
|
793
|
+
await ctx.context.internalAdapter.deleteSession(session.session.id);
|
|
700
794
|
}
|
|
795
|
+
return ctx.json(null, {
|
|
796
|
+
status: 401
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
const dontRememberMe = await ctx.getSignedCookie(
|
|
800
|
+
ctx.context.authCookies.dontRememberToken.name,
|
|
801
|
+
ctx.context.secret
|
|
701
802
|
);
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
const updatedSession = await ctx.context.internalAdapter.updateSession(
|
|
707
|
-
session.session
|
|
708
|
-
);
|
|
709
|
-
await ctx.setSignedCookie(
|
|
710
|
-
ctx.context.authCookies.sessionToken.name,
|
|
711
|
-
updatedSession.id,
|
|
712
|
-
ctx.context.secret,
|
|
713
|
-
{
|
|
714
|
-
...ctx.context.authCookies.sessionToken.options,
|
|
715
|
-
maxAge: updatedSession.expiresAt.valueOf() - Date.now()
|
|
803
|
+
if (dontRememberMe) {
|
|
804
|
+
return ctx.json(session);
|
|
716
805
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
session
|
|
720
|
-
|
|
721
|
-
|
|
806
|
+
const expiresIn = ctx.context.session.expiresIn;
|
|
807
|
+
const updateAge = ctx.context.session.updateAge;
|
|
808
|
+
const sessionIsDueToBeUpdatedDate = session.session.expiresAt.valueOf() - expiresIn * 1e3 + updateAge * 1e3;
|
|
809
|
+
const shouldBeUpdated = sessionIsDueToBeUpdatedDate <= Date.now();
|
|
810
|
+
if (shouldBeUpdated) {
|
|
811
|
+
const updatedSession = await ctx.context.internalAdapter.updateSession(
|
|
812
|
+
session.session.id,
|
|
813
|
+
{
|
|
814
|
+
expiresAt: getDate(ctx.context.session.expiresIn, true)
|
|
815
|
+
}
|
|
816
|
+
);
|
|
817
|
+
if (!updatedSession) {
|
|
818
|
+
deleteSessionCookie(ctx);
|
|
819
|
+
return ctx.json(null, { status: 401 });
|
|
820
|
+
}
|
|
821
|
+
const maxAge = (updatedSession.expiresAt.valueOf() - Date.now()) / 1e3;
|
|
822
|
+
await setSessionCookie(ctx, updatedSession.id, false, {
|
|
823
|
+
maxAge
|
|
824
|
+
});
|
|
825
|
+
return ctx.json({
|
|
826
|
+
session: updatedSession,
|
|
827
|
+
user: session.user
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
return ctx.json(session);
|
|
831
|
+
} catch (error2) {
|
|
832
|
+
ctx.context.logger.error(error2);
|
|
833
|
+
return ctx.json(null, { status: 500 });
|
|
834
|
+
}
|
|
722
835
|
}
|
|
723
836
|
);
|
|
724
837
|
var getSessionFromCtx = async (ctx) => {
|
|
@@ -736,22 +849,26 @@ var signInOAuth = createAuthEndpoint(
|
|
|
736
849
|
{
|
|
737
850
|
method: "POST",
|
|
738
851
|
requireHeaders: true,
|
|
739
|
-
query:
|
|
852
|
+
query: z.object({
|
|
740
853
|
/**
|
|
741
854
|
* Redirect to the current URL after the
|
|
742
855
|
* user has signed in.
|
|
743
856
|
*/
|
|
744
|
-
currentURL:
|
|
857
|
+
currentURL: z.string().optional()
|
|
745
858
|
}).optional(),
|
|
746
|
-
body:
|
|
859
|
+
body: z.object({
|
|
747
860
|
/**
|
|
748
861
|
* Callback URL to redirect to after the user has signed in.
|
|
749
862
|
*/
|
|
750
|
-
callbackURL:
|
|
863
|
+
callbackURL: z.string().optional(),
|
|
751
864
|
/**
|
|
752
865
|
* OAuth2 provider to use`
|
|
753
866
|
*/
|
|
754
|
-
provider:
|
|
867
|
+
provider: z.enum(oAuthProviderList),
|
|
868
|
+
/**
|
|
869
|
+
* If this is true the session will only be valid for the current browser session
|
|
870
|
+
*/
|
|
871
|
+
dontRememberMe: z.boolean().default(false).optional()
|
|
755
872
|
})
|
|
756
873
|
},
|
|
757
874
|
async (c) => {
|
|
@@ -765,7 +882,9 @@ var signInOAuth = createAuthEndpoint(
|
|
|
765
882
|
provider: c.body.provider
|
|
766
883
|
}
|
|
767
884
|
);
|
|
768
|
-
throw new
|
|
885
|
+
throw new APIError("NOT_FOUND", {
|
|
886
|
+
message: "Provider not found"
|
|
887
|
+
});
|
|
769
888
|
}
|
|
770
889
|
const cookie = c.context.authCookies;
|
|
771
890
|
const currentURL = c.query?.currentURL ? new URL(c.query?.currentURL) : null;
|
|
@@ -791,6 +910,10 @@ var signInOAuth = createAuthEndpoint(
|
|
|
791
910
|
state: state.state,
|
|
792
911
|
codeVerifier
|
|
793
912
|
});
|
|
913
|
+
url.searchParams.set(
|
|
914
|
+
"redirect_uri",
|
|
915
|
+
`${c.context.baseURL}/callback/${c.body.provider}`
|
|
916
|
+
);
|
|
794
917
|
return {
|
|
795
918
|
url: url.toString(),
|
|
796
919
|
state: state.state,
|
|
@@ -798,7 +921,7 @@ var signInOAuth = createAuthEndpoint(
|
|
|
798
921
|
redirect: true
|
|
799
922
|
};
|
|
800
923
|
} catch (e) {
|
|
801
|
-
throw new
|
|
924
|
+
throw new APIError("INTERNAL_SERVER_ERROR");
|
|
802
925
|
}
|
|
803
926
|
}
|
|
804
927
|
);
|
|
@@ -806,40 +929,42 @@ var signInEmail = createAuthEndpoint(
|
|
|
806
929
|
"/sign-in/email",
|
|
807
930
|
{
|
|
808
931
|
method: "POST",
|
|
809
|
-
body:
|
|
810
|
-
email:
|
|
811
|
-
password:
|
|
812
|
-
callbackURL:
|
|
932
|
+
body: z.object({
|
|
933
|
+
email: z.string().email(),
|
|
934
|
+
password: z.string(),
|
|
935
|
+
callbackURL: z.string().optional(),
|
|
813
936
|
/**
|
|
814
937
|
* If this is true the session will only be valid for the current browser session
|
|
815
938
|
* @default false
|
|
816
939
|
*/
|
|
817
|
-
dontRememberMe:
|
|
940
|
+
dontRememberMe: z.boolean().default(false).optional()
|
|
818
941
|
})
|
|
819
942
|
},
|
|
820
943
|
async (ctx) => {
|
|
821
944
|
if (!ctx.context.options?.emailAndPassword?.enabled) {
|
|
822
945
|
ctx.context.logger.error("Email and password is not enabled");
|
|
823
|
-
throw new
|
|
946
|
+
throw new APIError("BAD_REQUEST", {
|
|
824
947
|
message: "Email and password is not enabled"
|
|
825
948
|
});
|
|
826
949
|
}
|
|
827
950
|
const currentSession = await getSessionFromCtx(ctx);
|
|
828
951
|
if (currentSession) {
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
redirect: !!ctx.body.callbackURL,
|
|
833
|
-
url: ctx.body.callbackURL
|
|
834
|
-
});
|
|
952
|
+
await ctx.context.internalAdapter.deleteSession(
|
|
953
|
+
currentSession.session.id
|
|
954
|
+
);
|
|
835
955
|
}
|
|
836
956
|
const { email, password } = ctx.body;
|
|
837
|
-
const
|
|
957
|
+
const checkEmail = z.string().email().safeParse(email);
|
|
958
|
+
if (!checkEmail.success) {
|
|
959
|
+
throw new APIError("BAD_REQUEST", {
|
|
960
|
+
message: "Invalid email"
|
|
961
|
+
});
|
|
962
|
+
}
|
|
838
963
|
const user = await ctx.context.internalAdapter.findUserByEmail(email);
|
|
839
964
|
if (!user) {
|
|
840
|
-
await
|
|
965
|
+
await ctx.context.password.hash(password);
|
|
841
966
|
ctx.context.logger.error("User not found", { email });
|
|
842
|
-
throw new
|
|
967
|
+
throw new APIError("UNAUTHORIZED", {
|
|
843
968
|
message: "Invalid email or password"
|
|
844
969
|
});
|
|
845
970
|
}
|
|
@@ -848,37 +973,33 @@ var signInEmail = createAuthEndpoint(
|
|
|
848
973
|
);
|
|
849
974
|
if (!credentialAccount) {
|
|
850
975
|
ctx.context.logger.error("Credential account not found", { email });
|
|
851
|
-
throw new
|
|
976
|
+
throw new APIError("UNAUTHORIZED", {
|
|
852
977
|
message: "Invalid email or password"
|
|
853
978
|
});
|
|
854
979
|
}
|
|
855
980
|
const currentPassword = credentialAccount?.password;
|
|
856
981
|
if (!currentPassword) {
|
|
857
982
|
ctx.context.logger.error("Password not found", { email });
|
|
858
|
-
throw new
|
|
983
|
+
throw new APIError("UNAUTHORIZED", {
|
|
859
984
|
message: "Unexpected error"
|
|
860
985
|
});
|
|
861
986
|
}
|
|
862
|
-
const validPassword = await
|
|
987
|
+
const validPassword = await ctx.context.password.verify(
|
|
988
|
+
currentPassword,
|
|
989
|
+
password
|
|
990
|
+
);
|
|
863
991
|
if (!validPassword) {
|
|
864
992
|
ctx.context.logger.error("Invalid password");
|
|
865
|
-
throw new
|
|
993
|
+
throw new APIError("UNAUTHORIZED", {
|
|
866
994
|
message: "Invalid email or password"
|
|
867
995
|
});
|
|
868
996
|
}
|
|
869
997
|
const session = await ctx.context.internalAdapter.createSession(
|
|
870
998
|
user.user.id,
|
|
871
|
-
ctx.request
|
|
872
|
-
|
|
873
|
-
await ctx.setSignedCookie(
|
|
874
|
-
ctx.context.authCookies.sessionToken.name,
|
|
875
|
-
session.id,
|
|
876
|
-
ctx.context.secret,
|
|
877
|
-
ctx.body.dontRememberMe ? {
|
|
878
|
-
...ctx.context.authCookies.sessionToken.options,
|
|
879
|
-
maxAge: void 0
|
|
880
|
-
} : ctx.context.authCookies.sessionToken.options
|
|
999
|
+
ctx.request,
|
|
1000
|
+
ctx.body.dontRememberMe
|
|
881
1001
|
);
|
|
1002
|
+
await setSessionCookie(ctx, session.id, ctx.body.dontRememberMe);
|
|
882
1003
|
return ctx.json({
|
|
883
1004
|
user: user.user,
|
|
884
1005
|
session,
|
|
@@ -887,31 +1008,80 @@ var signInEmail = createAuthEndpoint(
|
|
|
887
1008
|
});
|
|
888
1009
|
}
|
|
889
1010
|
);
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
1011
|
+
z.object({
|
|
1012
|
+
id: z.string(),
|
|
1013
|
+
providerId: z.string(),
|
|
1014
|
+
accountId: z.string(),
|
|
1015
|
+
userId: z.string(),
|
|
1016
|
+
accessToken: z.string().nullable().optional(),
|
|
1017
|
+
refreshToken: z.string().nullable().optional(),
|
|
1018
|
+
idToken: z.string().nullable().optional(),
|
|
1019
|
+
accessTokenExpiresAt: z.date().nullable().optional(),
|
|
1020
|
+
refreshTokenExpiresAt: z.date().nullable().optional(),
|
|
1021
|
+
/**
|
|
1022
|
+
* Password is only stored in the credential provider
|
|
1023
|
+
*/
|
|
1024
|
+
password: z.string().optional().nullable()
|
|
1025
|
+
});
|
|
1026
|
+
var userSchema = z.object({
|
|
1027
|
+
id: z.string(),
|
|
1028
|
+
email: z.string().transform((val) => val.toLowerCase()),
|
|
1029
|
+
emailVerified: z.boolean().default(false),
|
|
1030
|
+
name: z.string(),
|
|
1031
|
+
image: z.string().optional(),
|
|
1032
|
+
createdAt: z.date().default(/* @__PURE__ */ new Date()),
|
|
1033
|
+
updatedAt: z.date().default(/* @__PURE__ */ new Date())
|
|
1034
|
+
});
|
|
1035
|
+
z.object({
|
|
1036
|
+
id: z.string(),
|
|
1037
|
+
userId: z.string(),
|
|
1038
|
+
expiresAt: z.date(),
|
|
1039
|
+
ipAddress: z.string().optional(),
|
|
1040
|
+
userAgent: z.string().optional()
|
|
1041
|
+
});
|
|
1042
|
+
var generateId = () => {
|
|
1043
|
+
return generateRandomString(36, alphabet("a-z", "0-9"));
|
|
1044
|
+
};
|
|
894
1045
|
|
|
895
1046
|
// src/client/client-utils.ts
|
|
896
1047
|
var HIDE_ON_CLIENT_METADATA = {
|
|
897
1048
|
onClient: "hide"
|
|
898
1049
|
};
|
|
899
1050
|
|
|
900
|
-
// src/utils/
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1051
|
+
// src/utils/getAccount.ts
|
|
1052
|
+
function getAccountTokens(tokens) {
|
|
1053
|
+
const accessToken = tokens.accessToken();
|
|
1054
|
+
let refreshToken = void 0;
|
|
1055
|
+
try {
|
|
1056
|
+
refreshToken = tokens.refreshToken();
|
|
1057
|
+
} catch {
|
|
1058
|
+
}
|
|
1059
|
+
let accessTokenExpiresAt = void 0;
|
|
1060
|
+
let refreshTokenExpiresAt = void 0;
|
|
1061
|
+
try {
|
|
1062
|
+
accessTokenExpiresAt = tokens.accessTokenExpiresAt();
|
|
1063
|
+
} catch {
|
|
1064
|
+
}
|
|
1065
|
+
try {
|
|
1066
|
+
refreshTokenExpiresAt = tokens.refreshTokenExpiresAt();
|
|
1067
|
+
} catch {
|
|
1068
|
+
}
|
|
1069
|
+
return {
|
|
1070
|
+
accessToken,
|
|
1071
|
+
refreshToken,
|
|
1072
|
+
accessTokenExpiresAt,
|
|
1073
|
+
refreshTokenExpiresAt
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
|
|
906
1077
|
// src/api/routes/callback.ts
|
|
907
1078
|
var callbackOAuth = createAuthEndpoint(
|
|
908
1079
|
"/callback/:id",
|
|
909
1080
|
{
|
|
910
1081
|
method: "GET",
|
|
911
|
-
query:
|
|
912
|
-
state:
|
|
913
|
-
code:
|
|
914
|
-
code_verifier: z4.string().optional()
|
|
1082
|
+
query: z.object({
|
|
1083
|
+
state: z.string(),
|
|
1084
|
+
code: z.string()
|
|
915
1085
|
}),
|
|
916
1086
|
metadata: HIDE_ON_CLIENT_METADATA
|
|
917
1087
|
},
|
|
@@ -925,15 +1095,20 @@ var callbackOAuth = createAuthEndpoint(
|
|
|
925
1095
|
c.params.id,
|
|
926
1096
|
"not found"
|
|
927
1097
|
);
|
|
928
|
-
throw new
|
|
1098
|
+
throw new APIError("NOT_FOUND");
|
|
929
1099
|
}
|
|
1100
|
+
const codeVerifier = await c.getSignedCookie(
|
|
1101
|
+
c.context.authCookies.pkCodeVerifier.name,
|
|
1102
|
+
c.context.secret
|
|
1103
|
+
);
|
|
930
1104
|
const tokens = await provider.validateAuthorizationCode(
|
|
931
1105
|
c.query.code,
|
|
932
|
-
|
|
1106
|
+
codeVerifier,
|
|
1107
|
+
`${c.context.baseURL}/callback/${provider.id}`
|
|
933
1108
|
);
|
|
934
1109
|
if (!tokens) {
|
|
935
1110
|
c.context.logger.error("Code verification failed");
|
|
936
|
-
throw new
|
|
1111
|
+
throw new APIError("UNAUTHORIZED");
|
|
937
1112
|
}
|
|
938
1113
|
const user = await provider.getUserInfo(tokens).then((res) => res?.user);
|
|
939
1114
|
const id = generateId();
|
|
@@ -941,17 +1116,24 @@ var callbackOAuth = createAuthEndpoint(
|
|
|
941
1116
|
...user,
|
|
942
1117
|
id
|
|
943
1118
|
});
|
|
944
|
-
const
|
|
1119
|
+
const parsedState = parseState(c.query.state);
|
|
1120
|
+
if (!parsedState.success) {
|
|
1121
|
+
c.context.logger.error("Unable to parse state");
|
|
1122
|
+
throw new APIError("BAD_REQUEST", {
|
|
1123
|
+
message: "invalid state"
|
|
1124
|
+
});
|
|
1125
|
+
}
|
|
1126
|
+
const { callbackURL, currentURL, dontRememberMe } = parsedState.data;
|
|
945
1127
|
if (!user || data.success === false) {
|
|
946
1128
|
if (currentURL) {
|
|
947
1129
|
throw c.redirect(`${currentURL}?error=oauth_validation_failed`);
|
|
948
1130
|
} else {
|
|
949
|
-
throw new
|
|
1131
|
+
throw new APIError("BAD_REQUEST");
|
|
950
1132
|
}
|
|
951
1133
|
}
|
|
952
1134
|
if (!callbackURL) {
|
|
953
1135
|
c.context.logger.error("Callback URL not found");
|
|
954
|
-
throw new
|
|
1136
|
+
throw new APIError("FORBIDDEN");
|
|
955
1137
|
}
|
|
956
1138
|
const dbUser = await c.context.internalAdapter.findUserByEmail(user.email);
|
|
957
1139
|
const userId = dbUser?.user.id;
|
|
@@ -971,13 +1153,13 @@ var callbackOAuth = createAuthEndpoint(
|
|
|
971
1153
|
accountId: user.id,
|
|
972
1154
|
id: `${provider.id}:${user.id}`,
|
|
973
1155
|
userId: dbUser.user.id,
|
|
974
|
-
...tokens
|
|
1156
|
+
...getAccountTokens(tokens)
|
|
975
1157
|
});
|
|
976
1158
|
}
|
|
977
1159
|
} else {
|
|
978
1160
|
try {
|
|
979
1161
|
await c.context.internalAdapter.createOAuthUser(data.data, {
|
|
980
|
-
...tokens,
|
|
1162
|
+
...getAccountTokens(tokens),
|
|
981
1163
|
id: `${provider.id}:${user.id}`,
|
|
982
1164
|
providerId: provider.id,
|
|
983
1165
|
accountId: user.id,
|
|
@@ -991,20 +1173,16 @@ var callbackOAuth = createAuthEndpoint(
|
|
|
991
1173
|
}
|
|
992
1174
|
}
|
|
993
1175
|
if (!userId && !id)
|
|
994
|
-
throw new
|
|
1176
|
+
throw new APIError("INTERNAL_SERVER_ERROR", {
|
|
995
1177
|
message: "Unable to create user"
|
|
996
1178
|
});
|
|
997
1179
|
const session = await c.context.internalAdapter.createSession(
|
|
998
1180
|
userId || id,
|
|
999
|
-
c.request
|
|
1181
|
+
c.request,
|
|
1182
|
+
dontRememberMe
|
|
1000
1183
|
);
|
|
1001
1184
|
try {
|
|
1002
|
-
await c.
|
|
1003
|
-
c.context.authCookies.sessionToken.name,
|
|
1004
|
-
session.id,
|
|
1005
|
-
c.context.secret,
|
|
1006
|
-
c.context.authCookies.sessionToken.options
|
|
1007
|
-
);
|
|
1185
|
+
await setSessionCookie(c, session.id, dontRememberMe);
|
|
1008
1186
|
} catch (e) {
|
|
1009
1187
|
c.context.logger.error("Unable to set session cookie", e);
|
|
1010
1188
|
const url = new URL(currentURL || callbackURL);
|
|
@@ -1014,15 +1192,12 @@ var callbackOAuth = createAuthEndpoint(
|
|
|
1014
1192
|
throw c.redirect(callbackURL);
|
|
1015
1193
|
}
|
|
1016
1194
|
);
|
|
1017
|
-
|
|
1018
|
-
// src/api/routes/sign-out.ts
|
|
1019
|
-
import { z as z5 } from "zod";
|
|
1020
1195
|
var signOut = createAuthEndpoint(
|
|
1021
1196
|
"/sign-out",
|
|
1022
1197
|
{
|
|
1023
1198
|
method: "POST",
|
|
1024
|
-
body:
|
|
1025
|
-
callbackURL:
|
|
1199
|
+
body: z.object({
|
|
1200
|
+
callbackURL: z.string().optional()
|
|
1026
1201
|
}).optional()
|
|
1027
1202
|
},
|
|
1028
1203
|
async (ctx) => {
|
|
@@ -1034,9 +1209,7 @@ var signOut = createAuthEndpoint(
|
|
|
1034
1209
|
return ctx.json(null);
|
|
1035
1210
|
}
|
|
1036
1211
|
await ctx.context.internalAdapter.deleteSession(sessionCookieToken);
|
|
1037
|
-
|
|
1038
|
-
maxAge: 0
|
|
1039
|
-
});
|
|
1212
|
+
deleteSessionCookie(ctx);
|
|
1040
1213
|
return ctx.json(null, {
|
|
1041
1214
|
body: {
|
|
1042
1215
|
redirect: !!ctx.body?.callbackURL,
|
|
@@ -1045,22 +1218,15 @@ var signOut = createAuthEndpoint(
|
|
|
1045
1218
|
});
|
|
1046
1219
|
}
|
|
1047
1220
|
);
|
|
1048
|
-
|
|
1049
|
-
// src/api/routes/forget-password.ts
|
|
1050
|
-
import { TimeSpan } from "oslo";
|
|
1051
|
-
import { createJWT } from "oslo/jwt";
|
|
1052
|
-
import { validateJWT } from "oslo/jwt";
|
|
1053
|
-
import { Argon2id as Argon2id2 } from "oslo/password";
|
|
1054
|
-
import { z as z6 } from "zod";
|
|
1055
1221
|
var forgetPassword = createAuthEndpoint(
|
|
1056
1222
|
"/forget-password",
|
|
1057
1223
|
{
|
|
1058
1224
|
method: "POST",
|
|
1059
|
-
body:
|
|
1225
|
+
body: z.object({
|
|
1060
1226
|
/**
|
|
1061
1227
|
* The email address of the user to send a password reset email to.
|
|
1062
1228
|
*/
|
|
1063
|
-
email:
|
|
1229
|
+
email: z.string().email()
|
|
1064
1230
|
})
|
|
1065
1231
|
},
|
|
1066
1232
|
async (ctx) => {
|
|
@@ -1119,10 +1285,10 @@ var resetPassword = createAuthEndpoint(
|
|
|
1119
1285
|
"/reset-password",
|
|
1120
1286
|
{
|
|
1121
1287
|
method: "POST",
|
|
1122
|
-
body:
|
|
1123
|
-
token:
|
|
1124
|
-
newPassword:
|
|
1125
|
-
callbackURL:
|
|
1288
|
+
body: z.object({
|
|
1289
|
+
token: z.string(),
|
|
1290
|
+
newPassword: z.string(),
|
|
1291
|
+
callbackURL: z.string().optional()
|
|
1126
1292
|
})
|
|
1127
1293
|
},
|
|
1128
1294
|
async (ctx) => {
|
|
@@ -1133,7 +1299,7 @@ var resetPassword = createAuthEndpoint(
|
|
|
1133
1299
|
Buffer.from(ctx.context.secret),
|
|
1134
1300
|
token
|
|
1135
1301
|
);
|
|
1136
|
-
const email =
|
|
1302
|
+
const email = z.string().email().parse(jwt.payload.email);
|
|
1137
1303
|
const user = await ctx.context.internalAdapter.findUserByEmail(email);
|
|
1138
1304
|
if (!user) {
|
|
1139
1305
|
return ctx.json(null, {
|
|
@@ -1153,8 +1319,7 @@ var resetPassword = createAuthEndpoint(
|
|
|
1153
1319
|
}
|
|
1154
1320
|
});
|
|
1155
1321
|
}
|
|
1156
|
-
const
|
|
1157
|
-
const hashedPassword = await argon2id.hash(newPassword);
|
|
1322
|
+
const hashedPassword = await ctx.context.password.hash(newPassword);
|
|
1158
1323
|
const updatedUser = await ctx.context.internalAdapter.updatePassword(
|
|
1159
1324
|
user.user.id,
|
|
1160
1325
|
hashedPassword
|
|
@@ -1185,18 +1350,30 @@ var resetPassword = createAuthEndpoint(
|
|
|
1185
1350
|
}
|
|
1186
1351
|
}
|
|
1187
1352
|
);
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1353
|
+
async function createEmailVerificationToken(secret, email) {
|
|
1354
|
+
const token = await createJWT(
|
|
1355
|
+
"HS256",
|
|
1356
|
+
Buffer.from(secret),
|
|
1357
|
+
{
|
|
1358
|
+
email: email.toLowerCase()
|
|
1359
|
+
},
|
|
1360
|
+
{
|
|
1361
|
+
expiresIn: new TimeSpan(1, "h"),
|
|
1362
|
+
issuer: "better-auth",
|
|
1363
|
+
subject: "verify-email",
|
|
1364
|
+
audiences: [email],
|
|
1365
|
+
includeIssuedTimestamp: true
|
|
1366
|
+
}
|
|
1367
|
+
);
|
|
1368
|
+
return token;
|
|
1369
|
+
}
|
|
1193
1370
|
var sendVerificationEmail = createAuthEndpoint(
|
|
1194
1371
|
"/send-verification-email",
|
|
1195
1372
|
{
|
|
1196
1373
|
method: "POST",
|
|
1197
|
-
body:
|
|
1198
|
-
email:
|
|
1199
|
-
callbackURL:
|
|
1374
|
+
body: z.object({
|
|
1375
|
+
email: z.string().email(),
|
|
1376
|
+
callbackURL: z.string().optional()
|
|
1200
1377
|
})
|
|
1201
1378
|
},
|
|
1202
1379
|
async (ctx) => {
|
|
@@ -1210,24 +1387,12 @@ var sendVerificationEmail = createAuthEndpoint(
|
|
|
1210
1387
|
});
|
|
1211
1388
|
}
|
|
1212
1389
|
const { email } = ctx.body;
|
|
1213
|
-
const token = await
|
|
1214
|
-
"HS256",
|
|
1215
|
-
Buffer.from(ctx.context.secret),
|
|
1216
|
-
{
|
|
1217
|
-
email: email.toLowerCase()
|
|
1218
|
-
},
|
|
1219
|
-
{
|
|
1220
|
-
expiresIn: new TimeSpan2(1, "h"),
|
|
1221
|
-
issuer: "better-auth",
|
|
1222
|
-
subject: "verify-email",
|
|
1223
|
-
audiences: [email],
|
|
1224
|
-
includeIssuedTimestamp: true
|
|
1225
|
-
}
|
|
1226
|
-
);
|
|
1390
|
+
const token = await createEmailVerificationToken(ctx.context.secret, email);
|
|
1227
1391
|
const url = `${ctx.context.baseURL}/verify-email?token=${token}?callbackURL=${ctx.body.callbackURL}`;
|
|
1228
1392
|
await ctx.context.options.emailAndPassword.sendVerificationEmail(
|
|
1229
1393
|
email,
|
|
1230
|
-
url
|
|
1394
|
+
url,
|
|
1395
|
+
token
|
|
1231
1396
|
);
|
|
1232
1397
|
return ctx.json({
|
|
1233
1398
|
status: true
|
|
@@ -1238,55 +1403,18 @@ var verifyEmail = createAuthEndpoint(
|
|
|
1238
1403
|
"/verify-email",
|
|
1239
1404
|
{
|
|
1240
1405
|
method: "GET",
|
|
1241
|
-
query:
|
|
1242
|
-
token:
|
|
1243
|
-
callbackURL:
|
|
1406
|
+
query: z.object({
|
|
1407
|
+
token: z.string(),
|
|
1408
|
+
callbackURL: z.string().optional()
|
|
1244
1409
|
})
|
|
1245
1410
|
},
|
|
1246
1411
|
async (ctx) => {
|
|
1247
1412
|
const { token } = ctx.query;
|
|
1413
|
+
let jwt;
|
|
1248
1414
|
try {
|
|
1249
|
-
|
|
1250
|
-
"HS256",
|
|
1251
|
-
Buffer.from(ctx.context.secret),
|
|
1252
|
-
token
|
|
1253
|
-
);
|
|
1254
|
-
const schema = z7.object({
|
|
1255
|
-
email: z7.string().email()
|
|
1256
|
-
});
|
|
1257
|
-
const parsed = schema.parse(jwt.payload);
|
|
1258
|
-
const user = await ctx.context.internalAdapter.findUserByEmail(
|
|
1259
|
-
parsed.email
|
|
1260
|
-
);
|
|
1261
|
-
if (!user) {
|
|
1262
|
-
return ctx.json(null, {
|
|
1263
|
-
status: 400,
|
|
1264
|
-
statusText: "USER_NOT_FOUND",
|
|
1265
|
-
body: {
|
|
1266
|
-
message: "User not found"
|
|
1267
|
-
}
|
|
1268
|
-
});
|
|
1269
|
-
}
|
|
1270
|
-
const account = user.accounts.find((a) => a.providerId === "credential");
|
|
1271
|
-
if (!account) {
|
|
1272
|
-
return ctx.json(null, {
|
|
1273
|
-
status: 400,
|
|
1274
|
-
statusText: "ACCOUNT_NOT_FOUND",
|
|
1275
|
-
body: {
|
|
1276
|
-
message: "Account not found"
|
|
1277
|
-
}
|
|
1278
|
-
});
|
|
1279
|
-
}
|
|
1280
|
-
await ctx.context.internalAdapter.updateUserByEmail(parsed.email, {
|
|
1281
|
-
emailVerified: true
|
|
1282
|
-
});
|
|
1283
|
-
if (ctx.query.callbackURL) {
|
|
1284
|
-
throw ctx.redirect(ctx.query.callbackURL);
|
|
1285
|
-
}
|
|
1286
|
-
return ctx.json({
|
|
1287
|
-
status: true
|
|
1288
|
-
});
|
|
1415
|
+
jwt = await validateJWT("HS256", Buffer.from(ctx.context.secret), token);
|
|
1289
1416
|
} catch (e) {
|
|
1417
|
+
ctx.context.logger.error("Failed to verify email", e);
|
|
1290
1418
|
return ctx.json(null, {
|
|
1291
1419
|
status: 400,
|
|
1292
1420
|
statusText: "INVALID_TOKEN",
|
|
@@ -1295,11 +1423,43 @@ var verifyEmail = createAuthEndpoint(
|
|
|
1295
1423
|
}
|
|
1296
1424
|
});
|
|
1297
1425
|
}
|
|
1426
|
+
const schema = z.object({
|
|
1427
|
+
email: z.string().email()
|
|
1428
|
+
});
|
|
1429
|
+
const parsed = schema.parse(jwt.payload);
|
|
1430
|
+
const user = await ctx.context.internalAdapter.findUserByEmail(
|
|
1431
|
+
parsed.email
|
|
1432
|
+
);
|
|
1433
|
+
if (!user) {
|
|
1434
|
+
return ctx.json(null, {
|
|
1435
|
+
status: 400,
|
|
1436
|
+
statusText: "USER_NOT_FOUND",
|
|
1437
|
+
body: {
|
|
1438
|
+
message: "User not found"
|
|
1439
|
+
}
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
const account = user.accounts.find((a) => a.providerId === "credential");
|
|
1443
|
+
if (!account) {
|
|
1444
|
+
return ctx.json(null, {
|
|
1445
|
+
status: 400,
|
|
1446
|
+
statusText: "ACCOUNT_NOT_FOUND",
|
|
1447
|
+
body: {
|
|
1448
|
+
message: "Account not found"
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1451
|
+
}
|
|
1452
|
+
await ctx.context.internalAdapter.updateUserByEmail(parsed.email, {
|
|
1453
|
+
emailVerified: true
|
|
1454
|
+
});
|
|
1455
|
+
if (ctx.query.callbackURL) {
|
|
1456
|
+
throw ctx.redirect(ctx.query.callbackURL);
|
|
1457
|
+
}
|
|
1458
|
+
return ctx.json({
|
|
1459
|
+
status: true
|
|
1460
|
+
});
|
|
1298
1461
|
}
|
|
1299
1462
|
);
|
|
1300
|
-
|
|
1301
|
-
// src/api/routes/csrf.ts
|
|
1302
|
-
import { alphabet as alphabet2, generateRandomString as generateRandomString2 } from "oslo/crypto";
|
|
1303
1463
|
var getCSRFToken = createAuthEndpoint(
|
|
1304
1464
|
"/csrf",
|
|
1305
1465
|
{
|
|
@@ -1316,9 +1476,9 @@ var getCSRFToken = createAuthEndpoint(
|
|
|
1316
1476
|
csrfToken
|
|
1317
1477
|
};
|
|
1318
1478
|
}
|
|
1319
|
-
const token =
|
|
1320
|
-
const
|
|
1321
|
-
const cookie = `${token}!${
|
|
1479
|
+
const token = generateRandomString(32, alphabet("a-z", "0-9", "A-Z"));
|
|
1480
|
+
const hash2 = await hs256(ctx.context.secret, token);
|
|
1481
|
+
const cookie = `${token}!${hash2}`;
|
|
1322
1482
|
await ctx.setSignedCookie(
|
|
1323
1483
|
ctx.context.authCookies.csrfToken.name,
|
|
1324
1484
|
cookie,
|
|
@@ -1335,7 +1495,8 @@ var getCSRFToken = createAuthEndpoint(
|
|
|
1335
1495
|
var ok = createAuthEndpoint(
|
|
1336
1496
|
"/ok",
|
|
1337
1497
|
{
|
|
1338
|
-
method: "GET"
|
|
1498
|
+
method: "GET",
|
|
1499
|
+
metadata: HIDE_ON_CLIENT_METADATA
|
|
1339
1500
|
},
|
|
1340
1501
|
async (ctx) => {
|
|
1341
1502
|
return ctx.json({
|
|
@@ -1346,27 +1507,23 @@ var ok = createAuthEndpoint(
|
|
|
1346
1507
|
var welcome = createAuthEndpoint(
|
|
1347
1508
|
"/welcome/ok",
|
|
1348
1509
|
{
|
|
1349
|
-
method: "GET"
|
|
1510
|
+
method: "GET",
|
|
1511
|
+
metadata: HIDE_ON_CLIENT_METADATA
|
|
1350
1512
|
},
|
|
1351
1513
|
async () => {
|
|
1352
1514
|
return new Response("Welcome to Better Auth");
|
|
1353
1515
|
}
|
|
1354
1516
|
);
|
|
1355
|
-
|
|
1356
|
-
// src/api/routes/sign-up.ts
|
|
1357
|
-
import { alphabet as alphabet3, generateRandomString as generateRandomString3 } from "oslo/crypto";
|
|
1358
|
-
import { Argon2id as Argon2id3 } from "oslo/password";
|
|
1359
|
-
import { z as z8 } from "zod";
|
|
1360
1517
|
var signUpEmail = createAuthEndpoint(
|
|
1361
1518
|
"/sign-up/email",
|
|
1362
1519
|
{
|
|
1363
1520
|
method: "POST",
|
|
1364
|
-
body:
|
|
1365
|
-
name:
|
|
1366
|
-
email:
|
|
1367
|
-
password:
|
|
1368
|
-
image:
|
|
1369
|
-
callbackURL:
|
|
1521
|
+
body: z.object({
|
|
1522
|
+
name: z.string(),
|
|
1523
|
+
email: z.string().email(),
|
|
1524
|
+
password: z.string(),
|
|
1525
|
+
image: z.string().optional(),
|
|
1526
|
+
callbackURL: z.string().optional()
|
|
1370
1527
|
})
|
|
1371
1528
|
},
|
|
1372
1529
|
async (ctx) => {
|
|
@@ -1387,9 +1544,8 @@ var signUpEmail = createAuthEndpoint(
|
|
|
1387
1544
|
body: { message: "Password is too short" }
|
|
1388
1545
|
});
|
|
1389
1546
|
}
|
|
1390
|
-
const argon2id = new Argon2id3();
|
|
1391
1547
|
const dbUser = await ctx.context.internalAdapter.findUserByEmail(email);
|
|
1392
|
-
const
|
|
1548
|
+
const hash2 = await ctx.context.password.hash(password);
|
|
1393
1549
|
if (dbUser?.user) {
|
|
1394
1550
|
return ctx.json(null, {
|
|
1395
1551
|
status: 400,
|
|
@@ -1399,7 +1555,7 @@ var signUpEmail = createAuthEndpoint(
|
|
|
1399
1555
|
});
|
|
1400
1556
|
}
|
|
1401
1557
|
const createdUser = await ctx.context.internalAdapter.createUser({
|
|
1402
|
-
id:
|
|
1558
|
+
id: generateRandomString(32, alphabet("a-z", "0-9", "A-Z")),
|
|
1403
1559
|
email: email.toLowerCase(),
|
|
1404
1560
|
name,
|
|
1405
1561
|
image,
|
|
@@ -1408,11 +1564,11 @@ var signUpEmail = createAuthEndpoint(
|
|
|
1408
1564
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1409
1565
|
});
|
|
1410
1566
|
await ctx.context.internalAdapter.linkAccount({
|
|
1411
|
-
id:
|
|
1567
|
+
id: generateRandomString(32, alphabet("a-z", "0-9", "A-Z")),
|
|
1412
1568
|
userId: createdUser.id,
|
|
1413
1569
|
providerId: "credential",
|
|
1414
1570
|
accountId: createdUser.id,
|
|
1415
|
-
password:
|
|
1571
|
+
password: hash2
|
|
1416
1572
|
});
|
|
1417
1573
|
const session = await ctx.context.internalAdapter.createSession(
|
|
1418
1574
|
createdUser.id,
|
|
@@ -1424,6 +1580,18 @@ var signUpEmail = createAuthEndpoint(
|
|
|
1424
1580
|
ctx.context.secret,
|
|
1425
1581
|
ctx.context.authCookies.sessionToken.options
|
|
1426
1582
|
);
|
|
1583
|
+
if (ctx.context.options.emailAndPassword.sendEmailVerificationOnSignUp) {
|
|
1584
|
+
const token = await createEmailVerificationToken(
|
|
1585
|
+
ctx.context.secret,
|
|
1586
|
+
createdUser.email
|
|
1587
|
+
);
|
|
1588
|
+
const url = `${ctx.context.baseURL}/verify-email?token=${token}?callbackURL=${ctx.body.callbackURL}`;
|
|
1589
|
+
await ctx.context.options.emailAndPassword.sendVerificationEmail?.(
|
|
1590
|
+
createdUser.email,
|
|
1591
|
+
url,
|
|
1592
|
+
token
|
|
1593
|
+
);
|
|
1594
|
+
}
|
|
1427
1595
|
return ctx.json(
|
|
1428
1596
|
{
|
|
1429
1597
|
user: createdUser,
|
|
@@ -1527,7 +1695,8 @@ var html = (errorCode = "Unknown") => `<!DOCTYPE html>
|
|
|
1527
1695
|
var error = createAuthEndpoint(
|
|
1528
1696
|
"/error",
|
|
1529
1697
|
{
|
|
1530
|
-
method: "GET"
|
|
1698
|
+
method: "GET",
|
|
1699
|
+
metadata: HIDE_ON_CLIENT_METADATA
|
|
1531
1700
|
},
|
|
1532
1701
|
async (c) => {
|
|
1533
1702
|
const query = new URL(c.request?.url || "").searchParams.get("error") || "Unknown";
|
|
@@ -1540,7 +1709,7 @@ var error = createAuthEndpoint(
|
|
|
1540
1709
|
);
|
|
1541
1710
|
|
|
1542
1711
|
// src/api/index.ts
|
|
1543
|
-
|
|
1712
|
+
function getEndpoints(ctx, options) {
|
|
1544
1713
|
const pluginEndpoints = ctx.options.plugins?.reduce(
|
|
1545
1714
|
(acc, plugin) => {
|
|
1546
1715
|
return {
|
|
@@ -1621,7 +1790,7 @@ var router = (ctx) => {
|
|
|
1621
1790
|
}
|
|
1622
1791
|
}
|
|
1623
1792
|
}
|
|
1624
|
-
const endpointRes = value({
|
|
1793
|
+
const endpointRes = await value({
|
|
1625
1794
|
...context,
|
|
1626
1795
|
context: {
|
|
1627
1796
|
...ctx,
|
|
@@ -1635,7 +1804,10 @@ var router = (ctx) => {
|
|
|
1635
1804
|
const match = hook.matcher(context);
|
|
1636
1805
|
if (match) {
|
|
1637
1806
|
const obj = Object.assign(context, {
|
|
1638
|
-
|
|
1807
|
+
context: {
|
|
1808
|
+
...ctx,
|
|
1809
|
+
returned: response
|
|
1810
|
+
}
|
|
1639
1811
|
});
|
|
1640
1812
|
const hookRes = await hook.handler(obj);
|
|
1641
1813
|
if (hookRes && "response" in hookRes) {
|
|
@@ -1652,9 +1824,17 @@ var router = (ctx) => {
|
|
|
1652
1824
|
api[key].options = value.options;
|
|
1653
1825
|
api[key].headers = value.headers;
|
|
1654
1826
|
}
|
|
1827
|
+
return {
|
|
1828
|
+
api,
|
|
1829
|
+
middlewares
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1832
|
+
var router = (ctx, _options) => {
|
|
1833
|
+
const { api, middlewares } = getEndpoints(ctx);
|
|
1834
|
+
const basePath = new URL(ctx.baseURL).pathname;
|
|
1655
1835
|
return createRouter(api, {
|
|
1656
1836
|
extraContext: ctx,
|
|
1657
|
-
basePath
|
|
1837
|
+
basePath,
|
|
1658
1838
|
routerMiddleware: [
|
|
1659
1839
|
{
|
|
1660
1840
|
path: "/**",
|
|
@@ -1662,47 +1842,15 @@ var router = (ctx) => {
|
|
|
1662
1842
|
},
|
|
1663
1843
|
...middlewares
|
|
1664
1844
|
],
|
|
1665
|
-
/**
|
|
1666
|
-
* this is to remove any sensitive data from the response
|
|
1667
|
-
*/
|
|
1668
|
-
async transformResponse(res) {
|
|
1669
|
-
let body = {};
|
|
1670
|
-
try {
|
|
1671
|
-
body = await res.json();
|
|
1672
|
-
} catch (e) {
|
|
1673
|
-
return res;
|
|
1674
|
-
}
|
|
1675
|
-
if (body?.user) {
|
|
1676
|
-
body.user = parseUser(ctx.options, body.user);
|
|
1677
|
-
}
|
|
1678
|
-
if (body?.session) {
|
|
1679
|
-
body.session = parseSession(ctx.options, body.session);
|
|
1680
|
-
}
|
|
1681
|
-
if (body?.account) {
|
|
1682
|
-
body.account = parseAccount(ctx.options, body.account);
|
|
1683
|
-
}
|
|
1684
|
-
return new Response(body ? JSON.stringify(body) : null, {
|
|
1685
|
-
headers: res.headers,
|
|
1686
|
-
status: res.status,
|
|
1687
|
-
statusText: res.statusText
|
|
1688
|
-
});
|
|
1689
|
-
},
|
|
1690
1845
|
onError(e) {
|
|
1691
|
-
|
|
1846
|
+
if (e instanceof APIError) {
|
|
1847
|
+
if (e.status === "INTERNAL_SERVER_ERROR") {
|
|
1848
|
+
logger.error(e);
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1692
1851
|
}
|
|
1693
1852
|
});
|
|
1694
1853
|
};
|
|
1695
|
-
|
|
1696
|
-
// src/adapters/kysely.ts
|
|
1697
|
-
import Database from "better-sqlite3";
|
|
1698
|
-
import { Kysely } from "kysely";
|
|
1699
|
-
import {
|
|
1700
|
-
MysqlDialect,
|
|
1701
|
-
PostgresDialect,
|
|
1702
|
-
SqliteDialect
|
|
1703
|
-
} from "kysely";
|
|
1704
|
-
import { createPool } from "mysql2";
|
|
1705
|
-
import pg from "pg";
|
|
1706
1854
|
var { Pool } = pg;
|
|
1707
1855
|
function convertWhere(w) {
|
|
1708
1856
|
if (!w)
|
|
@@ -1842,7 +1990,7 @@ var kyselyAdapter = (db, config) => {
|
|
|
1842
1990
|
if (or) {
|
|
1843
1991
|
query = query.where((eb) => eb.or(or));
|
|
1844
1992
|
}
|
|
1845
|
-
const res = await query.returningAll().executeTakeFirst();
|
|
1993
|
+
const res = await query.returningAll().executeTakeFirst() || null;
|
|
1846
1994
|
if (config?.transform) {
|
|
1847
1995
|
const schema = config.transform.schema[model];
|
|
1848
1996
|
return schema ? transformTo(res, schema, config.transform) : res;
|
|
@@ -1867,10 +2015,13 @@ var getDialect = (config) => {
|
|
|
1867
2015
|
if (!config.database) {
|
|
1868
2016
|
return null;
|
|
1869
2017
|
}
|
|
2018
|
+
if (config.database instanceof MysqlDialect || config.database instanceof PostgresDialect || config.database instanceof SqliteDialect) {
|
|
2019
|
+
return config.database;
|
|
2020
|
+
}
|
|
1870
2021
|
let dialect = null;
|
|
1871
2022
|
if ("provider" in config.database) {
|
|
1872
2023
|
const provider = config.database.provider;
|
|
1873
|
-
const connectionString = config.database
|
|
2024
|
+
const connectionString = config.database?.url?.trim();
|
|
1874
2025
|
if (provider === "postgres") {
|
|
1875
2026
|
const pool = new Pool({
|
|
1876
2027
|
connectionString
|
|
@@ -2040,37 +2191,25 @@ function getAdapter(options) {
|
|
|
2040
2191
|
if (!options.database) {
|
|
2041
2192
|
throw new BetterAuthError("Database configuration is required");
|
|
2042
2193
|
}
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
throw new BetterAuthError("Failed to initialize database adapter");
|
|
2047
|
-
}
|
|
2048
|
-
const tables = getAuthTables(options);
|
|
2049
|
-
return kyselyAdapter(db, {
|
|
2050
|
-
transform: {
|
|
2051
|
-
schema: {
|
|
2052
|
-
[tables.user.tableName]: tables.user.fields,
|
|
2053
|
-
[tables.session.tableName]: tables.session.fields,
|
|
2054
|
-
[tables.account.tableName]: tables.account.fields
|
|
2055
|
-
},
|
|
2056
|
-
date: true,
|
|
2057
|
-
boolean: getDatabaseType(options) === "sqlite"
|
|
2058
|
-
}
|
|
2059
|
-
});
|
|
2194
|
+
const db = createKyselyAdapter(options);
|
|
2195
|
+
if (!db) {
|
|
2196
|
+
throw new BetterAuthError("Failed to initialize database adapter");
|
|
2060
2197
|
}
|
|
2061
|
-
|
|
2198
|
+
const tables = getAuthTables(options);
|
|
2199
|
+
return kyselyAdapter(db, {
|
|
2200
|
+
transform: {
|
|
2201
|
+
schema: {
|
|
2202
|
+
[tables.user.tableName]: tables.user.fields,
|
|
2203
|
+
[tables.session.tableName]: tables.session.fields,
|
|
2204
|
+
[tables.account.tableName]: tables.account.fields
|
|
2205
|
+
},
|
|
2206
|
+
date: true,
|
|
2207
|
+
boolean: getDatabaseType(options) === "sqlite"
|
|
2208
|
+
}
|
|
2209
|
+
});
|
|
2062
2210
|
}
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
import { alphabet as alphabet4, generateRandomString as generateRandomString4 } from "oslo/crypto";
|
|
2066
|
-
|
|
2067
|
-
// src/utils/date.ts
|
|
2068
|
-
var getDate = (span) => {
|
|
2069
|
-
const date = /* @__PURE__ */ new Date();
|
|
2070
|
-
return new Date(date.getTime() + span);
|
|
2071
|
-
};
|
|
2072
|
-
|
|
2073
|
-
// src/adapters/internal-adapter.ts
|
|
2211
|
+
var hashPassword = argon2.hash;
|
|
2212
|
+
var verifyPassword = argon2.verify;
|
|
2074
2213
|
var createInternalAdapter = (adapter, options) => {
|
|
2075
2214
|
const sessionExpiration = options.session?.expiresIn || 60 * 60 * 24 * 7;
|
|
2076
2215
|
const tables = getAuthTables(options);
|
|
@@ -2101,11 +2240,16 @@ var createInternalAdapter = (adapter, options) => {
|
|
|
2101
2240
|
});
|
|
2102
2241
|
return createdUser;
|
|
2103
2242
|
},
|
|
2104
|
-
createSession: async (userId, request) => {
|
|
2243
|
+
createSession: async (userId, request, dontRememberMe) => {
|
|
2105
2244
|
const data = {
|
|
2106
|
-
id:
|
|
2245
|
+
id: generateRandomString(32, alphabet("a-z", "0-9", "A-Z")),
|
|
2107
2246
|
userId,
|
|
2108
|
-
|
|
2247
|
+
/**
|
|
2248
|
+
* If the user doesn't want to be remembered
|
|
2249
|
+
* set the session to expire in 1 day.
|
|
2250
|
+
* The cookie will be set to expire at the end of the session
|
|
2251
|
+
*/
|
|
2252
|
+
expiresAt: dontRememberMe ? getDate(1e3 * 60 * 60 * 24) : getDate(sessionExpiration, true),
|
|
2109
2253
|
ipAddress: request?.headers.get("x-forwarded-for") || "",
|
|
2110
2254
|
userAgent: request?.headers.get("user-agent") || ""
|
|
2111
2255
|
};
|
|
@@ -2145,39 +2289,18 @@ var createInternalAdapter = (adapter, options) => {
|
|
|
2145
2289
|
user
|
|
2146
2290
|
};
|
|
2147
2291
|
},
|
|
2148
|
-
updateSession: async (session) => {
|
|
2149
|
-
const
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
model: tables.session.tableName,
|
|
2156
|
-
data: {
|
|
2157
|
-
...session,
|
|
2158
|
-
id: generateRandomString4(32, alphabet4("a-z", "0-9", "A-Z")),
|
|
2159
|
-
expiresAt: new Date(Date.now() + sessionExpiration)
|
|
2160
|
-
}
|
|
2161
|
-
});
|
|
2162
|
-
await adapter.update({
|
|
2163
|
-
model: tables.session.tableName,
|
|
2164
|
-
where: [
|
|
2165
|
-
{
|
|
2166
|
-
field: "id",
|
|
2167
|
-
value: session.id
|
|
2168
|
-
}
|
|
2169
|
-
],
|
|
2170
|
-
update: {
|
|
2171
|
-
/**
|
|
2172
|
-
* update the session to expire in 2 minute. This is to prevent
|
|
2173
|
-
* the session from expiring too quickly and logging the user out.
|
|
2174
|
-
*/
|
|
2175
|
-
expiresAt: new Date(Date.now() + 1e3 * 60 * 2)
|
|
2292
|
+
updateSession: async (sessionId, session) => {
|
|
2293
|
+
const updatedSession = await adapter.update({
|
|
2294
|
+
model: tables.session.tableName,
|
|
2295
|
+
where: [
|
|
2296
|
+
{
|
|
2297
|
+
field: "id",
|
|
2298
|
+
value: sessionId
|
|
2176
2299
|
}
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
}
|
|
2180
|
-
return
|
|
2300
|
+
],
|
|
2301
|
+
update: session
|
|
2302
|
+
});
|
|
2303
|
+
return updatedSession;
|
|
2181
2304
|
},
|
|
2182
2305
|
deleteSession: async (id) => {
|
|
2183
2306
|
const session = await adapter.delete({
|
|
@@ -2278,144 +2401,37 @@ var createFieldAttribute = (type, config) => {
|
|
|
2278
2401
|
};
|
|
2279
2402
|
};
|
|
2280
2403
|
|
|
2281
|
-
// src/utils/
|
|
2282
|
-
|
|
2283
|
-
function getCookies(options) {
|
|
2284
|
-
const secure = !!options.advanced?.useSecureCookies || process.env.NODE_ENV === "production";
|
|
2285
|
-
const secureCookiePrefix = secure ? "__Secure-" : "";
|
|
2286
|
-
const cookiePrefix = "better-auth";
|
|
2287
|
-
const sessionMaxAge = new TimeSpan3(7, "d").seconds();
|
|
2288
|
-
return {
|
|
2289
|
-
sessionToken: {
|
|
2290
|
-
name: `${secureCookiePrefix}${cookiePrefix}.session_token`,
|
|
2291
|
-
options: {
|
|
2292
|
-
httpOnly: true,
|
|
2293
|
-
sameSite: "lax",
|
|
2294
|
-
path: "/",
|
|
2295
|
-
secure,
|
|
2296
|
-
maxAge: sessionMaxAge
|
|
2297
|
-
}
|
|
2298
|
-
},
|
|
2299
|
-
csrfToken: {
|
|
2300
|
-
name: `${secureCookiePrefix ? "__Host-" : ""}${cookiePrefix}.csrf_token`,
|
|
2301
|
-
options: {
|
|
2302
|
-
httpOnly: true,
|
|
2303
|
-
sameSite: "lax",
|
|
2304
|
-
path: "/",
|
|
2305
|
-
secure,
|
|
2306
|
-
maxAge: 60 * 60 * 24 * 7
|
|
2307
|
-
}
|
|
2308
|
-
},
|
|
2309
|
-
state: {
|
|
2310
|
-
name: `${secureCookiePrefix}${cookiePrefix}.state`,
|
|
2311
|
-
options: {
|
|
2312
|
-
httpOnly: true,
|
|
2313
|
-
sameSite: "lax",
|
|
2314
|
-
path: "/",
|
|
2315
|
-
secure,
|
|
2316
|
-
maxAge: 60 * 15
|
|
2317
|
-
// 15 minutes in seconds
|
|
2318
|
-
}
|
|
2319
|
-
},
|
|
2320
|
-
pkCodeVerifier: {
|
|
2321
|
-
name: `${secureCookiePrefix}${cookiePrefix}.pk_code_verifier`,
|
|
2322
|
-
options: {
|
|
2323
|
-
httpOnly: true,
|
|
2324
|
-
sameSite: "lax",
|
|
2325
|
-
path: "/",
|
|
2326
|
-
secure,
|
|
2327
|
-
maxAge: 60 * 15
|
|
2328
|
-
// 15 minutes in seconds
|
|
2329
|
-
}
|
|
2330
|
-
},
|
|
2331
|
-
nonce: {
|
|
2332
|
-
name: `${secureCookiePrefix}${cookiePrefix}.nonce`,
|
|
2333
|
-
options: {
|
|
2334
|
-
httpOnly: true,
|
|
2335
|
-
sameSite: "lax",
|
|
2336
|
-
path: "/",
|
|
2337
|
-
secure,
|
|
2338
|
-
maxAge: 60 * 15
|
|
2339
|
-
// 15 minutes in seconds
|
|
2340
|
-
}
|
|
2341
|
-
}
|
|
2342
|
-
};
|
|
2343
|
-
}
|
|
2344
|
-
function createCookieGetter(options) {
|
|
2345
|
-
const secure = !!options.advanced?.useSecureCookies || process.env.NODE_ENV === "production";
|
|
2346
|
-
const secureCookiePrefix = secure ? "__Secure-" : "";
|
|
2347
|
-
const cookiePrefix = "better-auth";
|
|
2348
|
-
function getCookie(cookieName, options2) {
|
|
2349
|
-
return {
|
|
2350
|
-
name: process.env.NODE_ENV === "production" ? `${secureCookiePrefix}${cookiePrefix}.${cookieName}` : `${cookiePrefix}.${cookieName}`,
|
|
2351
|
-
options: {
|
|
2352
|
-
secure,
|
|
2353
|
-
sameSite: "lax",
|
|
2354
|
-
path: "/",
|
|
2355
|
-
maxAge: 60 * 15,
|
|
2356
|
-
// 15 minutes in seconds
|
|
2357
|
-
...options2
|
|
2358
|
-
}
|
|
2359
|
-
};
|
|
2360
|
-
}
|
|
2361
|
-
return getCookie;
|
|
2362
|
-
}
|
|
2363
|
-
|
|
2364
|
-
// src/utils/logger.ts
|
|
2365
|
-
import { createConsola } from "consola";
|
|
2366
|
-
var consola = createConsola({
|
|
2367
|
-
formatOptions: {
|
|
2368
|
-
date: false
|
|
2369
|
-
}
|
|
2370
|
-
});
|
|
2371
|
-
var createLogger = (options) => {
|
|
2372
|
-
return {
|
|
2373
|
-
log: (...args) => {
|
|
2374
|
-
!options?.disabled && consola.log("", ...args);
|
|
2375
|
-
},
|
|
2376
|
-
error: (...args) => {
|
|
2377
|
-
!options?.disabled && consola.error("", ...args);
|
|
2378
|
-
},
|
|
2379
|
-
warn: (...args) => {
|
|
2380
|
-
!options?.disabled && consola.warn("", ...args);
|
|
2381
|
-
},
|
|
2382
|
-
info: (...args) => {
|
|
2383
|
-
!options?.disabled && consola.info("", ...args);
|
|
2384
|
-
},
|
|
2385
|
-
debug: (...args) => {
|
|
2386
|
-
!options?.disabled && consola.debug("", ...args);
|
|
2387
|
-
},
|
|
2388
|
-
box: (...args) => {
|
|
2389
|
-
!options?.disabled && consola.box("", ...args);
|
|
2390
|
-
},
|
|
2391
|
-
success: (...args) => {
|
|
2392
|
-
!options?.disabled && consola.success("", ...args);
|
|
2393
|
-
},
|
|
2394
|
-
break: (...args) => {
|
|
2395
|
-
!options?.disabled && console.log("\n");
|
|
2396
|
-
}
|
|
2397
|
-
};
|
|
2398
|
-
};
|
|
2399
|
-
var logger = createLogger();
|
|
2404
|
+
// src/utils/constants.ts
|
|
2405
|
+
var DEFAULT_SECRET = "better-auth-secret-123456789";
|
|
2400
2406
|
|
|
2401
2407
|
// src/init.ts
|
|
2402
2408
|
var init = (options) => {
|
|
2403
2409
|
const adapter = getAdapter(options);
|
|
2404
2410
|
const db = createKyselyAdapter(options);
|
|
2405
|
-
const
|
|
2411
|
+
const baseURL = getBaseURL(options.baseURL, options.basePath);
|
|
2406
2412
|
return {
|
|
2407
2413
|
options: {
|
|
2408
2414
|
...options,
|
|
2409
|
-
baseURL,
|
|
2415
|
+
baseURL: baseURL ? new URL(baseURL).origin : "",
|
|
2410
2416
|
basePath: options.basePath || "/api/auth"
|
|
2411
2417
|
},
|
|
2412
|
-
baseURL:
|
|
2413
|
-
|
|
2418
|
+
baseURL: baseURL || "",
|
|
2419
|
+
session: {
|
|
2420
|
+
updateAge: options.session?.updateAge || 24 * 60 * 60,
|
|
2421
|
+
// 24 hours
|
|
2422
|
+
expiresIn: options.session?.expiresIn || 60 * 60 * 24 * 7
|
|
2423
|
+
// 7 days
|
|
2424
|
+
},
|
|
2425
|
+
secret: options.secret || process.env.BETTER_AUTH_SECRET || process.env.AUTH_SECRET || DEFAULT_SECRET,
|
|
2414
2426
|
authCookies: getCookies(options),
|
|
2415
2427
|
logger: createLogger({
|
|
2416
2428
|
disabled: options.disableLog
|
|
2417
2429
|
}),
|
|
2418
2430
|
db,
|
|
2431
|
+
password: {
|
|
2432
|
+
hash: options.emailAndPassword?.password?.hash || hashPassword,
|
|
2433
|
+
verify: options.emailAndPassword?.password?.verify || verifyPassword
|
|
2434
|
+
},
|
|
2419
2435
|
adapter,
|
|
2420
2436
|
internalAdapter: createInternalAdapter(adapter, options),
|
|
2421
2437
|
createAuthCookie: createCookieGetter(options)
|
|
@@ -2425,16 +2441,30 @@ var init = (options) => {
|
|
|
2425
2441
|
// src/auth.ts
|
|
2426
2442
|
var betterAuth = (options) => {
|
|
2427
2443
|
const authContext = init(options);
|
|
2428
|
-
const {
|
|
2444
|
+
const { api } = getEndpoints(authContext);
|
|
2429
2445
|
return {
|
|
2430
|
-
handler
|
|
2431
|
-
|
|
2432
|
-
|
|
2446
|
+
handler: async (request) => {
|
|
2447
|
+
const basePath = authContext.options.basePath;
|
|
2448
|
+
const url = new URL(request.url);
|
|
2449
|
+
if (!authContext.options.baseURL) {
|
|
2450
|
+
const baseURL = `${url.origin}/api/auth`;
|
|
2451
|
+
authContext.options.baseURL = baseURL;
|
|
2452
|
+
authContext.baseURL = baseURL;
|
|
2453
|
+
}
|
|
2454
|
+
if (!authContext.options.baseURL) {
|
|
2455
|
+
return new Response("Base URL not set", { status: 400 });
|
|
2456
|
+
}
|
|
2457
|
+
if (url.pathname === basePath || url.pathname === `${basePath}/`) {
|
|
2458
|
+
return new Response("Welcome to BetterAuth", { status: 200 });
|
|
2459
|
+
}
|
|
2460
|
+
const { handler } = router(authContext);
|
|
2461
|
+
return handler(request);
|
|
2462
|
+
},
|
|
2463
|
+
api,
|
|
2464
|
+
options: authContext.options
|
|
2433
2465
|
};
|
|
2434
2466
|
};
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
createInternalAdapter
|
|
2439
|
-
};
|
|
2467
|
+
|
|
2468
|
+
export { betterAuth, createFieldAttribute, createInternalAdapter };
|
|
2469
|
+
//# sourceMappingURL=index.js.map
|
|
2440
2470
|
//# sourceMappingURL=index.js.map
|