@nuxtjs/better-auth 0.3.9 → 0.3.11
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.d.mts +20 -17
- package/dist/module.json +1 -1
- package/dist/module.mjs +53 -11
- package/dist/runtime/app/composables/useUserSession.js +3 -3
- package/dist/runtime/app/internal/session-fetch.js +5 -4
- package/dist/runtime/app/internal/utils.d.ts +8 -0
- package/dist/runtime/app/internal/utils.js +16 -0
- package/dist/runtime/app/middleware/auth.global.js +5 -3
- package/dist/runtime/app/pages/__better-auth-devtools.vue +20 -10
- package/dist/runtime/config.d.ts +6 -3
- package/dist/runtime/internal/auth-route-rules.js +3 -1
- package/dist/runtime/server/internal/nitro2.d.ts +1 -2
- package/dist/runtime/server/internal/nitro2.js +3 -1
- package/dist/runtime/server/internal/portable.d.ts +1 -2
- package/dist/runtime/server/internal/portable.js +3 -1
- package/dist/runtime/server/utils/auth.d.ts +7 -2
- package/dist/runtime/server/utils/auth.js +1 -0
- package/dist/runtime/types/augment.d.ts +2 -0
- package/package.json +9 -9
package/dist/module.d.mts
CHANGED
|
@@ -30,24 +30,27 @@ interface BetterAuthDatabaseProviderDefinition {
|
|
|
30
30
|
isEnabled?: (ctx: BetterAuthDatabaseProviderEnabledContext) => boolean;
|
|
31
31
|
priority?: number;
|
|
32
32
|
}
|
|
33
|
+
interface BetterAuthNuxtHooks {
|
|
34
|
+
/** Register absolute plugin module paths during module setup. Sources are additive and are not deduplicated. */
|
|
35
|
+
'better-auth:plugins:extend': (sources: BetterAuthPluginSources) => void | Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Add plugin schemas to generated Better Auth database tables.
|
|
38
|
+
* This build-time hook does not install plugins in the runtime auth instance.
|
|
39
|
+
* @param config - Plugins to include during schema generation
|
|
40
|
+
* @param config.plugins - Better Auth plugins whose schemas should be generated
|
|
41
|
+
*/
|
|
42
|
+
'better-auth:config:extend': (config: {
|
|
43
|
+
plugins?: BetterAuthPlugin[];
|
|
44
|
+
}) => void | Promise<void>;
|
|
45
|
+
/** Register or override Better Auth database providers. */
|
|
46
|
+
'better-auth:database:providers': (providers: Record<string, BetterAuthDatabaseProviderDefinition>) => void | Promise<void>;
|
|
47
|
+
}
|
|
33
48
|
declare module '@nuxt/schema' {
|
|
34
|
-
interface NuxtHooks {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
* This build-time hook does not install plugins in the runtime auth instance.
|
|
40
|
-
* @param config - Plugins to include during schema generation
|
|
41
|
-
* @param config.plugins - Better Auth plugins whose schemas should be generated
|
|
42
|
-
*/
|
|
43
|
-
'better-auth:config:extend': (config: {
|
|
44
|
-
plugins?: BetterAuthPlugin[];
|
|
45
|
-
}) => void | Promise<void>;
|
|
46
|
-
/**
|
|
47
|
-
* Register or override Better Auth database providers.
|
|
48
|
-
* Providers are auto-selected via `isEnabled` + `priority`.
|
|
49
|
-
*/
|
|
50
|
-
'better-auth:database:providers': (providers: Record<string, BetterAuthDatabaseProviderDefinition>) => void | Promise<void>;
|
|
49
|
+
interface NuxtHooks extends BetterAuthNuxtHooks {
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
declare module 'nuxt/schema' {
|
|
53
|
+
interface NuxtHooks extends BetterAuthNuxtHooks {
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { getLayerDirectories, resolveServerVariant, hasNuxtCompatibility, updateTemplates, addServerImports, addServerHandler, addNitroPlugin, addImports, addPlugin, addComponentsDir, addRouteMiddleware, extendPages, addTemplate, hasNuxtModule, addTypeTemplate, defineNuxtModule, createResolver, getNitroVersion } from '@nuxt/kit';
|
|
3
|
+
import { getLayerDirectories, resolveServerVariant, hasNuxtCompatibility, hasNuxtModuleCompatibility, updateTemplates, addServerImports, addServerHandler, addNitroPlugin, addImports, addPlugin, addComponentsDir, addRouteMiddleware, extendPages, addTemplate, hasNuxtModule, addTypeTemplate, defineNuxtModule, createResolver, getNitroVersion } from '@nuxt/kit';
|
|
4
4
|
import { consola as consola$1 } from 'consola';
|
|
5
5
|
import { defineDiagnostics, Diagnostic, formatDiagnostic } from 'nostics';
|
|
6
6
|
import { isAbsolute, join, resolve, relative, dirname } from 'pathe';
|
|
@@ -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.11";
|
|
17
17
|
|
|
18
18
|
const CONFIG_EXTENSIONS = [".ts", ".js", ".mts", ".cts", ".mjs", ".cjs"];
|
|
19
19
|
const CONFIG_EXTENSION_RE = /\.[cm]?[jt]s$/;
|
|
@@ -152,6 +152,17 @@ function resolveAuthPluginSources(nuxt) {
|
|
|
152
152
|
return resolved;
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
function exportedNames(declaration) {
|
|
156
|
+
const names = /* @__PURE__ */ new Set();
|
|
157
|
+
for (const [, specifiers] of declaration.matchAll(/\bexport\s+(?:type\s+)?\{([^}]*)\}/g)) {
|
|
158
|
+
for (const specifier of specifiers.split(",")) {
|
|
159
|
+
const name = specifier.trim().replace(/^type\s+/, "").split(/\s+as\s+/).pop()?.trim();
|
|
160
|
+
if (name)
|
|
161
|
+
names.add(name);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return names;
|
|
165
|
+
}
|
|
155
166
|
function selectNitro3RouteRulesTarget(nitroTypesDeclaration, resolveH3Rules) {
|
|
156
167
|
if (/\binterface\s+NitroRouteConfig\b/.test(nitroTypesDeclaration)) {
|
|
157
168
|
return {
|
|
@@ -160,6 +171,14 @@ function selectNitro3RouteRulesTarget(nitroTypesDeclaration, resolveH3Rules) {
|
|
|
160
171
|
rulesInterface: "NitroRouteRules"
|
|
161
172
|
};
|
|
162
173
|
}
|
|
174
|
+
const exported = exportedNames(nitroTypesDeclaration);
|
|
175
|
+
if (exported.has("RouteRuleConfig") && exported.has("RouteRules")) {
|
|
176
|
+
return {
|
|
177
|
+
moduleName: "nitro/types",
|
|
178
|
+
configInterface: "RouteRuleConfig",
|
|
179
|
+
rulesInterface: "RouteRules"
|
|
180
|
+
};
|
|
181
|
+
}
|
|
163
182
|
return {
|
|
164
183
|
moduleName: resolveH3Rules(),
|
|
165
184
|
configInterface: "RouteRuleConfig",
|
|
@@ -208,6 +227,9 @@ function resolveServerRuntimeShim(resolve) {
|
|
|
208
227
|
function resolveH3TypesPath(nitroMajor) {
|
|
209
228
|
return nitroMajor === 3 ? "nitro/h3" : "h3";
|
|
210
229
|
}
|
|
230
|
+
function resolveServerImportsTypesFile(nitroMajor) {
|
|
231
|
+
return nitroMajor === 3 ? "server-imports.d.ts" : "nitro-imports.d.ts";
|
|
232
|
+
}
|
|
211
233
|
async function resolveAppSecretShim(resolve) {
|
|
212
234
|
const canDeriveAppSecret = await hasNuxtCompatibility({ nuxt: ">=4.6.0-0" });
|
|
213
235
|
return resolve(`./runtime/server/internal/${canDeriveAppSecret ? "app-secret" : "app-secret-unavailable"}`);
|
|
@@ -273,8 +295,22 @@ const diagnostics = defineDiagnostics({
|
|
|
273
295
|
}
|
|
274
296
|
});
|
|
275
297
|
|
|
276
|
-
function setupDevTools(nuxt) {
|
|
298
|
+
async function setupDevTools(nuxt) {
|
|
277
299
|
const hookable = nuxt;
|
|
300
|
+
if (await hasNuxtModuleCompatibility("@nuxt/devtools", ">=4.0.0-alpha.8", nuxt)) {
|
|
301
|
+
hookable.hook("devtools:ready", (ctx) => {
|
|
302
|
+
ctx.docks.register({
|
|
303
|
+
id: "better-auth",
|
|
304
|
+
title: "Auth",
|
|
305
|
+
icon: "simple-icons:betterauth",
|
|
306
|
+
type: "iframe",
|
|
307
|
+
url: "/__better-auth-devtools",
|
|
308
|
+
groupId: "nuxt",
|
|
309
|
+
category: "server"
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
278
314
|
hookable.hook("devtools:customTabs", (tabs) => {
|
|
279
315
|
tabs.push({
|
|
280
316
|
category: "server",
|
|
@@ -342,12 +378,12 @@ function registerAuthMiddleware(resolve) {
|
|
|
342
378
|
});
|
|
343
379
|
}
|
|
344
380
|
function registerPrepareTypesHook(input) {
|
|
345
|
-
const { nuxt, serverDir, hasHubDb } = input;
|
|
381
|
+
const { nuxt, serverDir, hasHubDb, serverImportsTypesFile } = input;
|
|
346
382
|
nuxt.hook("prepare:types", ({ nodeTsConfig, nodeReferences, sharedReferences }) => {
|
|
347
383
|
nodeTsConfig.compilerOptions ||= {};
|
|
348
384
|
nodeTsConfig.compilerOptions.paths ||= {};
|
|
349
385
|
const projectReferenceTypePaths = [
|
|
350
|
-
join(nuxt.options.buildDir, "types
|
|
386
|
+
join(nuxt.options.buildDir, "types", serverImportsTypesFile),
|
|
351
387
|
join(nuxt.options.buildDir, "types/auth-database.d.ts")
|
|
352
388
|
];
|
|
353
389
|
if (hasHubDb)
|
|
@@ -394,7 +430,7 @@ async function registerDevtools(input) {
|
|
|
394
430
|
const isProduction = process.env.NODE_ENV === "production" || !nuxt.options.dev;
|
|
395
431
|
if (isProduction || clientOnly)
|
|
396
432
|
return;
|
|
397
|
-
setupDevTools(nuxt);
|
|
433
|
+
await setupDevTools(nuxt);
|
|
398
434
|
addServerHandler({ route: "/api/_better-auth/config", method: "get", handler: resolve("./runtime/server/api/_better-auth/config.get") });
|
|
399
435
|
if (hasHubDb) {
|
|
400
436
|
const handlers = [
|
|
@@ -1209,7 +1245,7 @@ function buildNuxtFetchFallback(name) {
|
|
|
1209
1245
|
`).join("");
|
|
1210
1246
|
}
|
|
1211
1247
|
function registerServerTypeTemplates(input) {
|
|
1212
|
-
const { serverConfigPath, hasHubDb, runtimeTypesPath, sharedServerConfigSafe, h3TypesPath, nitro3RouteRulesTarget } = input;
|
|
1248
|
+
const { serverConfigPath, hasHubDb, runtimeTypesPath, sharedServerConfigSafe, h3TypesPath, serverImportsTypesFile, nitro3RouteRulesTarget } = input;
|
|
1213
1249
|
const fetchRoutingTypeDeclarations = h3TypesPath === "nitro/h3" ? `type NitroFetchRequest = string & {}
|
|
1214
1250
|
type AvailableRouterMethod<_ReqT extends NitroFetchRequest> = 'get' | 'head' | 'patch' | 'post' | 'put' | 'delete' | 'connect' | 'options' | 'trace'` : `import type { AvailableRouterMethod, NitroFetchRequest } from 'nitropack/types'`;
|
|
1215
1251
|
const resolvedNitro3RouteRulesTarget = nitro3RouteRulesTarget ?? {
|
|
@@ -1232,7 +1268,7 @@ type AvailableRouterMethod<_ReqT extends NitroFetchRequest> = 'get' | 'head' | '
|
|
|
1232
1268
|
auth?: import('${runtimeTypesPath}').AuthMeta
|
|
1233
1269
|
}
|
|
1234
1270
|
}`).join("\n");
|
|
1235
|
-
const serverConfigTypeTemplateOptions = sharedServerConfigSafe ? { nuxt: true, nitro: true, node: true, shared: true } : { nuxt: true, nitro: true, node: true };
|
|
1271
|
+
const serverConfigTypeTemplateOptions = sharedServerConfigSafe || h3TypesPath === "nitro/h3" ? { nuxt: true, nitro: true, node: true, shared: true } : { nuxt: true, nitro: true, node: true };
|
|
1236
1272
|
addTypeTemplate({
|
|
1237
1273
|
filename: "types/auth-database.d.ts",
|
|
1238
1274
|
getContents: () => `
|
|
@@ -1246,7 +1282,7 @@ declare module '#auth/database' {
|
|
|
1246
1282
|
addTypeTemplate({
|
|
1247
1283
|
filename: "types/nuxt-better-auth-server-context.d.ts",
|
|
1248
1284
|
getContents: () => `
|
|
1249
|
-
/// <reference path="
|
|
1285
|
+
/// <reference path="./${serverImportsTypesFile}" />
|
|
1250
1286
|
/// <reference path="./auth-database.d.ts" />
|
|
1251
1287
|
${hasHubDb ? '/// <reference path="../hub/db.d.ts" />' : ""}
|
|
1252
1288
|
|
|
@@ -1537,6 +1573,8 @@ declare module '#nuxt-better-auth' {
|
|
|
1537
1573
|
emailVerified: boolean
|
|
1538
1574
|
name: string
|
|
1539
1575
|
image?: string | null
|
|
1576
|
+
/** Whether the user has enabled two-factor authentication. */
|
|
1577
|
+
twoFactorEnabled?: boolean
|
|
1540
1578
|
}
|
|
1541
1579
|
|
|
1542
1580
|
export interface AuthSession {
|
|
@@ -1694,7 +1732,9 @@ const module$1 = defineNuxtModule({
|
|
|
1694
1732
|
setup(options, nuxt) {
|
|
1695
1733
|
const finishSetup = async () => {
|
|
1696
1734
|
const resolver = createResolver(import.meta.url);
|
|
1697
|
-
const
|
|
1735
|
+
const nitroMajor = getNitroVersion(nuxt);
|
|
1736
|
+
const h3TypesPath = resolveH3TypesPath(nitroMajor);
|
|
1737
|
+
const serverImportsTypesFile = resolveServerImportsTypesFile(nitroMajor);
|
|
1698
1738
|
nuxt.options.alias["#better-auth/nitro-compat"] = resolveServerRuntimeShim(resolver.resolve);
|
|
1699
1739
|
nuxt.options.alias["#better-auth/app-secret"] = await resolveAppSecretShim(resolver.resolve);
|
|
1700
1740
|
const registeredPluginSources = {};
|
|
@@ -1758,7 +1798,8 @@ const module$1 = defineNuxtModule({
|
|
|
1758
1798
|
registerPrepareTypesHook({
|
|
1759
1799
|
nuxt,
|
|
1760
1800
|
serverDir: setup.prepareTypes.serverDir,
|
|
1761
|
-
hasHubDb: setup.prepareTypes.hasHubDb
|
|
1801
|
+
hasHubDb: setup.prepareTypes.hasHubDb,
|
|
1802
|
+
serverImportsTypesFile
|
|
1762
1803
|
});
|
|
1763
1804
|
}
|
|
1764
1805
|
if (setup.database.providerDefinition) {
|
|
@@ -1783,6 +1824,7 @@ const module$1 = defineNuxtModule({
|
|
|
1783
1824
|
runtimeTypesPath: resolver.resolve("./runtime/types"),
|
|
1784
1825
|
sharedServerConfigSafe: [setup.configs.server.path, ...setup.pluginSources.server].every(isServerConfigSharedTypeSafe),
|
|
1785
1826
|
h3TypesPath,
|
|
1827
|
+
serverImportsTypesFile,
|
|
1786
1828
|
nitro3RouteRulesTarget: h3TypesPath === "nitro/h3" ? resolveNitro3RouteRulesTarget(nuxt.options.rootDir) : void 0
|
|
1787
1829
|
});
|
|
1788
1830
|
}
|
|
@@ -2,7 +2,7 @@ import { computed, navigateTo, nextTick, useNuxtApp, useRequestURL, useRuntimeCo
|
|
|
2
2
|
import { normalizeAuthActionError } from "../internal/auth-action-error.js";
|
|
3
3
|
import { resolvePostAuthSuccessRedirect, withFallbackSocialCallbackURL } from "../internal/redirect-helpers.js";
|
|
4
4
|
import { fetchSessionClient, fetchSessionServer, isExpectedSignedOutSessionError, stripToken } from "../internal/session-fetch.js";
|
|
5
|
-
import { isRecord } from "../internal/utils.js";
|
|
5
|
+
import { isRecord, setIfChanged } from "../internal/utils.js";
|
|
6
6
|
import { createVueSafeAuthFacade, isAuthProxyProbeKey } from "../internal/vue-safe-auth-proxy.js";
|
|
7
7
|
import { wrapAuthMethod } from "../internal/wrap-auth-method.js";
|
|
8
8
|
import { getAuthRuntimeFlags, useRawAuthClient } from "./useAuthClient.js";
|
|
@@ -152,8 +152,8 @@ export function useUserSession() {
|
|
|
152
152
|
if (newSession?.data?.session && newSession?.data?.user) {
|
|
153
153
|
sessionExpiredRedirecting = false;
|
|
154
154
|
hydrationSessionWasActive = false;
|
|
155
|
-
session
|
|
156
|
-
user
|
|
155
|
+
setIfChanged(session, stripToken(newSession.data.session));
|
|
156
|
+
setIfChanged(user, newSession.data.user);
|
|
157
157
|
} else if (!newSession?.isPending && !newSession?.isRefetching) {
|
|
158
158
|
const isHydrationEmptySnapshot = nuxtApp.isHydrating && nuxtApp.payload.serverRendered && Boolean(session.value && user.value) && !newSession?.data?.session && !newSession?.data?.user;
|
|
159
159
|
if (isHydrationEmptySnapshot) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { parseJSON } from "better-auth/client";
|
|
2
2
|
import { useRequestFetch, useRequestHeaders } from "#imports";
|
|
3
3
|
import { normalizeAuthActionError } from "./auth-action-error.js";
|
|
4
|
+
import { setIfChanged } from "./utils.js";
|
|
4
5
|
export function stripToken(session) {
|
|
5
6
|
const { token: _, ...safe } = session;
|
|
6
7
|
return safe;
|
|
@@ -35,8 +36,8 @@ export async function fetchSessionServer(session, user, authReady, options = {})
|
|
|
35
36
|
...options.force ? { query: { disableCookieCache: true } } : {}
|
|
36
37
|
});
|
|
37
38
|
if (data?.session && data?.user) {
|
|
38
|
-
session
|
|
39
|
-
user
|
|
39
|
+
setIfChanged(session, stripToken(data.session));
|
|
40
|
+
setIfChanged(user, data.user);
|
|
40
41
|
} else {
|
|
41
42
|
session.value = null;
|
|
42
43
|
user.value = null;
|
|
@@ -60,8 +61,8 @@ export async function fetchSessionClient(client, session, user, authReady, optio
|
|
|
60
61
|
}
|
|
61
62
|
const data = result.data;
|
|
62
63
|
if (data?.session && data?.user) {
|
|
63
|
-
session
|
|
64
|
-
user
|
|
64
|
+
setIfChanged(session, stripToken(data.session));
|
|
65
|
+
setIfChanged(user, data.user);
|
|
65
66
|
} else {
|
|
66
67
|
session.value = null;
|
|
67
68
|
user.value = null;
|
|
@@ -1 +1,9 @@
|
|
|
1
1
|
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
2
|
+
/**
|
|
3
|
+
* Better Auth refetches the session on window focus and returns fresh objects even
|
|
4
|
+
* when nothing changed. Keeping the current object avoids retriggering every
|
|
5
|
+
* computed and watcher that depends on the session or user.
|
|
6
|
+
*/
|
|
7
|
+
export declare function setIfChanged<T>(target: {
|
|
8
|
+
value: T;
|
|
9
|
+
}, value: T): void;
|
|
@@ -1,3 +1,19 @@
|
|
|
1
1
|
export function isRecord(value) {
|
|
2
2
|
return Boolean(value && typeof value === "object");
|
|
3
3
|
}
|
|
4
|
+
function isSameValue(a, b) {
|
|
5
|
+
if (Object.is(a, b))
|
|
6
|
+
return true;
|
|
7
|
+
if (a instanceof Date || b instanceof Date)
|
|
8
|
+
return a instanceof Date && b instanceof Date && a.getTime() === b.getTime();
|
|
9
|
+
if (Array.isArray(a) || Array.isArray(b))
|
|
10
|
+
return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((item, index) => isSameValue(item, b[index]));
|
|
11
|
+
if (!isRecord(a) || !isRecord(b))
|
|
12
|
+
return false;
|
|
13
|
+
const keys = Object.keys(a);
|
|
14
|
+
return keys.length === Object.keys(b).length && keys.every((key) => Object.hasOwn(b, key) && isSameValue(a[key], b[key]));
|
|
15
|
+
}
|
|
16
|
+
export function setIfChanged(target, value) {
|
|
17
|
+
if (!isSameValue(target.value, value))
|
|
18
|
+
target.value = value;
|
|
19
|
+
}
|
|
@@ -2,13 +2,15 @@ import { defu } from "defu";
|
|
|
2
2
|
import { createRouter, toRouteMatcher } from "radix3";
|
|
3
3
|
import { parsePath, parseQuery } from "ufo";
|
|
4
4
|
import { createError, defineNuxtRouteMiddleware, getRouteRules, navigateTo, useNuxtApp, useRequestHeaders, useRuntimeConfig } from "#imports";
|
|
5
|
-
import { shouldSkipAuthRouteRules } from "../../internal/auth-route-rules.js";
|
|
5
|
+
import { normalizeAuthRoutePath, shouldSkipAuthRouteRules } from "../../internal/auth-route-rules.js";
|
|
6
6
|
import { matchesUser } from "../../utils/match-user.js";
|
|
7
7
|
import { useUserSession } from "../composables/useUserSession.js";
|
|
8
8
|
let authRouteRulesPromise = null;
|
|
9
9
|
let routeRulesMatcherPromise = null;
|
|
10
10
|
export default defineNuxtRouteMiddleware(async (to) => {
|
|
11
|
-
|
|
11
|
+
const runtimeConfig = useRuntimeConfig();
|
|
12
|
+
const path = normalizeAuthRoutePath(to.path, runtimeConfig.app?.baseURL);
|
|
13
|
+
if (shouldSkipAuthRouteRules(to.path) || shouldSkipAuthRouteRules(path))
|
|
12
14
|
return;
|
|
13
15
|
if (to.matched?.length === 0)
|
|
14
16
|
return;
|
|
@@ -30,7 +32,7 @@ export default defineNuxtRouteMiddleware(async (to) => {
|
|
|
30
32
|
const auth = to.meta.auth;
|
|
31
33
|
if (auth === void 0 || auth === false)
|
|
32
34
|
return;
|
|
33
|
-
const config =
|
|
35
|
+
const config = runtimeConfig.public.auth;
|
|
34
36
|
if (import.meta.server && config?.clientOnly)
|
|
35
37
|
return;
|
|
36
38
|
const { fetchSession, user, loggedIn } = useUserSession();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { useFetch, useRuntimeConfig } from "#imports";
|
|
2
|
+
import { useFetch, useHead, useRuntimeConfig } from "#imports";
|
|
3
3
|
import { useDevtoolsClient } from "@nuxt/devtools-kit/iframe-client";
|
|
4
|
-
import { computed, ref,
|
|
4
|
+
import { computed, onBeforeUnmount, onMounted, ref, shallowRef, triggerRef, watch } from "vue";
|
|
5
5
|
const pageSize = 20;
|
|
6
6
|
const tabLabels = {
|
|
7
7
|
sessions: "Sessions",
|
|
@@ -10,13 +10,23 @@ const tabLabels = {
|
|
|
10
10
|
config: "Config"
|
|
11
11
|
};
|
|
12
12
|
const devtoolsClient = useDevtoolsClient();
|
|
13
|
+
const nativeHost = shallowRef();
|
|
14
|
+
let stopHostUpdates;
|
|
15
|
+
onMounted(() => {
|
|
16
|
+
if (window.parent === window)
|
|
17
|
+
return;
|
|
18
|
+
try {
|
|
19
|
+
nativeHost.value = window.parent.__NUXT_DEVTOOLS_HOST__;
|
|
20
|
+
} catch {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
stopHostUpdates = nativeHost.value?.hooks.hook("host:update:reactivity", () => triggerRef(nativeHost));
|
|
24
|
+
});
|
|
25
|
+
onBeforeUnmount(() => stopHostUpdates?.());
|
|
13
26
|
const runtimeConfig = useRuntimeConfig();
|
|
14
27
|
const hasDb = computed(() => runtimeConfig.public.auth?.useDatabase ?? false);
|
|
15
|
-
const isDark = computed(() => devtoolsClient.value?.host?.app?.colorMode?.value === "dark");
|
|
16
|
-
|
|
17
|
-
if (import.meta.client)
|
|
18
|
-
document.documentElement.classList.toggle("dark", isDark.value);
|
|
19
|
-
});
|
|
28
|
+
const isDark = computed(() => (nativeHost.value ?? devtoolsClient.value?.host)?.app?.colorMode?.value === "dark");
|
|
29
|
+
useHead({ style: [{ key: "better-auth-devtools-body", textContent: "body { margin: 0 !important; }" }] });
|
|
20
30
|
const activeTab = ref(hasDb.value ? "sessions" : "config");
|
|
21
31
|
const tabs = computed(() => hasDb.value ? ["sessions", "users", "accounts", "config"] : ["config"]);
|
|
22
32
|
watch(hasDb, (enabled) => {
|
|
@@ -125,7 +135,7 @@ async function deleteConfirmedSession() {
|
|
|
125
135
|
</script>
|
|
126
136
|
|
|
127
137
|
<template>
|
|
128
|
-
<div class="devtools-shell">
|
|
138
|
+
<div class="devtools-shell" :class="{ dark: isDark }">
|
|
129
139
|
<header class="devtools-header">
|
|
130
140
|
<div class="brand">
|
|
131
141
|
<svg width="60" height="45" viewBox="0 0 60 45" fill="none" class="brand-mark" xmlns="http://www.w3.org/2000/svg">
|
|
@@ -375,6 +385,6 @@ async function deleteConfirmedSession() {
|
|
|
375
385
|
</div>
|
|
376
386
|
</template>
|
|
377
387
|
|
|
378
|
-
<style>
|
|
379
|
-
|
|
388
|
+
<style scoped>
|
|
389
|
+
.devtools-shell{--ba-bg:#fff;--ba-fg:#1f1f1f;--ba-muted:#707070;--ba-border:#e5e5e5;--ba-soft:#f7f7f7;--ba-hover:#efefef;--ba-good:#047857;--ba-bad:#b91c1c}.devtools-shell.dark{--ba-bg:#111;--ba-fg:#f4f4f5;--ba-muted:#a1a1aa;--ba-border:#2d2d2d;--ba-soft:#191919;--ba-hover:#242424;--ba-good:#34d399;--ba-bad:#f87171}*{box-sizing:border-box}button,input{font:inherit}.devtools-shell{background:var(--ba-bg);color:var(--ba-fg);font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-size:13px;min-height:100vh}.devtools-header{border-bottom:1px solid var(--ba-border);gap:16px;justify-content:space-between;min-height:48px;padding:0 16px}.actions,.brand,.confirm,.devtools-header,.links,.pagination,.toolbar,.toolbar-meta{align-items:center;display:flex}.brand{font-weight:600;gap:10px}.brand-mark{height:auto;width:24px}.links a{color:var(--ba-muted);padding:8px 10px;text-decoration:none}.links a:hover{color:var(--ba-fg)}.tabs{border-bottom:1px solid var(--ba-border);display:flex;gap:4px;padding:8px 12px 0}.actions button,.confirm button,.pagination button,.tabs button,.toolbar button{background:transparent;border:1px solid var(--ba-border);color:var(--ba-fg);cursor:pointer;min-height:30px;padding:5px 10px}.tabs button{border-bottom:0}.tabs button.active,button:hover:not(:disabled){background:var(--ba-hover)}button:disabled{color:var(--ba-muted);cursor:default}button.danger{color:var(--ba-bad)}.panel{padding:16px}.section{display:grid;gap:14px}.toolbar{gap:12px;justify-content:space-between}.actions,.confirm,.pagination,.toolbar-meta{gap:8px}input{background:var(--ba-bg);border:1px solid var(--ba-border);color:var(--ba-fg);padding:7px 10px;width:min(340px,100%)}table{border:1px solid var(--ba-border);border-collapse:collapse;width:100%}td,th{border-bottom:1px solid var(--ba-border);padding:9px 10px;text-align:left;vertical-align:top}th{background:var(--ba-soft);font-weight:500}td small,th{color:var(--ba-muted)}td small{display:block;margin-top:3px}code,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace}.badge{background:var(--ba-soft);border:1px solid var(--ba-border);color:var(--ba-muted);display:inline-flex;padding:2px 8px}.badge.good{color:var(--ba-good)}.badge.bad,.error{color:var(--ba-bad)}.confirm,.empty,.notice{background:var(--ba-soft);border:1px solid var(--ba-border);padding:10px 12px}.empty,.notice{color:var(--ba-muted)}.config-grid{grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}.config-grid .error,.config-grid .toolbar{grid-column:1/-1}article{background:var(--ba-soft);border:1px solid var(--ba-border)}h2{border-bottom:1px solid var(--ba-border);font-size:13px;padding:10px 12px}h2,pre{margin:0}pre{font-size:12px;line-height:1.5;overflow:auto;padding:12px}.capitalize{text-transform:capitalize}@media (max-width:720px){.devtools-header,.toolbar,.toolbar-meta{align-items:stretch;flex-direction:column}.actions,.tabs{flex-wrap:wrap}table{display:block;overflow-x:auto}}
|
|
380
390
|
</style>
|
package/dist/runtime/config.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface ClientAuthContext {
|
|
|
12
12
|
export type ServerAuthConfig = Omit<BetterAuthOptions, 'secret' | 'baseURL' | 'basePath'> & {
|
|
13
13
|
/** The module registers its server handler at /api/auth. */
|
|
14
14
|
basePath?: '/api/auth';
|
|
15
|
-
plugins?: readonly
|
|
15
|
+
plugins?: readonly unknown[];
|
|
16
16
|
};
|
|
17
17
|
export type ClientAuthConfig = Omit<BetterAuthClientOptions, 'baseURL'> & {
|
|
18
18
|
baseURL?: string;
|
|
@@ -108,8 +108,11 @@ export interface AuthRuntimeConfig {
|
|
|
108
108
|
export interface AuthPrivateRuntimeConfig {
|
|
109
109
|
hubSecondaryStorage: boolean | 'custom';
|
|
110
110
|
}
|
|
111
|
-
export declare function defineServerAuth<const R>(config: (ctx: ServerAuthContext) => R
|
|
112
|
-
export declare function defineServerAuth<const R>(config:
|
|
111
|
+
export declare function defineServerAuth<const R extends ServerAuthConfig>(config: (ctx: ServerAuthContext) => R): (ctx: ServerAuthContext) => R;
|
|
112
|
+
export declare function defineServerAuth<const R>(config: (ctx: ServerAuthContext) => R & ServerAuthConfig & (R extends {
|
|
113
|
+
basePath: infer B;
|
|
114
|
+
} ? B extends '/api/auth' ? unknown : never : unknown)): (ctx: ServerAuthContext) => R;
|
|
115
|
+
export declare function defineServerAuth<const R extends ServerAuthConfig>(config: R & (R extends (...args: never[]) => unknown ? never : unknown)): (ctx: ServerAuthContext) => R;
|
|
113
116
|
export declare function extendServerAuth<const R extends ServerAuthConfig, const P extends readonly BetterAuthPlugin[]>(createConfig: (ctx: ServerAuthContext) => R, plugins: P): (ctx: ServerAuthContext) => ExtendedServerAuthConfig<R, P>;
|
|
114
117
|
export declare function defineClientAuth<T extends ClientAuthConfig>(config: T | ((ctx: ClientAuthContext) => T)): ClientAuthFactory<T>;
|
|
115
118
|
export declare function extendClientAuth<T extends ClientAuthConfig, const P extends BetterAuthClientPlugin[]>(createClient: ClientAuthFactory<T>, plugins: P): ClientAuthFactory<ExtendedClientAuthConfig<T, P>>;
|
|
@@ -2,7 +2,9 @@ import { withoutBase } from "ufo";
|
|
|
2
2
|
const internalAssetPaths = ["/_fonts", "/_ipx", "/_nuxt", "/api/_nuxt_icon"];
|
|
3
3
|
export function isInternalAssetPath(path) {
|
|
4
4
|
const pathname = path.split(/[?#]/, 1)[0] || "/";
|
|
5
|
-
return internalAssetPaths.some((prefix) =>
|
|
5
|
+
return internalAssetPaths.some((prefix) => {
|
|
6
|
+
return pathname === prefix || pathname.startsWith(`${prefix}/`) || pathname.endsWith(prefix) || pathname.includes(`${prefix}/`);
|
|
7
|
+
});
|
|
6
8
|
}
|
|
7
9
|
const internalRouteRuleAuthPaths = /* @__PURE__ */ new Set([
|
|
8
10
|
"/__better-auth-devtools",
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type { H3Event } from 'h3';
|
|
2
2
|
import type { AuthRouteRules } from '../../types.js';
|
|
3
|
-
import { splitSetCookieString } from 'cookie-es';
|
|
4
3
|
import { defineEventHandler, getQuery, getRequestHost, getRequestProtocol, getRequestURL, readBody, toWebRequest } from 'h3';
|
|
5
4
|
import { useRuntimeConfig } from 'nitropack/runtime';
|
|
6
5
|
export { defineEventHandler, getQuery, getRequestHost, getRequestProtocol, getRequestURL, readBody, toWebRequest, useRuntimeConfig, };
|
|
7
6
|
export type ServerEvent = H3Event;
|
|
8
|
-
export
|
|
7
|
+
export declare function splitCookiesString(cookiesString: string | string[]): string[];
|
|
9
8
|
export declare function getAuthRouteRules(event: ServerEvent): AuthRouteRules;
|
|
10
9
|
export declare function createAuthError(status: number, statusText: string): Error;
|
|
@@ -20,7 +20,9 @@ export {
|
|
|
20
20
|
toWebRequest,
|
|
21
21
|
useRuntimeConfig
|
|
22
22
|
};
|
|
23
|
-
export
|
|
23
|
+
export function splitCookiesString(cookiesString) {
|
|
24
|
+
return splitSetCookieString(cookiesString);
|
|
25
|
+
}
|
|
24
26
|
export function getAuthRouteRules(event) {
|
|
25
27
|
return getRouteRules(event);
|
|
26
28
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type { RequestEvent } from 'nuxt/server';
|
|
2
2
|
import type { AuthRouteRules } from '../../types.js';
|
|
3
|
-
import { splitSetCookieString } from 'cookie-es';
|
|
4
3
|
import { defineEventHandler, getQuery, getRequestHost, getRequestProtocol, getRequestURL, readBody, useRuntimeConfig } from 'nuxt/server';
|
|
5
4
|
export { defineEventHandler, getQuery, getRequestHost, getRequestProtocol, getRequestURL, readBody, useRuntimeConfig, };
|
|
6
5
|
export type ServerEvent = RequestEvent;
|
|
7
|
-
export
|
|
6
|
+
export declare function splitCookiesString(cookiesString: string | string[]): string[];
|
|
8
7
|
export declare function getAuthRouteRules(event: ServerEvent): AuthRouteRules;
|
|
9
8
|
export declare function createAuthError(status: number, statusText: string): Error;
|
|
10
9
|
export declare function toWebRequest(event: ServerEvent): Request;
|
|
@@ -20,7 +20,9 @@ export {
|
|
|
20
20
|
readBody,
|
|
21
21
|
useRuntimeConfig
|
|
22
22
|
};
|
|
23
|
-
export
|
|
23
|
+
export function splitCookiesString(cookiesString) {
|
|
24
|
+
return splitSetCookieString(cookiesString);
|
|
25
|
+
}
|
|
24
26
|
export function getAuthRouteRules(event) {
|
|
25
27
|
const rules = getRouteRules(event);
|
|
26
28
|
return { auth: normalizeAuthRouteRule(rules.auth) };
|
|
@@ -9,13 +9,18 @@ type UserAuthConfig = AuthOptions & {
|
|
|
9
9
|
trustedOrigins?: BetterAuthOptions['trustedOrigins'];
|
|
10
10
|
secondaryStorage?: BetterAuthOptions['secondaryStorage'];
|
|
11
11
|
};
|
|
12
|
-
type ResolvedAuthOptions = Omit<UserAuthConfig, 'secret' | 'baseURL' | 'trustedOrigins' | 'database'> & {
|
|
12
|
+
type ResolvedAuthOptions = Omit<UserAuthConfig, 'secret' | 'baseURL' | 'trustedOrigins' | 'database' | 'plugins'> & {
|
|
13
|
+
plugins?: BetterAuthOptions['plugins'];
|
|
13
14
|
secret: string;
|
|
14
15
|
baseURL: string;
|
|
15
16
|
trustedOrigins?: BetterAuthOptions['trustedOrigins'];
|
|
16
17
|
database?: BetterAuthOptions['database'];
|
|
17
18
|
};
|
|
18
|
-
type AuthInstance = ReturnType<typeof betterAuth<ResolvedAuthOptions
|
|
19
|
+
type AuthInstance = ReturnType<typeof betterAuth<ResolvedAuthOptions>> & {
|
|
20
|
+
api: ReturnType<typeof betterAuth<ResolvedAuthOptions>>['api'] & {
|
|
21
|
+
signInUsername?: (...args: unknown[]) => unknown;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
19
24
|
/**
|
|
20
25
|
* Returns the Better Auth instance like `serverAuth()`, after waiting for the auth secret that
|
|
21
26
|
* Nuxt 4.6+ derives from `appSecret` (`NUXT_APP_SECRET`). Use it in code that can run before the
|
|
@@ -274,6 +274,7 @@ export function serverAuth(event) {
|
|
|
274
274
|
}
|
|
275
275
|
const authOptions = {
|
|
276
276
|
...userConfig,
|
|
277
|
+
...userConfig.plugins ? { plugins: [...userConfig.plugins] } : {},
|
|
277
278
|
...rateLimit ? { rateLimit } : {},
|
|
278
279
|
...database ? { database } : {},
|
|
279
280
|
secret: betterAuthSecret,
|
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.11",
|
|
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",
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"@libsql/client": "^0.18.0",
|
|
95
95
|
"@libsql/linux-x64-gnu": "0.5.29",
|
|
96
96
|
"@nuxt/devtools": "^3.4.1",
|
|
97
|
-
"@nuxt/fonts": "0.
|
|
97
|
+
"@nuxt/fonts": "1.0.1",
|
|
98
98
|
"@nuxt/module-builder": "^1.0.3",
|
|
99
99
|
"@nuxt/schema": "^4.6.0",
|
|
100
100
|
"@nuxt/test-utils": "^4.3.3",
|
|
@@ -102,25 +102,25 @@
|
|
|
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.
|
|
106
|
-
"better-call": "1.4.
|
|
105
|
+
"better-auth": "1.7.10",
|
|
106
|
+
"better-call": "1.4.2",
|
|
107
107
|
"better-sqlite3": "^13.0.0",
|
|
108
108
|
"drizzle-kit": "^0.31.10",
|
|
109
109
|
"drizzle-orm": "^0.45.2",
|
|
110
110
|
"eslint": "^10.8.1",
|
|
111
|
-
"happy-dom": "^
|
|
111
|
+
"happy-dom": "^21.0.0",
|
|
112
112
|
"nitropack": "2.13.4",
|
|
113
113
|
"nuxt": "^4.6.0",
|
|
114
114
|
"pinia": "^4.0.3",
|
|
115
|
-
"pkg-pr-new": "0.0.
|
|
116
|
-
"playwright-core": "1.
|
|
115
|
+
"pkg-pr-new": "0.0.91",
|
|
116
|
+
"playwright-core": "1.64.1",
|
|
117
117
|
"postgres": "^3.4.9",
|
|
118
118
|
"typescript": "~5.9.3",
|
|
119
119
|
"unstorage": "^1.17.5",
|
|
120
120
|
"vitest": "^5.0.0",
|
|
121
|
-
"vue": "3.5.
|
|
121
|
+
"vue": "3.5.44",
|
|
122
122
|
"vue-tsc": "^3.3.10",
|
|
123
|
-
"wrangler": "4.
|
|
123
|
+
"wrangler": "4.151.1",
|
|
124
124
|
"yaml": "^2.9.0"
|
|
125
125
|
},
|
|
126
126
|
"scripts": {
|