@nuxtjs/better-auth 0.3.5 → 0.3.7
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/module.json +1 -1
- package/dist/module.mjs +61 -31
- package/dist/runtime/app/composables/useAuthClient.js +4 -1
- package/dist/runtime/app/composables/useAuthRequestFetch.js +25 -9
- package/dist/runtime/app/composables/useUserSession.js +3 -1
- package/dist/runtime/app/middleware/auth.global.js +9 -10
- package/dist/runtime/internal/auth-base-url.d.ts +2 -0
- package/dist/runtime/internal/auth-base-url.js +5 -0
- package/dist/runtime/server/api/_better-auth/_schema.d.ts +7 -0
- package/dist/runtime/server/api/_better-auth/accounts.get.js +8 -1
- package/dist/runtime/server/internal/cookie-headers.d.ts +1 -0
- package/dist/runtime/server/internal/cookie-headers.js +1 -1
- package/dist/runtime/server/utils/auth.js +14 -23
- package/dist/runtime/server/utils/session.js +30 -2
- package/dist/runtime/server/virtual-modules.d.ts +0 -4
- package/dist/runtime/test-utils/e2e.js +4 -2
- package/package.json +3 -3
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -13,7 +13,7 @@ import { isCI, isTest } from 'std-env';
|
|
|
13
13
|
import { isInternalAssetPath } from '../dist/runtime/internal/auth-route-rules.js';
|
|
14
14
|
export { defineClientAuth, defineServerAuth } from '../dist/runtime/config.js';
|
|
15
15
|
|
|
16
|
-
const version = "0.3.
|
|
16
|
+
const version = "0.3.7";
|
|
17
17
|
|
|
18
18
|
const CONFIG_EXTENSIONS = [".ts", ".js", ".mts", ".cts", ".mjs", ".cjs"];
|
|
19
19
|
const CONFIG_EXTENSION_RE = /\.[cm]?[jt]s$/;
|
|
@@ -332,8 +332,7 @@ function registerPrepareTypesHook(input) {
|
|
|
332
332
|
nodeTsConfig.compilerOptions.paths ||= {};
|
|
333
333
|
const projectReferenceTypePaths = [
|
|
334
334
|
join(nuxt.options.buildDir, "types/nitro-imports.d.ts"),
|
|
335
|
-
join(nuxt.options.buildDir, "types/auth-database.d.ts")
|
|
336
|
-
join(nuxt.options.buildDir, "types/auth-schema.d.ts")
|
|
335
|
+
join(nuxt.options.buildDir, "types/auth-database.d.ts")
|
|
337
336
|
];
|
|
338
337
|
if (hasHubDb)
|
|
339
338
|
projectReferenceTypePaths.push(join(nuxt.options.buildDir, "hub/db.d.ts"));
|
|
@@ -342,7 +341,8 @@ function registerPrepareTypesHook(input) {
|
|
|
342
341
|
"#auth/server": nuxt.options.alias["#auth/server"],
|
|
343
342
|
"#auth/client": nuxt.options.alias["#auth/client"],
|
|
344
343
|
"#auth/database": nuxt.options.alias["#auth/database"],
|
|
345
|
-
|
|
344
|
+
// An exact .mjs path bypasses declaration lookup in TypeScript paths.
|
|
345
|
+
"#auth/schema": join(nuxt.options.buildDir, "better-auth/schema"),
|
|
346
346
|
"#auth/route-rules": nuxt.options.alias["#auth/route-rules"]
|
|
347
347
|
};
|
|
348
348
|
for (const [key, value] of Object.entries(exactNodeAliases)) {
|
|
@@ -607,6 +607,18 @@ async function setupBetterAuthSchema(nuxt, serverConfigPath, options, consola, h
|
|
|
607
607
|
const hubCasing = getHubCasing(hub);
|
|
608
608
|
const schemaOptions = { ...options.schema, useUuid: userConfig.advanced?.database?.generateId === "uuid", casing: options.schema?.casing ?? hubCasing };
|
|
609
609
|
const schemaCode = await generateDrizzleSchema(authOptions, dialect, schemaOptions);
|
|
610
|
+
const { getAuthTables } = await import('better-auth/db');
|
|
611
|
+
const { initGetModelName } = await import('better-auth/adapters');
|
|
612
|
+
const authTables = getAuthTables(authOptions);
|
|
613
|
+
const getModelName = initGetModelName({
|
|
614
|
+
schema: authTables,
|
|
615
|
+
usePlural: schemaOptions.usePlural ?? false
|
|
616
|
+
});
|
|
617
|
+
const modelNames = {};
|
|
618
|
+
for (const name of ["user", "session", "account", "verification"]) {
|
|
619
|
+
if (authTables[name])
|
|
620
|
+
modelNames[name] = getModelName(name);
|
|
621
|
+
}
|
|
610
622
|
const schemaDir = join(nuxt.options.buildDir, "better-auth");
|
|
611
623
|
const schemaPathTs = join(schemaDir, `schema.${dialect}.ts`);
|
|
612
624
|
const schemaPathMjs = join(schemaDir, `schema.${dialect}.mjs`);
|
|
@@ -622,6 +634,7 @@ async function setupBetterAuthSchema(nuxt, serverConfigPath, options, consola, h
|
|
|
622
634
|
addTemplate({ filename: `better-auth/schema.${dialect}.ts`, getContents: () => schemaCode, write: true });
|
|
623
635
|
addTemplate({ filename: `better-auth/schema.${dialect}.mjs`, getContents: () => schemaCode, write: true });
|
|
624
636
|
consola.info(`Generated ${dialect} schema (.ts + .mjs)`);
|
|
637
|
+
return modelNames;
|
|
625
638
|
} catch (error) {
|
|
626
639
|
throw error instanceof Diagnostic ? error : diagnostics.NUXT_AUTH_SCHEMA_GENERATION_FAILED({ cause: error });
|
|
627
640
|
}
|
|
@@ -755,7 +768,7 @@ function setupRuntimeConfig(input) {
|
|
|
755
768
|
if (clientOnly) {
|
|
756
769
|
const siteUrl = nuxt.options.runtimeConfig.public.siteUrl;
|
|
757
770
|
if (!siteUrl)
|
|
758
|
-
consola.warn("clientOnly mode: set runtimeConfig.public.siteUrl (or NUXT_PUBLIC_SITE_URL) to your
|
|
771
|
+
consola.warn("clientOnly mode: set runtimeConfig.public.siteUrl (or NUXT_PUBLIC_SITE_URL) to your auth backend URL, or set baseURL in defineClientAuth()");
|
|
759
772
|
consola.info("clientOnly mode enabled - server utilities (serverAuth, getRequestSession, getUserSession, requireUserSession) are not available");
|
|
760
773
|
return { secondaryStorageEnabled };
|
|
761
774
|
}
|
|
@@ -914,7 +927,7 @@ export { db }`;
|
|
|
914
927
|
return `export function createDatabase() { return undefined }
|
|
915
928
|
export const db = undefined`;
|
|
916
929
|
}
|
|
917
|
-
function buildSchemaExportCode(hasHubDb, hubDialect) {
|
|
930
|
+
function buildSchemaExportCode(hasHubDb, hubDialect, modelNames = {}) {
|
|
918
931
|
if (!hasHubDb) {
|
|
919
932
|
return `export const user = undefined
|
|
920
933
|
export const session = undefined
|
|
@@ -927,13 +940,43 @@ export const schema = undefined
|
|
|
927
940
|
import * as generatedSchema from './schema.${hubDialect}.mjs'
|
|
928
941
|
|
|
929
942
|
const getGeneratedTable = name => generatedSchema[name] ?? generatedSchema[\`\${name}s\`]
|
|
930
|
-
export const user = getGeneratedTable(
|
|
931
|
-
export const session = getGeneratedTable(
|
|
932
|
-
export const account = getGeneratedTable(
|
|
933
|
-
export const verification = getGeneratedTable(
|
|
943
|
+
export const user = getGeneratedTable(${JSON.stringify(modelNames.user ?? "user")})
|
|
944
|
+
export const session = getGeneratedTable(${JSON.stringify(modelNames.session ?? "session")})
|
|
945
|
+
export const account = getGeneratedTable(${JSON.stringify(modelNames.account ?? "account")})
|
|
946
|
+
export const verification = getGeneratedTable(${JSON.stringify(modelNames.verification ?? "verification")})
|
|
934
947
|
export const schema = { ...generatedSchema, user, session, account, verification }
|
|
935
948
|
`;
|
|
936
949
|
}
|
|
950
|
+
function buildSchemaExportTypes(hasHubDb, hubDialect, modelNames = {}) {
|
|
951
|
+
if (!hasHubDb) {
|
|
952
|
+
return `export declare const user: undefined
|
|
953
|
+
export declare const session: undefined
|
|
954
|
+
export declare const account: undefined
|
|
955
|
+
export declare const verification: undefined
|
|
956
|
+
export declare const schema: undefined
|
|
957
|
+
`;
|
|
958
|
+
}
|
|
959
|
+
return `export * from './schema.${hubDialect}.js'
|
|
960
|
+
|
|
961
|
+
type GeneratedSchema = typeof import('./schema.${hubDialect}.js')
|
|
962
|
+
type GeneratedTable<Name extends string> = Name extends keyof GeneratedSchema
|
|
963
|
+
? GeneratedSchema[Name]
|
|
964
|
+
: \`\${Name}s\` extends keyof GeneratedSchema
|
|
965
|
+
? GeneratedSchema[\`\${Name}s\`]
|
|
966
|
+
: undefined
|
|
967
|
+
|
|
968
|
+
export declare const user: GeneratedTable<${JSON.stringify(modelNames.user ?? "user")}>
|
|
969
|
+
export declare const session: GeneratedTable<${JSON.stringify(modelNames.session ?? "session")}>
|
|
970
|
+
export declare const account: GeneratedTable<${JSON.stringify(modelNames.account ?? "account")}>
|
|
971
|
+
export declare const verification: GeneratedTable<${JSON.stringify(modelNames.verification ?? "verification")}>
|
|
972
|
+
export declare const schema: GeneratedSchema & {
|
|
973
|
+
user: typeof user
|
|
974
|
+
session: typeof session
|
|
975
|
+
account: typeof account
|
|
976
|
+
verification: typeof verification
|
|
977
|
+
}
|
|
978
|
+
`;
|
|
979
|
+
}
|
|
937
980
|
function buildAuthRouteRulesCode(authRouteRules) {
|
|
938
981
|
return `export const authRouteRules = ${JSON.stringify(authRouteRules, null, 2)}
|
|
939
982
|
`;
|
|
@@ -1165,24 +1208,6 @@ declare module '#auth/database' {
|
|
|
1165
1208
|
export function createDatabase(event?: import('${h3TypesPath}').H3Event): BetterAuthOptions['database']
|
|
1166
1209
|
export const db: ${hasHubDb ? `typeof import('@nuxthub/db')['db']` : "undefined"}
|
|
1167
1210
|
}
|
|
1168
|
-
`
|
|
1169
|
-
}, { nitro: true });
|
|
1170
|
-
addTypeTemplate({
|
|
1171
|
-
filename: "types/auth-schema.d.ts",
|
|
1172
|
-
getContents: () => `
|
|
1173
|
-
declare module '#auth/schema' {
|
|
1174
|
-
export const user: any
|
|
1175
|
-
export const session: any
|
|
1176
|
-
export const account: any
|
|
1177
|
-
export const verification: any
|
|
1178
|
-
export const schema: {
|
|
1179
|
-
user: any
|
|
1180
|
-
session: any
|
|
1181
|
-
account: any
|
|
1182
|
-
verification: any
|
|
1183
|
-
[key: string]: any
|
|
1184
|
-
} | undefined
|
|
1185
|
-
}
|
|
1186
1211
|
`
|
|
1187
1212
|
}, { nitro: true });
|
|
1188
1213
|
addTypeTemplate({
|
|
@@ -1190,7 +1215,6 @@ declare module '#auth/schema' {
|
|
|
1190
1215
|
getContents: () => `
|
|
1191
1216
|
/// <reference path="./nitro-imports.d.ts" />
|
|
1192
1217
|
/// <reference path="./auth-database.d.ts" />
|
|
1193
|
-
/// <reference path="./auth-schema.d.ts" />
|
|
1194
1218
|
${hasHubDb ? '/// <reference path="../hub/db.d.ts" />' : ""}
|
|
1195
1219
|
|
|
1196
1220
|
export {}
|
|
@@ -1646,6 +1670,7 @@ const module$1 = defineNuxtModule({
|
|
|
1646
1670
|
});
|
|
1647
1671
|
let serverConfigPath = setup.aliases["#auth/server"];
|
|
1648
1672
|
let clientConfigPath = setup.aliases["#auth/client"];
|
|
1673
|
+
let schemaModelNames;
|
|
1649
1674
|
if (serverConfigPath && setup.pluginSources.server.length) {
|
|
1650
1675
|
const serverConfigCode = buildExtendedServerAuthCode(setup.configs.server.path, setup.pluginSources.server);
|
|
1651
1676
|
serverConfigPath = addTemplate({
|
|
@@ -1675,10 +1700,15 @@ const module$1 = defineNuxtModule({
|
|
|
1675
1700
|
nuxt.options.alias["#auth/database"] = databaseTemplate.dst;
|
|
1676
1701
|
const schemaTemplate = addTemplate({
|
|
1677
1702
|
filename: "better-auth/schema.mjs",
|
|
1678
|
-
getContents: () => buildSchemaExportCode(setup.database.hasHubDb, setup.database.buildContext?.hubDialect ?? "sqlite"),
|
|
1703
|
+
getContents: () => buildSchemaExportCode(setup.database.hasHubDb, setup.database.buildContext?.hubDialect ?? "sqlite", schemaModelNames),
|
|
1679
1704
|
write: true
|
|
1680
1705
|
});
|
|
1681
1706
|
nuxt.options.alias["#auth/schema"] = schemaTemplate.dst;
|
|
1707
|
+
addTemplate({
|
|
1708
|
+
filename: "better-auth/schema.d.ts",
|
|
1709
|
+
getContents: () => buildSchemaExportTypes(setup.database.hasHubDb, setup.database.buildContext?.hubDialect ?? "sqlite", schemaModelNames),
|
|
1710
|
+
write: true
|
|
1711
|
+
});
|
|
1682
1712
|
if (setup.schemaGeneration)
|
|
1683
1713
|
await ensureSchemaBootstrap(schemaTemplate.dst, setup.database.buildContext?.hubDialect ?? "sqlite");
|
|
1684
1714
|
}
|
|
@@ -1717,7 +1747,7 @@ const module$1 = defineNuxtModule({
|
|
|
1717
1747
|
if (setup.schemaGeneration) {
|
|
1718
1748
|
if (setup.schemaGeneration.externalizeNuxtHubDatabase)
|
|
1719
1749
|
registerNuxtHubDatabaseExternalHook(nuxt);
|
|
1720
|
-
await setupBetterAuthSchema(
|
|
1750
|
+
schemaModelNames = await setupBetterAuthSchema(
|
|
1721
1751
|
nuxt,
|
|
1722
1752
|
serverConfigPath,
|
|
1723
1753
|
options,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import createAppAuthClient from "#auth/client";
|
|
2
2
|
import { useRequestURL, useRuntimeConfig } from "#imports";
|
|
3
|
+
import { resolveAuthBaseURL } from "../../internal/auth-base-url.js";
|
|
3
4
|
import { createVueSafeAuthProxy } from "../internal/vue-safe-auth-proxy.js";
|
|
4
5
|
let _client = null;
|
|
5
6
|
let _clientFacade = null;
|
|
@@ -27,7 +28,9 @@ export function useRawAuthClient() {
|
|
|
27
28
|
const requestURL = useRequestURL();
|
|
28
29
|
const configuredSiteUrl = runtimeConfig.public.siteUrl;
|
|
29
30
|
const siteUrl = typeof configuredSiteUrl === "string" && configuredSiteUrl ? configuredSiteUrl : requestURL.origin;
|
|
30
|
-
|
|
31
|
+
const clientOnly = runtimeConfig.public.auth?.clientOnly;
|
|
32
|
+
const appBaseURL = runtimeConfig.app?.baseURL;
|
|
33
|
+
return getClient(clientOnly ? siteUrl : resolveAuthBaseURL(siteUrl, appBaseURL));
|
|
31
34
|
}
|
|
32
35
|
export function useAuthClient() {
|
|
33
36
|
const rawClient = useRawAuthClient();
|
|
@@ -11,14 +11,30 @@ export function useAuthRequestFetch() {
|
|
|
11
11
|
const clientOptions = createAppAuthClient.resolveOptions(siteUrl);
|
|
12
12
|
const configuredBaseURL = clientOptions.baseURL ?? siteUrl;
|
|
13
13
|
const baseURL = configuredBaseURL && new URL(configuredBaseURL).pathname.replace(/\/+$/, "") ? configuredBaseURL : joinURL(configuredBaseURL || "/", clientOptions.basePath ?? "/api/auth");
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
function wrapRequestFetch(fetch) {
|
|
15
|
+
const methods = /* @__PURE__ */ new Map();
|
|
16
|
+
return new Proxy(fetch, {
|
|
17
|
+
apply(target, thisArg, args) {
|
|
18
|
+
const [request, opts] = args;
|
|
19
|
+
if (typeof request !== "string" || !/^\/api\/auth(?=\/|$)/.test(request))
|
|
20
|
+
return Reflect.apply(target, thisArg, args);
|
|
21
|
+
return Reflect.apply(target, thisArg, [request.slice("/api/auth".length), {
|
|
22
|
+
...opts,
|
|
23
|
+
baseURL,
|
|
24
|
+
credentials: "include"
|
|
25
|
+
}]);
|
|
26
|
+
},
|
|
27
|
+
get(target, prop, receiver) {
|
|
28
|
+
if (methods.has(prop))
|
|
29
|
+
return methods.get(prop);
|
|
30
|
+
const value = Reflect.get(target, prop, receiver);
|
|
31
|
+
if (typeof value !== "function" || prop !== "raw" && prop !== "create")
|
|
32
|
+
return value;
|
|
33
|
+
const method = prop === "raw" ? wrapRequestFetch(value) : (...args) => wrapRequestFetch(Reflect.apply(value, target, args));
|
|
34
|
+
methods.set(prop, method);
|
|
35
|
+
return method;
|
|
36
|
+
}
|
|
22
37
|
});
|
|
23
|
-
}
|
|
38
|
+
}
|
|
39
|
+
return wrapRequestFetch(requestFetch);
|
|
24
40
|
}
|
|
@@ -74,6 +74,7 @@ export function useUserSession() {
|
|
|
74
74
|
return;
|
|
75
75
|
const previousUser = user.value;
|
|
76
76
|
user.value = { ...user.value, ...updates };
|
|
77
|
+
const optimisticUser = user.value;
|
|
77
78
|
if (!rawClient)
|
|
78
79
|
return;
|
|
79
80
|
try {
|
|
@@ -86,7 +87,8 @@ export function useUserSession() {
|
|
|
86
87
|
throw new Error(normalizedError.message);
|
|
87
88
|
}
|
|
88
89
|
} catch (error) {
|
|
89
|
-
user.value
|
|
90
|
+
if (user.value === optimisticUser)
|
|
91
|
+
user.value = previousUser;
|
|
90
92
|
if (!(error instanceof Error)) {
|
|
91
93
|
const normalizedError = normalizeAuthActionError(error);
|
|
92
94
|
throw new Error(normalizedError.message);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defu } from "defu";
|
|
2
2
|
import { createRouter, toRouteMatcher } from "radix3";
|
|
3
|
+
import { parsePath, parseQuery } from "ufo";
|
|
3
4
|
import { createError, defineNuxtRouteMiddleware, getRouteRules, navigateTo, useNuxtApp, useRequestHeaders, useRuntimeConfig } from "#imports";
|
|
4
5
|
import { shouldSkipAuthRouteRules } from "../../internal/auth-route-rules.js";
|
|
5
6
|
import { matchesUser } from "../../utils/match-user.js";
|
|
@@ -64,11 +65,11 @@ function resolveLoginRedirect(input) {
|
|
|
64
65
|
return { to: loginTarget, external: false };
|
|
65
66
|
if (!loginTarget.startsWith("/") || loginTarget.startsWith("//"))
|
|
66
67
|
return { to: loginTarget, external: false };
|
|
67
|
-
const
|
|
68
|
-
const
|
|
68
|
+
const { pathname: path, search, hash } = parsePath(loginTarget);
|
|
69
|
+
const query = search.slice(1);
|
|
69
70
|
try {
|
|
70
|
-
const
|
|
71
|
-
if (
|
|
71
|
+
const params = new URLSearchParams(query);
|
|
72
|
+
if (params.has(redirectQueryKey))
|
|
72
73
|
return { to: loginTarget, external: false };
|
|
73
74
|
} catch {
|
|
74
75
|
return { to: loginTarget, external: false };
|
|
@@ -76,15 +77,13 @@ function resolveLoginRedirect(input) {
|
|
|
76
77
|
if (import.meta.server) {
|
|
77
78
|
const separator = query ? "&" : "";
|
|
78
79
|
const encodedRedirect = encodeURIComponent(route.fullPath);
|
|
79
|
-
const
|
|
80
|
+
const encodedKey = encodeURIComponent(redirectQueryKey);
|
|
81
|
+
const url = `${path}?${query}${separator}${encodedKey}=${encodedRedirect}${hash}`;
|
|
80
82
|
return { to: url, external: true };
|
|
81
83
|
}
|
|
82
|
-
const
|
|
83
|
-
const queryObj = {};
|
|
84
|
-
for (const [k, v] of params.entries())
|
|
85
|
-
queryObj[k] = v;
|
|
84
|
+
const queryObj = parseQuery(query);
|
|
86
85
|
queryObj[redirectQueryKey] = route.fullPath;
|
|
87
|
-
return { to: { path, query: queryObj, ...hash ? { hash
|
|
86
|
+
return { to: { path, query: queryObj, ...hash ? { hash } : {} }, external: false };
|
|
88
87
|
}
|
|
89
88
|
async function getAuthRouteRules() {
|
|
90
89
|
if (!authRouteRulesPromise) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { Account, Session, User } from 'better-auth/types';
|
|
2
|
+
import type { AnyColumn, Table } from 'drizzle-orm';
|
|
1
3
|
import { z } from 'zod';
|
|
2
4
|
export declare const paginationQuerySchema: z.ZodObject<{
|
|
3
5
|
page: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
@@ -6,3 +8,8 @@ export declare const paginationQuerySchema: z.ZodObject<{
|
|
|
6
8
|
}, z.core.$strip>;
|
|
7
9
|
export type PaginationQuery = z.infer<typeof paginationQuerySchema>;
|
|
8
10
|
export declare function sanitizeSearchPattern(search: string): string;
|
|
11
|
+
export type DevtoolsSchema = {
|
|
12
|
+
user?: Table & Record<keyof User, AnyColumn>;
|
|
13
|
+
session?: Table & Record<keyof Session, AnyColumn>;
|
|
14
|
+
account?: Table & Record<keyof Account, AnyColumn>;
|
|
15
|
+
} | undefined;
|
|
@@ -21,7 +21,14 @@ export default defineEventHandler(async (event) => {
|
|
|
21
21
|
dbQuery.orderBy(desc(schema.account.createdAt)).limit(limit).offset(offset),
|
|
22
22
|
countQuery
|
|
23
23
|
]);
|
|
24
|
-
|
|
24
|
+
const safeAccounts = accounts.map((account) => ({
|
|
25
|
+
id: account.id,
|
|
26
|
+
userId: account.userId,
|
|
27
|
+
providerId: account.providerId,
|
|
28
|
+
accountId: account.accountId,
|
|
29
|
+
createdAt: account.createdAt
|
|
30
|
+
}));
|
|
31
|
+
return { accounts: safeAccounts, total: totalResult[0]?.count ?? 0, page, limit };
|
|
25
32
|
} catch (error) {
|
|
26
33
|
console.error("[DevTools] Fetch accounts failed:", error);
|
|
27
34
|
return { accounts: [], total: 0, error: "Failed to fetch accounts" };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { ServerEvent } from './nitro-compat.js';
|
|
2
2
|
export declare function appendCookieHeader(event: ServerEvent, header: string): void;
|
|
3
|
+
export declare function getSetCookieHeaders(headers: Headers): string[];
|
|
3
4
|
export declare function appendSetCookieHeaders(event: ServerEvent, headers: Headers): void;
|
|
@@ -14,7 +14,7 @@ export function appendCookieHeader(event, header) {
|
|
|
14
14
|
const responseHeaders = eventWithResponse.res?.headers ?? eventWithResponse.response?.headers;
|
|
15
15
|
responseHeaders?.append("set-cookie", header);
|
|
16
16
|
}
|
|
17
|
-
function getSetCookieHeaders(headers) {
|
|
17
|
+
export function getSetCookieHeaders(headers) {
|
|
18
18
|
const cookieHeaders = headers;
|
|
19
19
|
const getSetCookie = cookieHeaders.getSetCookie;
|
|
20
20
|
if (getSetCookie)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { betterAuth, env } from "better-auth";
|
|
2
|
-
import { withoutProtocol } from "ufo";
|
|
3
2
|
import { createDatabase, db } from "#auth/database";
|
|
4
3
|
import createServerAuth from "#auth/server";
|
|
5
4
|
import { getRequestHost, getRequestProtocol, useRuntimeConfig } from "../internal/nitro-compat.js";
|
|
5
|
+
import { resolveAuthBaseURL } from "../../internal/auth-base-url.js";
|
|
6
6
|
import { resolveCustomSecondaryStorageRequirement } from "./custom-secondary-storage.js";
|
|
7
7
|
const _authCache = /* @__PURE__ */ new Map();
|
|
8
8
|
const requestAuthKey = Symbol.for("nuxt-better-auth.requestAuth");
|
|
@@ -82,33 +82,25 @@ function getNitroOrigin() {
|
|
|
82
82
|
let port;
|
|
83
83
|
if (import.meta.dev)
|
|
84
84
|
port = process.env.NITRO_PORT || process.env.PORT || "3000";
|
|
85
|
-
|
|
85
|
+
const protocol = cert && key || !import.meta.dev ? "https" : "http";
|
|
86
86
|
try {
|
|
87
87
|
if ((import.meta.dev || import.meta.prerender) && process.env.__NUXT_DEV__) {
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
const url = new URL(JSON.parse(process.env.__NUXT_DEV__).proxy.url);
|
|
89
|
+
if (url.protocol === "http:" || url.protocol === "https:")
|
|
90
|
+
return url.origin;
|
|
91
91
|
} else if ((import.meta.dev || import.meta.prerender) && process.env.NUXT_VITE_NODE_OPTIONS) {
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
const url = new URL(JSON.parse(process.env.NUXT_VITE_NODE_OPTIONS).baseURL);
|
|
93
|
+
if (url.protocol === "http:" || url.protocol === "https:")
|
|
94
|
+
return url.origin;
|
|
95
95
|
}
|
|
96
96
|
} catch {
|
|
97
97
|
}
|
|
98
98
|
if (!host)
|
|
99
99
|
return void 0;
|
|
100
|
-
if (host.startsWith("[") && host.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (extractedPort)
|
|
105
|
-
port = extractedPort;
|
|
106
|
-
} else if (host.includes(":") && !host.startsWith("[")) {
|
|
107
|
-
const hostParts = host.split(":");
|
|
108
|
-
port = hostParts.pop();
|
|
109
|
-
host = hostParts.join(":");
|
|
110
|
-
}
|
|
111
|
-
const portSuffix = port ? `:${port}` : "";
|
|
100
|
+
if (!host.startsWith("[") && host.indexOf(":") !== host.lastIndexOf(":"))
|
|
101
|
+
host = `[${host}]`;
|
|
102
|
+
const hasExplicitPort = host.startsWith("[") ? host.includes("]:") : host.includes(":");
|
|
103
|
+
const portSuffix = !hasExplicitPort && port ? `:${port}` : "";
|
|
112
104
|
return `${protocol}://${host}${portSuffix}`;
|
|
113
105
|
}
|
|
114
106
|
function resolveEnvironmentOrigin() {
|
|
@@ -161,8 +153,7 @@ function getDevTrustedOrigins() {
|
|
|
161
153
|
try {
|
|
162
154
|
const url = new URL(nitroOrigin);
|
|
163
155
|
const protocol = url.protocol === "https:" ? "https" : "http";
|
|
164
|
-
const
|
|
165
|
-
const localhostOrigin = `${protocol}://localhost:${port}`;
|
|
156
|
+
const localhostOrigin = `${protocol}://localhost${url.port ? `:${url.port}` : ""}`;
|
|
166
157
|
return dedupeOrigins([localhostOrigin, url.origin]);
|
|
167
158
|
} catch {
|
|
168
159
|
return [fallbackOrigin];
|
|
@@ -254,7 +245,7 @@ export function serverAuth(event) {
|
|
|
254
245
|
...rateLimit ? { rateLimit } : {},
|
|
255
246
|
...database ? { database } : {},
|
|
256
247
|
secret: betterAuthSecret,
|
|
257
|
-
baseURL: siteUrl,
|
|
248
|
+
baseURL: resolveAuthBaseURL(siteUrl, runtimeConfig.app?.baseURL),
|
|
258
249
|
trustedOrigins
|
|
259
250
|
};
|
|
260
251
|
const auth = betterAuth(authOptions);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { matchesUser } from "../../utils/match-user.js";
|
|
2
2
|
import { createAuthError } from "../internal/nitro-compat.js";
|
|
3
|
-
import { appendCookieHeader, appendSetCookieHeaders } from "../internal/cookie-headers.js";
|
|
3
|
+
import { appendCookieHeader, appendSetCookieHeaders, getSetCookieHeaders } from "../internal/cookie-headers.js";
|
|
4
4
|
import { serverAuth } from "./auth.js";
|
|
5
5
|
const requestSessionLoadKey = Symbol.for("nuxt-better-auth.requestSessionLoad");
|
|
6
6
|
const requestSessionMutationKey = Symbol.for("nuxt-better-auth.requestSessionMutation");
|
|
@@ -162,6 +162,30 @@ function expireCookies(event, cookie) {
|
|
|
162
162
|
for (const cookieName of getChunkedCookieNames(event, cookie.name))
|
|
163
163
|
expireCookie(event, cookieName, cookie.attributes);
|
|
164
164
|
}
|
|
165
|
+
function isCookieOrChunk(name, cookieName) {
|
|
166
|
+
if (name === cookieName)
|
|
167
|
+
return true;
|
|
168
|
+
if (!name.startsWith(`${cookieName}.`))
|
|
169
|
+
return false;
|
|
170
|
+
const suffix = name.slice(cookieName.length + 1);
|
|
171
|
+
const index = Number(suffix);
|
|
172
|
+
return Number.isSafeInteger(index) && index >= 0 && String(index) === suffix;
|
|
173
|
+
}
|
|
174
|
+
function expireUnrefreshedSessionCookieCache(event, headers, cookie) {
|
|
175
|
+
const cookieName = getCookieName(cookie.name, cookie.attributes.prefix);
|
|
176
|
+
if (!cookieName)
|
|
177
|
+
return;
|
|
178
|
+
const responseHandlesCache = getSetCookieHeaders(headers).some((header) => {
|
|
179
|
+
const name = header.slice(0, header.indexOf("="));
|
|
180
|
+
return isCookieOrChunk(name, cookieName);
|
|
181
|
+
});
|
|
182
|
+
if (responseHandlesCache)
|
|
183
|
+
return;
|
|
184
|
+
for (const name of parseRequestCookies(getRequestHeaders(event).get("cookie")).keys()) {
|
|
185
|
+
if (isCookieOrChunk(name, cookieName))
|
|
186
|
+
expireCookie(event, name, cookie.attributes);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
165
189
|
function updateRequestHeaders(event, sessionCookie, clearedCookieNames) {
|
|
166
190
|
const requestContext = getRequestSessionContext(event);
|
|
167
191
|
const incomingHeaders = getIncomingRequestHeaders(event);
|
|
@@ -227,10 +251,14 @@ export async function refreshSessionCookieCache(event) {
|
|
|
227
251
|
if (context[requestSessionLoadKey] !== load)
|
|
228
252
|
return context.requestSession ?? null;
|
|
229
253
|
delete context.requestSession;
|
|
230
|
-
const { headers, response } = await
|
|
254
|
+
const [{ headers, response }, authContext] = await Promise.all([
|
|
255
|
+
loadFreshSession(event),
|
|
256
|
+
getServerAuthContext(event)
|
|
257
|
+
]);
|
|
231
258
|
if (context[requestSessionLoadKey] !== load)
|
|
232
259
|
return context.requestSession ?? null;
|
|
233
260
|
appendSetCookieHeaders(event, headers);
|
|
261
|
+
expireUnrefreshedSessionCookieCache(event, headers, authContext.authCookies.sessionData);
|
|
234
262
|
context.requestSession = response;
|
|
235
263
|
return response;
|
|
236
264
|
});
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
-
import { url } from "@nuxt/test-utils/e2e";
|
|
3
|
+
import { url, useTestContext } from "@nuxt/test-utils/e2e";
|
|
4
4
|
import { parseJSON } from "better-auth/client";
|
|
5
5
|
import { getRandomPort } from "get-port-please";
|
|
6
6
|
import { defu } from "defu";
|
|
7
|
+
import { joinURL } from "ufo";
|
|
7
8
|
export async function createAuthTestContext(options = {}) {
|
|
8
9
|
if (options.host || options.dev || options.build === false || options.server === false)
|
|
9
10
|
throw new Error("[nuxt-better-auth] Auth E2E helpers require a local test build. Use setup() with build and server enabled, without dev or host.");
|
|
@@ -22,7 +23,8 @@ export async function createAuthTestContext(options = {}) {
|
|
|
22
23
|
}
|
|
23
24
|
}, options);
|
|
24
25
|
async function call(action, data = {}) {
|
|
25
|
-
const
|
|
26
|
+
const baseURL = useTestContext().nuxt?.options.app.baseURL ?? "/";
|
|
27
|
+
const response = await fetch(url(joinURL(baseURL, endpoint)), {
|
|
26
28
|
method: "POST",
|
|
27
29
|
headers: { "content-type": "application/json", "x-nuxt-auth-test": token },
|
|
28
30
|
body: JSON.stringify({ action, data })
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuxtjs/better-auth",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.7",
|
|
5
5
|
"description": "Nuxt module for Better Auth integration with NuxtHub, route protection, session management, and role-based access",
|
|
6
6
|
"author": "onmax",
|
|
7
7
|
"license": "MIT",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"@nuxt/devtools-kit": "^3.4.1",
|
|
76
76
|
"@nuxt/kit": "^4.5.2",
|
|
77
77
|
"@types/node": "^24.13.3",
|
|
78
|
-
"auth": "1.7.
|
|
78
|
+
"auth": "1.7.7",
|
|
79
79
|
"consola": "^3.4.2",
|
|
80
80
|
"cookie-es": "^3.1.1",
|
|
81
81
|
"defu": "^6.1.7",
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"@pinia/nuxt": "^1.0.2",
|
|
103
103
|
"@types/better-sqlite3": "^9.6.0",
|
|
104
104
|
"@vue/test-utils": "^2.4.6",
|
|
105
|
-
"better-auth": "1.7.
|
|
105
|
+
"better-auth": "1.7.7",
|
|
106
106
|
"better-call": "1.4.0",
|
|
107
107
|
"better-sqlite3": "^12.11.1",
|
|
108
108
|
"drizzle-kit": "^0.31.10",
|