@convex-dev/better-auth 0.6.1 → 0.7.0-alpha.0
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/README.md +8 -2
- package/dist/commonjs/client/adapter.d.ts.map +1 -1
- package/dist/commonjs/client/adapter.js +27 -9
- package/dist/commonjs/client/adapter.js.map +1 -1
- package/dist/commonjs/client/cors.d.ts +9 -4
- package/dist/commonjs/client/cors.d.ts.map +1 -1
- package/dist/commonjs/client/cors.js +44 -28
- package/dist/commonjs/client/cors.js.map +1 -1
- package/dist/commonjs/client/index.d.ts +1 -2
- package/dist/commonjs/client/index.d.ts.map +1 -1
- package/dist/commonjs/client/index.js +57 -61
- package/dist/commonjs/client/index.js.map +1 -1
- package/dist/commonjs/component/lib.d.ts +58 -0
- package/dist/commonjs/component/lib.d.ts.map +1 -1
- package/dist/commonjs/component/lib.js +28 -0
- package/dist/commonjs/component/lib.js.map +1 -1
- package/dist/commonjs/component/schema.d.ts +2 -0
- package/dist/commonjs/component/schema.d.ts.map +1 -1
- package/dist/commonjs/component/schema.js +3 -1
- package/dist/commonjs/component/schema.js.map +1 -1
- package/dist/commonjs/component/util.d.ts +6 -0
- package/dist/commonjs/component/util.d.ts.map +1 -1
- package/dist/commonjs/nextjs/index.d.ts.map +1 -1
- package/dist/commonjs/nextjs/index.js +3 -2
- package/dist/commonjs/nextjs/index.js.map +1 -1
- package/dist/commonjs/plugins/convex/index.d.ts +111 -2
- package/dist/commonjs/plugins/convex/index.d.ts.map +1 -1
- package/dist/commonjs/plugins/convex/index.js +66 -5
- package/dist/commonjs/plugins/convex/index.js.map +1 -1
- package/dist/commonjs/react-router/index.d.ts +10 -0
- package/dist/commonjs/react-router/index.d.ts.map +1 -0
- package/dist/commonjs/react-router/index.js +24 -0
- package/dist/commonjs/react-router/index.js.map +1 -0
- package/dist/commonjs/react-start/index.d.ts.map +1 -1
- package/dist/commonjs/react-start/index.js +2 -2
- package/dist/commonjs/react-start/index.js.map +1 -1
- package/dist/commonjs/util.d.ts +2 -0
- package/dist/commonjs/util.d.ts.map +1 -0
- package/dist/commonjs/util.js +8 -0
- package/dist/commonjs/util.js.map +1 -0
- package/dist/esm/client/adapter.d.ts.map +1 -1
- package/dist/esm/client/adapter.js +27 -9
- package/dist/esm/client/adapter.js.map +1 -1
- package/dist/esm/client/cors.d.ts +9 -4
- package/dist/esm/client/cors.d.ts.map +1 -1
- package/dist/esm/client/cors.js +44 -28
- package/dist/esm/client/cors.js.map +1 -1
- package/dist/esm/client/index.d.ts +1 -2
- package/dist/esm/client/index.d.ts.map +1 -1
- package/dist/esm/client/index.js +57 -61
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/component/lib.d.ts +58 -0
- package/dist/esm/component/lib.d.ts.map +1 -1
- package/dist/esm/component/lib.js +28 -0
- package/dist/esm/component/lib.js.map +1 -1
- package/dist/esm/component/schema.d.ts +2 -0
- package/dist/esm/component/schema.d.ts.map +1 -1
- package/dist/esm/component/schema.js +3 -1
- package/dist/esm/component/schema.js.map +1 -1
- package/dist/esm/component/util.d.ts +6 -0
- package/dist/esm/component/util.d.ts.map +1 -1
- package/dist/esm/nextjs/index.d.ts.map +1 -1
- package/dist/esm/nextjs/index.js +3 -2
- package/dist/esm/nextjs/index.js.map +1 -1
- package/dist/esm/plugins/convex/index.d.ts +111 -2
- package/dist/esm/plugins/convex/index.d.ts.map +1 -1
- package/dist/esm/plugins/convex/index.js +66 -5
- package/dist/esm/plugins/convex/index.js.map +1 -1
- package/dist/esm/react-router/index.d.ts +10 -0
- package/dist/esm/react-router/index.d.ts.map +1 -0
- package/dist/esm/react-router/index.js +24 -0
- package/dist/esm/react-router/index.js.map +1 -0
- package/dist/esm/react-start/index.d.ts.map +1 -1
- package/dist/esm/react-start/index.js +2 -2
- package/dist/esm/react-start/index.js.map +1 -1
- package/dist/esm/util.d.ts +2 -0
- package/dist/esm/util.d.ts.map +1 -0
- package/dist/esm/util.js +8 -0
- package/dist/esm/util.js.map +1 -0
- package/package.json +1 -1
- package/src/client/adapter.ts +36 -11
- package/src/client/cors.ts +60 -38
- package/src/client/index.ts +65 -73
- package/src/component/_generated/api.d.ts +12 -0
- package/src/component/lib.ts +32 -0
- package/src/component/schema.ts +3 -1
- package/src/nextjs/index.ts +3 -2
- package/src/plugins/convex/index.ts +79 -11
- package/src/react-router/index.ts +31 -0
- package/src/react-start/index.ts +2 -2
- package/src/util.ts +7 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { createCookieGetter } from "better-auth/cookies";
|
|
2
2
|
import { betterFetch } from "@better-fetch/fetch";
|
|
3
|
+
import { JWT_COOKIE_NAME } from "../plugins/convex/index.js";
|
|
3
4
|
export const getCookieName = async (createAuth) => {
|
|
4
5
|
const auth = createAuth({});
|
|
5
6
|
const createCookie = createCookieGetter(auth.options);
|
|
6
|
-
const cookie = createCookie(
|
|
7
|
+
const cookie = createCookie(JWT_COOKIE_NAME);
|
|
7
8
|
return cookie.name;
|
|
8
9
|
};
|
|
9
10
|
export const fetchSession = async (createAuth, request) => {
|
|
@@ -15,7 +16,6 @@ export const fetchSession = async (createAuth, request) => {
|
|
|
15
16
|
baseURL,
|
|
16
17
|
headers: {
|
|
17
18
|
cookie: request.headers.get("cookie") ?? "",
|
|
18
|
-
origin: baseURL,
|
|
19
19
|
},
|
|
20
20
|
});
|
|
21
21
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react-start/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react-start/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,EAChC,UAAyE,EACzE,EAAE;IACF,MAAM,IAAI,GAAG,UAAU,CAAC,EAAS,CAAC,CAAC;IACnC,MAAM,YAAY,GAAG,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,YAAY,CAAC,eAAe,CAAC,CAAC;IAC7C,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAG/B,UAAa,EACb,OAAiB,EACjB,EAAE;IAGF,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACtC,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,WAAW,CACzC,uBAAuB,EACvB;QACE,OAAO;QACP,OAAO,EAAE;YACP,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE;SAC5C;KACF,CACF,CAAC;IACF,OAAO;QACL,OAAO;KACR,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,OAAgB,EAChB,IAAiC,EACjC,EAAE;IACF,MAAM,aAAa,GAAG,IAAI,EAAE,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;IAC9E,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,GAAG,aAAa,GAAG,UAAU,CAAC,QAAQ,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC;IAC7E,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,CAAC;IAC3D,OAAO,KAAK,CAAC,OAAO,EAAE,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;AACtE,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/util.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,WAMtC,CAAC"}
|
package/dist/esm/util.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/util.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,EAAE;IACzC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,kCAAkC,IAAI,IAAI,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC"}
|
package/package.json
CHANGED
package/src/client/adapter.ts
CHANGED
|
@@ -80,7 +80,7 @@ export const convexAdapter = <
|
|
|
80
80
|
limit,
|
|
81
81
|
}): Promise<any[]> => {
|
|
82
82
|
if (offset) {
|
|
83
|
-
throw new Error("
|
|
83
|
+
throw new Error("offset not supported");
|
|
84
84
|
}
|
|
85
85
|
if (
|
|
86
86
|
model === "jwks" &&
|
|
@@ -90,18 +90,12 @@ export const convexAdapter = <
|
|
|
90
90
|
) {
|
|
91
91
|
return ctx.runQuery(component.component.lib.getJwks, { limit });
|
|
92
92
|
}
|
|
93
|
-
if (
|
|
93
|
+
if (
|
|
94
|
+
where?.length !== 1 ||
|
|
95
|
+
(where[0].operator && where[0].operator !== "eq")
|
|
96
|
+
) {
|
|
94
97
|
throw new Error("where clause not supported");
|
|
95
98
|
}
|
|
96
|
-
if (offset) {
|
|
97
|
-
throw new Error("offset not supported");
|
|
98
|
-
}
|
|
99
|
-
if (model === "account" && where[0].field === "userId") {
|
|
100
|
-
return ctx.runQuery(component.component.lib.getAccountsByUserId, {
|
|
101
|
-
userId: where[0].value as any,
|
|
102
|
-
limit,
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
99
|
if (model === "verification" && where[0].field === "identifier") {
|
|
106
100
|
return ctx.runQuery(
|
|
107
101
|
component.component.lib.listVerificationsByIdentifier,
|
|
@@ -112,6 +106,18 @@ export const convexAdapter = <
|
|
|
112
106
|
}
|
|
113
107
|
);
|
|
114
108
|
}
|
|
109
|
+
if (model === "account" && where[0].field === "userId" && !sortBy) {
|
|
110
|
+
return ctx.runQuery(component.component.lib.getAccountsByUserId, {
|
|
111
|
+
userId: where[0].value as any,
|
|
112
|
+
limit,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (model === "session" && where[0].field === "userId" && !sortBy) {
|
|
116
|
+
return ctx.runQuery(component.component.lib.getSessionsByUserId, {
|
|
117
|
+
userId: where[0].value as any,
|
|
118
|
+
limit,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
115
121
|
throw new Error("where clause not supported");
|
|
116
122
|
},
|
|
117
123
|
count: async ({ where }) => {
|
|
@@ -184,6 +190,24 @@ export const convexAdapter = <
|
|
|
184
190
|
userId: where[0].value as any,
|
|
185
191
|
});
|
|
186
192
|
}
|
|
193
|
+
if (
|
|
194
|
+
model === "session" &&
|
|
195
|
+
where?.length === 2 &&
|
|
196
|
+
where[0].operator === "eq" &&
|
|
197
|
+
where[0].connector === "AND" &&
|
|
198
|
+
where[0].field === "userId" &&
|
|
199
|
+
where[1].operator === "lte" &&
|
|
200
|
+
where[1].field === "expiresAt" &&
|
|
201
|
+
typeof where[1].value === "number"
|
|
202
|
+
) {
|
|
203
|
+
return ctx.runMutation(
|
|
204
|
+
component.component.lib.deleteExpiredSessions,
|
|
205
|
+
{
|
|
206
|
+
userId: where[0].value as string,
|
|
207
|
+
expiresAt: where[1].value as number,
|
|
208
|
+
}
|
|
209
|
+
);
|
|
210
|
+
}
|
|
187
211
|
throw new Error("where clause not supported");
|
|
188
212
|
// return count;
|
|
189
213
|
},
|
|
@@ -208,6 +232,7 @@ export const convexAdapter = <
|
|
|
208
232
|
where[0].operator === "eq" &&
|
|
209
233
|
where[0].connector === "AND" &&
|
|
210
234
|
where[0].field === "userId" &&
|
|
235
|
+
where[1].operator === "eq" &&
|
|
211
236
|
where[1].field === "providerId"
|
|
212
237
|
) {
|
|
213
238
|
return ctx.runMutation(
|
package/src/client/cors.ts
CHANGED
|
@@ -13,14 +13,16 @@
|
|
|
13
13
|
* maintaining proper CORS configuration.
|
|
14
14
|
*/
|
|
15
15
|
import {
|
|
16
|
-
GenericActionCtx,
|
|
16
|
+
type GenericActionCtx,
|
|
17
17
|
httpActionGeneric,
|
|
18
18
|
httpRouter,
|
|
19
19
|
HttpRouter,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
ROUTABLE_HTTP_METHODS,
|
|
21
|
+
type RoutableMethod,
|
|
22
|
+
type PublicHttpAction,
|
|
23
|
+
type RouteSpec,
|
|
24
|
+
type RouteSpecWithPath,
|
|
25
|
+
type RouteSpecWithPathPrefix,
|
|
24
26
|
} from "convex/server";
|
|
25
27
|
|
|
26
28
|
export const DEFAULT_EXPOSED_HEADERS = [
|
|
@@ -46,7 +48,7 @@ export type CorsConfig = {
|
|
|
46
48
|
* - https://example.com
|
|
47
49
|
* @default ["*"]
|
|
48
50
|
*/
|
|
49
|
-
allowedOrigins?: string[];
|
|
51
|
+
allowedOrigins?: string[] | ((req: Request) => Promise<string[]>);
|
|
50
52
|
/**
|
|
51
53
|
* An array of allowed headers: what headers are allowed to be sent in
|
|
52
54
|
* the request.
|
|
@@ -68,10 +70,15 @@ export type CorsConfig = {
|
|
|
68
70
|
*/
|
|
69
71
|
browserCacheMaxAge?: number;
|
|
70
72
|
/**
|
|
71
|
-
* Whether to
|
|
73
|
+
* Whether to block requests from origins that are not in the allowedOrigins list.
|
|
74
|
+
* @default true
|
|
75
|
+
*/
|
|
76
|
+
enforceAllowOrigins?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Whether to log debugging information about CORS requests.
|
|
72
79
|
* @default false
|
|
73
80
|
*/
|
|
74
|
-
|
|
81
|
+
debug?: boolean;
|
|
75
82
|
};
|
|
76
83
|
|
|
77
84
|
type RouteSpecWithCors = RouteSpec & CorsConfig;
|
|
@@ -219,8 +226,6 @@ export default corsRouter;
|
|
|
219
226
|
* to web applications hosted on different domains.
|
|
220
227
|
*/
|
|
221
228
|
|
|
222
|
-
import { ROUTABLE_HTTP_METHODS, RoutableMethod } from "convex/server";
|
|
223
|
-
|
|
224
229
|
const SECONDS_IN_A_DAY = 60 * 60 * 24;
|
|
225
230
|
|
|
226
231
|
/**
|
|
@@ -240,7 +245,8 @@ const handleCors = ({
|
|
|
240
245
|
exposedHeaders = DEFAULT_EXPOSED_HEADERS,
|
|
241
246
|
allowCredentials = false,
|
|
242
247
|
browserCacheMaxAge = SECONDS_IN_A_DAY,
|
|
243
|
-
|
|
248
|
+
enforceAllowOrigins = true,
|
|
249
|
+
debug = false,
|
|
244
250
|
}: {
|
|
245
251
|
originalHandler?: PublicHttpAction;
|
|
246
252
|
allowedMethods?: string[];
|
|
@@ -279,9 +285,17 @@ const handleCors = ({
|
|
|
279
285
|
commonHeaders["Access-Control-Expose-Headers"] = exposedHeaders.join(", ");
|
|
280
286
|
}
|
|
281
287
|
|
|
288
|
+
async function parseAllowedOrigins(request: Request): Promise<string[]> {
|
|
289
|
+
return Array.isArray(allowedOrigins)
|
|
290
|
+
? allowedOrigins
|
|
291
|
+
: await allowedOrigins(request);
|
|
292
|
+
}
|
|
293
|
+
|
|
282
294
|
// Helper function to check if origin is allowed (including wildcard subdomain matching)
|
|
283
|
-
function isAllowedOrigin(
|
|
284
|
-
|
|
295
|
+
async function isAllowedOrigin(request: Request): Promise<boolean> {
|
|
296
|
+
const requestOrigin = request.headers.get("origin");
|
|
297
|
+
if (!requestOrigin) return false;
|
|
298
|
+
return (await parseAllowedOrigins(request)).some((allowed) => {
|
|
285
299
|
if (allowed === "*") return true;
|
|
286
300
|
if (allowed === requestOrigin) return true;
|
|
287
301
|
if (allowed.startsWith("*.")) {
|
|
@@ -307,38 +321,38 @@ const handleCors = ({
|
|
|
307
321
|
*/
|
|
308
322
|
return httpActionGeneric(
|
|
309
323
|
async (ctx: GenericActionCtx<any>, request: Request) => {
|
|
310
|
-
if (
|
|
311
|
-
console.log("
|
|
312
|
-
|
|
313
|
-
|
|
324
|
+
if (debug) {
|
|
325
|
+
console.log("CORS request", {
|
|
326
|
+
path: request.url,
|
|
327
|
+
origin: request.headers.get("origin"),
|
|
328
|
+
headers: request.headers,
|
|
329
|
+
method: request.method,
|
|
330
|
+
body: request.body,
|
|
331
|
+
});
|
|
314
332
|
}
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
request.headers.get("Referer") ??
|
|
318
|
-
request.headers.get("Expo-Origin");
|
|
333
|
+
const requestOrigin = request.headers.get("origin");
|
|
334
|
+
const parsedAllowedOrigins = await parseAllowedOrigins(request);
|
|
319
335
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
? new URL(requestOriginRaw).origin
|
|
324
|
-
: requestOriginRaw;
|
|
336
|
+
if (debug) {
|
|
337
|
+
console.log("allowed origins", parsedAllowedOrigins);
|
|
338
|
+
}
|
|
325
339
|
|
|
326
340
|
// Handle origin matching
|
|
327
341
|
let allowOrigins: string | null = null;
|
|
328
|
-
if (
|
|
342
|
+
if (parsedAllowedOrigins.includes("*") && !allowCredentials) {
|
|
329
343
|
allowOrigins = "*";
|
|
330
344
|
} else if (requestOrigin) {
|
|
331
345
|
// Check if the request origin matches any of the allowed origins
|
|
332
346
|
// (including wildcard subdomain matching if configured)
|
|
333
|
-
if (isAllowedOrigin(
|
|
347
|
+
if (await isAllowedOrigin(request)) {
|
|
334
348
|
allowOrigins = requestOrigin;
|
|
335
349
|
}
|
|
336
350
|
}
|
|
337
351
|
|
|
338
|
-
if (!allowOrigins) {
|
|
352
|
+
if (enforceAllowOrigins && !allowOrigins) {
|
|
339
353
|
// Origin not allowed
|
|
340
354
|
console.error(
|
|
341
|
-
`Request from origin ${requestOrigin} blocked, missing from allowed origins
|
|
355
|
+
`Request from origin ${requestOrigin} blocked, missing from allowed origins: ${parsedAllowedOrigins.join()}`
|
|
342
356
|
);
|
|
343
357
|
return new Response(null, { status: 403 });
|
|
344
358
|
}
|
|
@@ -346,15 +360,19 @@ const handleCors = ({
|
|
|
346
360
|
* OPTIONS has no handler and just returns headers
|
|
347
361
|
*/
|
|
348
362
|
if (request.method === "OPTIONS") {
|
|
363
|
+
const responseHeaders = new Headers({
|
|
364
|
+
...commonHeaders,
|
|
365
|
+
"Access-Control-Allow-Origin": allowOrigins ?? "",
|
|
366
|
+
"Access-Control-Allow-Methods": allowMethods,
|
|
367
|
+
"Access-Control-Allow-Headers": allowedHeaders.join(", "),
|
|
368
|
+
"Access-Control-Max-Age": browserCacheMaxAge.toString(),
|
|
369
|
+
});
|
|
370
|
+
if (debug) {
|
|
371
|
+
console.log("CORS OPTIONS response headers", responseHeaders);
|
|
372
|
+
}
|
|
349
373
|
return new Response(null, {
|
|
350
374
|
status: 204,
|
|
351
|
-
headers:
|
|
352
|
-
...commonHeaders,
|
|
353
|
-
"Access-Control-Allow-Origin": allowOrigins,
|
|
354
|
-
"Access-Control-Allow-Methods": allowMethods,
|
|
355
|
-
"Access-Control-Allow-Headers": allowedHeaders.join(", "),
|
|
356
|
-
"Access-Control-Max-Age": browserCacheMaxAge.toString(),
|
|
357
|
-
}),
|
|
375
|
+
headers: responseHeaders,
|
|
358
376
|
});
|
|
359
377
|
}
|
|
360
378
|
|
|
@@ -380,7 +398,7 @@ const handleCors = ({
|
|
|
380
398
|
* Second, get a copy of the original response's headers
|
|
381
399
|
*/
|
|
382
400
|
const newHeaders = new Headers(originalResponse.headers);
|
|
383
|
-
newHeaders.set("Access-Control-Allow-Origin", allowOrigins);
|
|
401
|
+
newHeaders.set("Access-Control-Allow-Origin", allowOrigins ?? "");
|
|
384
402
|
|
|
385
403
|
/**
|
|
386
404
|
* Third, add or update our CORS headers
|
|
@@ -389,6 +407,10 @@ const handleCors = ({
|
|
|
389
407
|
newHeaders.set(key, value);
|
|
390
408
|
});
|
|
391
409
|
|
|
410
|
+
if (debug) {
|
|
411
|
+
console.log("CORS response headers", newHeaders);
|
|
412
|
+
}
|
|
413
|
+
|
|
392
414
|
/**
|
|
393
415
|
* Fourth, return the modified Response.
|
|
394
416
|
* A Response object is immutable, so we create a new one to return here.
|
package/src/client/index.ts
CHANGED
|
@@ -22,6 +22,8 @@ import { betterAuth } from "better-auth";
|
|
|
22
22
|
import { omit } from "convex-helpers";
|
|
23
23
|
import { createCookieGetter } from "better-auth/cookies";
|
|
24
24
|
import { fetchQuery } from "convex/nextjs";
|
|
25
|
+
import { JWT_COOKIE_NAME } from "../plugins/convex";
|
|
26
|
+
import { requireEnv } from "../util";
|
|
25
27
|
export { convexAdapter };
|
|
26
28
|
|
|
27
29
|
const createUserFields = omit(schema.tables.user.validator.fields, ["userId"]);
|
|
@@ -147,7 +149,7 @@ export class BetterAuth<UserId extends string = string> {
|
|
|
147
149
|
) {
|
|
148
150
|
const auth = createAuth({} as any);
|
|
149
151
|
const createCookie = createCookieGetter(auth.options);
|
|
150
|
-
const cookie = createCookie(
|
|
152
|
+
const cookie = createCookie(JWT_COOKIE_NAME);
|
|
151
153
|
return cookie.name;
|
|
152
154
|
}
|
|
153
155
|
|
|
@@ -234,41 +236,10 @@ export class BetterAuth<UserId extends string = string> {
|
|
|
234
236
|
registerRoutes(
|
|
235
237
|
http: HttpRouter,
|
|
236
238
|
createAuth: (ctx: GenericActionCtx<any>) => ReturnType<typeof betterAuth>,
|
|
237
|
-
opts
|
|
238
|
-
path?: string;
|
|
239
|
-
allowedOrigins?: string[];
|
|
240
|
-
}
|
|
239
|
+
opts = { cors: false }
|
|
241
240
|
) {
|
|
242
|
-
const
|
|
243
|
-
const
|
|
244
|
-
const trustedOriginsOption: string[] = Array.isArray(options.trustedOrigins)
|
|
245
|
-
? options.trustedOrigins
|
|
246
|
-
: [];
|
|
247
|
-
|
|
248
|
-
const trustedOrigins = createAuth({} as any).options.plugins?.reduce(
|
|
249
|
-
(acc, plugin) => {
|
|
250
|
-
if (plugin.options?.trustedOrigins) {
|
|
251
|
-
acc.push(...plugin.options.trustedOrigins);
|
|
252
|
-
}
|
|
253
|
-
return acc;
|
|
254
|
-
},
|
|
255
|
-
[...trustedOriginsOption, options.baseURL].filter(Boolean) as string[]
|
|
256
|
-
);
|
|
257
|
-
// Reuse trustedOrigins as default for allowedOrigins
|
|
258
|
-
const allowedOrigins =
|
|
259
|
-
opts?.allowedOrigins ??
|
|
260
|
-
trustedOrigins?.map((origin) =>
|
|
261
|
-
// Strip trailing wildcards, unsupported for allowedOrigins
|
|
262
|
-
origin.endsWith("*") && origin.length > 1 ? origin.slice(0, -1) : origin
|
|
263
|
-
);
|
|
264
|
-
const requireEnv = (name: string) => {
|
|
265
|
-
const value = process.env[name];
|
|
266
|
-
if (value === undefined) {
|
|
267
|
-
throw new Error(`Missing environment variable \`${name}\``);
|
|
268
|
-
}
|
|
269
|
-
return value;
|
|
270
|
-
};
|
|
271
|
-
|
|
241
|
+
const betterAuthOptions = createAuth({} as any).options;
|
|
242
|
+
const path = betterAuthOptions.basePath ?? "/api/auth";
|
|
272
243
|
const authRequestHandler = httpActionGeneric(async (ctx, request) => {
|
|
273
244
|
const auth = createAuth(ctx);
|
|
274
245
|
const response = await auth.handler(request);
|
|
@@ -278,14 +249,7 @@ export class BetterAuth<UserId extends string = string> {
|
|
|
278
249
|
return response;
|
|
279
250
|
});
|
|
280
251
|
|
|
281
|
-
|
|
282
|
-
allowedOrigins,
|
|
283
|
-
allowCredentials: true,
|
|
284
|
-
allowedHeaders: ["Authorization", "Content-Type", "Better-Auth-Cookie"],
|
|
285
|
-
verbose: this.config?.verbose,
|
|
286
|
-
exposedHeaders: ["Set-Better-Auth-Cookie"],
|
|
287
|
-
});
|
|
288
|
-
|
|
252
|
+
// Redirect root well-known to api well-known
|
|
289
253
|
http.route({
|
|
290
254
|
path: "/.well-known/openid-configuration",
|
|
291
255
|
method: "GET",
|
|
@@ -295,40 +259,68 @@ export class BetterAuth<UserId extends string = string> {
|
|
|
295
259
|
}),
|
|
296
260
|
});
|
|
297
261
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
http.route({
|
|
305
|
-
path: `${path}/convex/jwks`,
|
|
306
|
-
method: "GET",
|
|
307
|
-
handler: authRequestHandler,
|
|
308
|
-
});
|
|
262
|
+
if (!opts.cors) {
|
|
263
|
+
http.route({
|
|
264
|
+
pathPrefix: `${path}/`,
|
|
265
|
+
method: "GET",
|
|
266
|
+
handler: authRequestHandler,
|
|
267
|
+
});
|
|
309
268
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
269
|
+
http.route({
|
|
270
|
+
pathPrefix: `${path}/`,
|
|
271
|
+
method: "POST",
|
|
272
|
+
handler: authRequestHandler,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
315
275
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
276
|
+
const trustedOrigins = [
|
|
277
|
+
...(Array.isArray(betterAuthOptions.trustedOrigins)
|
|
278
|
+
? betterAuthOptions.trustedOrigins
|
|
279
|
+
: [betterAuthOptions.trustedOrigins]),
|
|
280
|
+
betterAuthOptions.baseURL!,
|
|
281
|
+
];
|
|
282
|
+
// The crossDomain plugin adds siteUrl to trustedOrigins
|
|
283
|
+
const trustedOriginsFromPlugins =
|
|
284
|
+
betterAuthOptions.plugins?.reduce((acc, plugin) => {
|
|
285
|
+
if (plugin.options?.trustedOrigins) {
|
|
286
|
+
acc.push(...plugin.options.trustedOrigins);
|
|
287
|
+
}
|
|
288
|
+
return acc;
|
|
289
|
+
}, [] as string[]) ?? [];
|
|
321
290
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
291
|
+
// Reuse trustedOrigins as default for allowedOrigins
|
|
292
|
+
const allowedOrigins = async (request: Request) => {
|
|
293
|
+
return (
|
|
294
|
+
await Promise.all(
|
|
295
|
+
[...trustedOrigins, ...trustedOriginsFromPlugins].map(
|
|
296
|
+
async (origin) => {
|
|
297
|
+
if (!origin) {
|
|
298
|
+
return [];
|
|
299
|
+
}
|
|
300
|
+
if (typeof origin === "function") {
|
|
301
|
+
return origin(request);
|
|
302
|
+
}
|
|
303
|
+
return [origin];
|
|
304
|
+
}
|
|
305
|
+
)
|
|
306
|
+
)
|
|
307
|
+
)
|
|
308
|
+
.flat()
|
|
309
|
+
.map((origin) =>
|
|
310
|
+
// Strip trailing wildcards, unsupported for allowedOrigins
|
|
311
|
+
origin.endsWith("*") && origin.length > 1
|
|
312
|
+
? origin.slice(0, -1)
|
|
313
|
+
: origin
|
|
314
|
+
);
|
|
315
|
+
};
|
|
327
316
|
|
|
328
|
-
http
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
317
|
+
const cors = corsRouter(http, {
|
|
318
|
+
allowedOrigins,
|
|
319
|
+
allowCredentials: true,
|
|
320
|
+
allowedHeaders: ["Content-Type", "Better-Auth-Cookie"],
|
|
321
|
+
exposedHeaders: ["Set-Better-Auth-Cookie"],
|
|
322
|
+
debug: this.config?.verbose,
|
|
323
|
+
enforceAllowOrigins: false,
|
|
332
324
|
});
|
|
333
325
|
|
|
334
326
|
cors.route({
|
|
@@ -130,6 +130,12 @@ export type Mounts = {
|
|
|
130
130
|
},
|
|
131
131
|
any
|
|
132
132
|
>;
|
|
133
|
+
deleteExpiredSessions: FunctionReference<
|
|
134
|
+
"mutation",
|
|
135
|
+
"public",
|
|
136
|
+
{ expiresAt: number; userId: string },
|
|
137
|
+
any
|
|
138
|
+
>;
|
|
133
139
|
deleteOldVerifications: FunctionReference<
|
|
134
140
|
"action",
|
|
135
141
|
"public",
|
|
@@ -188,6 +194,12 @@ export type Mounts = {
|
|
|
188
194
|
>;
|
|
189
195
|
getCurrentSession: FunctionReference<"query", "public", {}, any>;
|
|
190
196
|
getJwks: FunctionReference<"query", "public", { limit?: number }, any>;
|
|
197
|
+
getSessionsByUserId: FunctionReference<
|
|
198
|
+
"query",
|
|
199
|
+
"public",
|
|
200
|
+
{ limit?: number; userId: string },
|
|
201
|
+
any
|
|
202
|
+
>;
|
|
191
203
|
listVerificationsByIdentifier: FunctionReference<
|
|
192
204
|
"query",
|
|
193
205
|
"public",
|
package/src/component/lib.ts
CHANGED
|
@@ -181,6 +181,19 @@ export const getAccountsByUserId = query({
|
|
|
181
181
|
},
|
|
182
182
|
});
|
|
183
183
|
|
|
184
|
+
export const getSessionsByUserId = query({
|
|
185
|
+
args: { userId: v.string(), limit: v.optional(v.number()) },
|
|
186
|
+
handler: async (ctx, args) => {
|
|
187
|
+
const query = ctx.db
|
|
188
|
+
.query("session")
|
|
189
|
+
.withIndex("userId", (q) => q.eq("userId", args.userId));
|
|
190
|
+
const docs = args.limit
|
|
191
|
+
? await query.take(args.limit)
|
|
192
|
+
: await query.collect();
|
|
193
|
+
return docs.map((doc) => transformOutput(doc, "session"));
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
|
|
184
197
|
export const getJwks = query({
|
|
185
198
|
args: {
|
|
186
199
|
limit: v.optional(v.number()),
|
|
@@ -276,6 +289,25 @@ export const deleteOldVerifications = action({
|
|
|
276
289
|
},
|
|
277
290
|
});
|
|
278
291
|
|
|
292
|
+
export const deleteExpiredSessions = mutation({
|
|
293
|
+
args: {
|
|
294
|
+
userId: v.string(),
|
|
295
|
+
expiresAt: v.number(),
|
|
296
|
+
},
|
|
297
|
+
handler: async (ctx, args) => {
|
|
298
|
+
const docs = await ctx.db
|
|
299
|
+
.query("session")
|
|
300
|
+
.withIndex("userId_expiresAt", (q) =>
|
|
301
|
+
q.eq("userId", args.userId).lt("expiresAt", args.expiresAt)
|
|
302
|
+
)
|
|
303
|
+
.collect();
|
|
304
|
+
await asyncMap(docs, async (doc) => {
|
|
305
|
+
await ctx.db.delete(doc._id);
|
|
306
|
+
});
|
|
307
|
+
return docs.length;
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
|
|
279
311
|
export const deleteAllForUserPage = mutation({
|
|
280
312
|
args: {
|
|
281
313
|
table: v.string(),
|
package/src/component/schema.ts
CHANGED
|
@@ -25,7 +25,9 @@ const schema = defineSchema({
|
|
|
25
25
|
userId: v.string(),
|
|
26
26
|
})
|
|
27
27
|
.index("token", ["token"])
|
|
28
|
-
.index("userId", ["userId"])
|
|
28
|
+
.index("userId", ["userId"])
|
|
29
|
+
.index("expiresAt", ["expiresAt"])
|
|
30
|
+
.index("userId_expiresAt", ["userId", "expiresAt"]),
|
|
29
31
|
|
|
30
32
|
account: defineTable({
|
|
31
33
|
accountId: v.string(),
|
package/src/nextjs/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { betterAuth } from "better-auth";
|
|
2
2
|
import { createCookieGetter } from "better-auth/cookies";
|
|
3
3
|
import { GenericActionCtx } from "convex/server";
|
|
4
|
+
import { JWT_COOKIE_NAME } from "../plugins/convex";
|
|
4
5
|
|
|
5
6
|
export const getToken = async (
|
|
6
7
|
createAuth: (ctx: GenericActionCtx<any>) => ReturnType<typeof betterAuth>
|
|
@@ -9,9 +10,9 @@ export const getToken = async (
|
|
|
9
10
|
const cookieStore = await cookies();
|
|
10
11
|
const auth = createAuth({} as any);
|
|
11
12
|
const createCookie = createCookieGetter(auth.options);
|
|
12
|
-
const cookie = createCookie(
|
|
13
|
+
const cookie = createCookie(JWT_COOKIE_NAME);
|
|
13
14
|
const token = cookieStore.get(cookie.name);
|
|
14
|
-
return
|
|
15
|
+
return token?.value;
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
const handler = (request: Request, opts?: { convexSiteUrl?: string }) => {
|