@onmax/nuxt-better-auth 0.0.2-alpha.3 → 0.0.2-alpha.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +1 -1
  2. package/dist/module.d.mts +28 -1
  3. package/dist/module.json +2 -2
  4. package/dist/module.mjs +965 -313
  5. package/dist/runtime/app/components/BetterAuthState.vue +1 -0
  6. package/dist/runtime/app/composables/useAction.d.ts +2 -0
  7. package/dist/runtime/app/composables/useAction.js +6 -0
  8. package/dist/runtime/app/composables/useAuthAsyncData.d.ts +6 -0
  9. package/dist/runtime/app/composables/useAuthAsyncData.js +16 -0
  10. package/dist/runtime/app/composables/useAuthClientAction.d.ts +5 -0
  11. package/dist/runtime/app/composables/useAuthClientAction.js +15 -0
  12. package/dist/runtime/app/composables/useAuthRequestFetch.d.ts +11 -0
  13. package/dist/runtime/app/composables/useAuthRequestFetch.js +4 -0
  14. package/dist/runtime/app/composables/useSignIn.d.ts +16 -0
  15. package/dist/runtime/app/composables/useSignIn.js +8 -0
  16. package/dist/runtime/app/composables/useSignUp.d.ts +5 -0
  17. package/dist/runtime/app/composables/useSignUp.js +8 -0
  18. package/dist/runtime/app/composables/useUserSession.d.ts +14 -12
  19. package/dist/runtime/app/composables/useUserSession.js +162 -67
  20. package/dist/runtime/app/internal/auth-action-error.d.ts +3 -0
  21. package/dist/runtime/app/internal/auth-action-error.js +33 -0
  22. package/dist/runtime/app/internal/auth-action-handles.d.ts +18 -0
  23. package/dist/runtime/app/internal/auth-action-handles.js +105 -0
  24. package/dist/runtime/app/internal/redirect-helpers.d.ts +4 -0
  25. package/dist/runtime/app/internal/redirect-helpers.js +37 -0
  26. package/dist/runtime/app/internal/session-fetch.d.ts +12 -0
  27. package/dist/runtime/app/internal/session-fetch.js +56 -0
  28. package/dist/runtime/app/internal/utils.d.ts +1 -0
  29. package/dist/runtime/app/internal/utils.js +3 -0
  30. package/dist/runtime/app/internal/wrap-auth-method.d.ts +15 -0
  31. package/dist/runtime/app/internal/wrap-auth-method.js +66 -0
  32. package/dist/runtime/app/middleware/auth.global.js +73 -12
  33. package/dist/runtime/app/pages/__better-auth-devtools.vue +4 -10
  34. package/dist/runtime/app/plugins/session.client.js +1 -2
  35. package/dist/runtime/config.d.ts +66 -11
  36. package/dist/runtime/config.js +9 -2
  37. package/dist/runtime/server/api/_better-auth/_schema.js +2 -1
  38. package/dist/runtime/server/api/_better-auth/accounts.get.js +3 -2
  39. package/dist/runtime/server/api/_better-auth/config.get.d.ts +17 -11
  40. package/dist/runtime/server/api/_better-auth/config.get.js +17 -5
  41. package/dist/runtime/server/api/_better-auth/sessions.delete.js +3 -2
  42. package/dist/runtime/server/api/_better-auth/sessions.get.d.ts +3 -1
  43. package/dist/runtime/server/api/_better-auth/sessions.get.js +3 -2
  44. package/dist/runtime/server/api/_better-auth/users.get.js +3 -2
  45. package/dist/runtime/server/api/auth/[...all].js +1 -1
  46. package/dist/runtime/server/middleware/route-access.js +1 -0
  47. package/dist/runtime/server/tsconfig.json +9 -1
  48. package/dist/runtime/server/utils/auth.d.ts +5 -7
  49. package/dist/runtime/server/utils/auth.js +197 -17
  50. package/dist/runtime/server/utils/custom-secondary-storage.d.ts +6 -0
  51. package/dist/runtime/server/utils/custom-secondary-storage.js +8 -0
  52. package/dist/runtime/server/utils/session.d.ts +4 -8
  53. package/dist/runtime/server/utils/session.js +43 -4
  54. package/dist/runtime/server/virtual-modules.d.ts +22 -0
  55. package/dist/runtime/types/augment.d.ts +18 -2
  56. package/dist/runtime/types.d.ts +12 -13
  57. package/dist/types.d.mts +1 -1
  58. package/package.json +32 -42
@@ -0,0 +1,12 @@
1
+ import type { AppAuthClient, AuthSession, AuthUser } from '#nuxt-better-auth';
2
+ import type { Ref } from 'vue';
3
+ export declare function stripToken(session: AuthSession & {
4
+ token?: string;
5
+ }): AuthSession;
6
+ export declare function fetchSessionServer(session: Ref<AuthSession | null>, user: Ref<AuthUser | null>, authReady: Ref<boolean>, options?: {
7
+ headers?: HeadersInit;
8
+ }): Promise<void>;
9
+ export declare function fetchSessionClient(client: AppAuthClient, session: Ref<AuthSession | null>, user: Ref<AuthUser | null>, authReady: Ref<boolean>, options?: {
10
+ headers?: HeadersInit;
11
+ force?: boolean;
12
+ }): Promise<void>;
@@ -0,0 +1,56 @@
1
+ import { useRequestFetch, useRequestHeaders } from "#imports";
2
+ import { normalizeAuthActionError } from "./auth-action-error.js";
3
+ export function stripToken(session) {
4
+ const { token: _, ...safe } = session;
5
+ return safe;
6
+ }
7
+ function isExpectedSignedOutSessionError(error) {
8
+ const normalizedError = normalizeAuthActionError(error);
9
+ if (normalizedError.status === 401)
10
+ return true;
11
+ return normalizedError.code === "UNAUTHORIZED";
12
+ }
13
+ export async function fetchSessionServer(session, user, authReady, options = {}) {
14
+ try {
15
+ const headers = options.headers || useRequestHeaders(["cookie"]);
16
+ const requestFetch = useRequestFetch();
17
+ const data = await requestFetch("/api/auth/get-session", { headers });
18
+ if (data?.session && data?.user) {
19
+ session.value = stripToken(data.session);
20
+ user.value = data.user;
21
+ } else {
22
+ session.value = null;
23
+ user.value = null;
24
+ }
25
+ } catch {
26
+ session.value = null;
27
+ user.value = null;
28
+ } finally {
29
+ if (!authReady.value)
30
+ authReady.value = true;
31
+ }
32
+ }
33
+ export async function fetchSessionClient(client, session, user, authReady, options = {}) {
34
+ try {
35
+ const headers = options.headers || useRequestHeaders(["cookie"]);
36
+ const fetchOptions = headers ? { headers } : void 0;
37
+ const query = options.force ? { disableCookieCache: true } : void 0;
38
+ const result = await client.getSession({ query }, fetchOptions);
39
+ const data = result.data;
40
+ if (data?.session && data?.user) {
41
+ session.value = stripToken(data.session);
42
+ user.value = data.user;
43
+ } else {
44
+ session.value = null;
45
+ user.value = null;
46
+ }
47
+ } catch (error) {
48
+ session.value = null;
49
+ user.value = null;
50
+ if (!isExpectedSignedOutSessionError(error))
51
+ console.error("[nuxt-better-auth] Failed to fetch session:", error);
52
+ } finally {
53
+ if (!authReady.value)
54
+ authReady.value = true;
55
+ }
56
+ }
@@ -0,0 +1 @@
1
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
@@ -0,0 +1,3 @@
1
+ export function isRecord(value) {
2
+ return Boolean(value && typeof value === "object");
3
+ }
@@ -0,0 +1,15 @@
1
+ import type { ComputedRef } from 'vue';
2
+ export declare function wrapOnSuccess(fetchSession: (options?: {
3
+ force?: boolean;
4
+ }) => Promise<void>, loggedIn: ComputedRef<boolean>, waitForSession: () => Promise<void>, cb: (ctx: unknown) => void | Promise<void>): (ctx: unknown) => Promise<void>;
5
+ export declare function wrapAuthMethod<T extends (...args: unknown[]) => Promise<unknown>>(method: T, deps: {
6
+ fetchSession: (options?: {
7
+ force?: boolean;
8
+ }) => Promise<void>;
9
+ loggedIn: ComputedRef<boolean>;
10
+ waitForSession: () => Promise<void>;
11
+ resolvePostAuthSuccessRedirect: () => (() => Promise<void>) | undefined;
12
+ }, wrapOptions?: {
13
+ shouldSkipSessionSync?: (data: unknown, options: unknown) => boolean;
14
+ transformData?: (data: unknown, options: unknown) => unknown;
15
+ }): T;
@@ -0,0 +1,66 @@
1
+ import { nextTick } from "#imports";
2
+ import { isRecord } from "./utils.js";
3
+ export function wrapOnSuccess(fetchSession, loggedIn, waitForSession, cb) {
4
+ return async (ctx) => {
5
+ await fetchSession({ force: true });
6
+ if (!loggedIn.value)
7
+ await waitForSession();
8
+ await nextTick();
9
+ await cb(ctx);
10
+ };
11
+ }
12
+ export function wrapAuthMethod(method, deps, wrapOptions = {}) {
13
+ return (async (...args) => {
14
+ const originalData = args[0];
15
+ const options = args[1];
16
+ const data = wrapOptions.transformData?.(originalData, options) ?? originalData;
17
+ const dataRecord = isRecord(data) ? data : void 0;
18
+ const optionsRecord = isRecord(options) ? options : void 0;
19
+ if (wrapOptions.shouldSkipSessionSync?.(data, options))
20
+ return method(data, options);
21
+ const fetchOptions = isRecord(dataRecord?.fetchOptions) ? dataRecord.fetchOptions : void 0;
22
+ const nestedOnSuccess = fetchOptions?.onSuccess;
23
+ const topLevelOnSuccess = optionsRecord?.onSuccess;
24
+ const fallbackOnSuccess = deps.resolvePostAuthSuccessRedirect();
25
+ const wrappedFallbackOnSuccess = fallbackOnSuccess && wrapOnSuccess(deps.fetchSession, deps.loggedIn, deps.waitForSession, async () => {
26
+ if (!deps.loggedIn.value)
27
+ return;
28
+ await fallbackOnSuccess();
29
+ });
30
+ if (typeof nestedOnSuccess === "function") {
31
+ const nextData = {
32
+ ...dataRecord,
33
+ fetchOptions: {
34
+ ...fetchOptions,
35
+ onSuccess: wrapOnSuccess(deps.fetchSession, deps.loggedIn, deps.waitForSession, nestedOnSuccess)
36
+ }
37
+ };
38
+ return method(nextData, options);
39
+ }
40
+ if (typeof topLevelOnSuccess === "function") {
41
+ const nextOptions = {
42
+ ...optionsRecord,
43
+ onSuccess: wrapOnSuccess(deps.fetchSession, deps.loggedIn, deps.waitForSession, topLevelOnSuccess)
44
+ };
45
+ return method(data, nextOptions);
46
+ }
47
+ if (wrappedFallbackOnSuccess) {
48
+ if (fetchOptions) {
49
+ const nextData = {
50
+ ...dataRecord,
51
+ fetchOptions: {
52
+ ...fetchOptions,
53
+ onSuccess: wrappedFallbackOnSuccess
54
+ }
55
+ };
56
+ return method(nextData, options);
57
+ }
58
+ const nextOptions = {
59
+ ...optionsRecord,
60
+ onSuccess: wrappedFallbackOnSuccess
61
+ };
62
+ return method(data, nextOptions);
63
+ }
64
+ return method(data, options);
65
+ });
66
+ }
@@ -1,16 +1,24 @@
1
- import { createError, defineNuxtRouteMiddleware, getRouteRules, navigateTo, useNuxtApp, useRequestHeaders, useRuntimeConfig } from "#imports";
1
+ import { createError, defineNuxtRouteMiddleware, getRouteRules, navigateTo, useNuxtApp, useRequestHeaders, useRuntimeConfig, useUserSession } from "#imports";
2
+ import { defu } from "defu";
3
+ import { createRouter, toRouteMatcher } from "radix3";
2
4
  import { matchesUser } from "../../utils/match-user.js";
5
+ let authRouteRulesPromise = null;
6
+ let routeRulesMatcherPromise = null;
3
7
  export default defineNuxtRouteMiddleware(async (to) => {
4
8
  const nuxtApp = useNuxtApp();
5
- if (import.meta.client) {
6
- const isPrerendered = nuxtApp.payload.prerenderedAt || nuxtApp.payload.isCached;
7
- if (isPrerendered && nuxtApp.isHydrating)
8
- return;
9
- }
10
9
  if (to.meta.auth === void 0) {
11
- const rules = await getRouteRules({ path: to.path });
12
- if (rules.auth !== void 0)
13
- to.meta.auth = rules.auth;
10
+ const routeRulesMatcher = await getRouteRulesMatcher();
11
+ const matches = routeRulesMatcher?.matchAll(to.path);
12
+ if (matches?.length) {
13
+ const merged = defu({}, ...matches.reverse());
14
+ if (merged.auth !== void 0)
15
+ to.meta.auth = merged.auth;
16
+ }
17
+ if (to.meta.auth === void 0) {
18
+ const rules = await getRouteRules({ path: to.path });
19
+ if (rules.auth !== void 0)
20
+ to.meta.auth = rules.auth;
21
+ }
14
22
  }
15
23
  const auth = to.meta.auth;
16
24
  if (auth === void 0 || auth === false)
@@ -19,7 +27,8 @@ export default defineNuxtRouteMiddleware(async (to) => {
19
27
  const { fetchSession, user, loggedIn } = useUserSession();
20
28
  if (!loggedIn.value) {
21
29
  const headers = import.meta.server ? useRequestHeaders(["cookie"]) : void 0;
22
- await fetchSession({ headers });
30
+ const isHydratedPrerenderPayload = (import.meta.client || !import.meta.server) && nuxtApp.isHydrating && Boolean(nuxtApp.payload.prerenderedAt || nuxtApp.payload.isCached);
31
+ await fetchSession({ headers, ...isHydratedPrerenderPayload ? { force: true } : {} });
23
32
  }
24
33
  const mode = typeof auth === "string" ? auth : auth?.only ?? "user";
25
34
  const redirectTo = typeof auth === "object" ? auth.redirectTo : void 0;
@@ -28,10 +37,62 @@ export default defineNuxtRouteMiddleware(async (to) => {
28
37
  return navigateTo(redirectTo ?? config?.redirects?.guest ?? "/");
29
38
  return;
30
39
  }
31
- if (!loggedIn.value)
32
- return navigateTo(redirectTo ?? config?.redirects?.login ?? "/login");
40
+ if (!loggedIn.value) {
41
+ const resolved = resolveLoginRedirect({
42
+ route: to,
43
+ loginTarget: redirectTo ?? config?.redirects?.login ?? "/login",
44
+ config
45
+ });
46
+ return resolved.external ? navigateTo(resolved.to, { external: true }) : navigateTo(resolved.to);
47
+ }
33
48
  if (typeof auth === "object" && auth.user) {
34
49
  if (!user.value || !matchesUser(user.value, auth.user))
35
50
  throw createError({ statusCode: 403, statusMessage: "Access denied" });
36
51
  }
37
52
  });
53
+ function resolveLoginRedirect(input) {
54
+ const { route, loginTarget, config } = input;
55
+ const preserveRedirect = config?.preserveRedirect ?? true;
56
+ const redirectQueryKey = config?.redirectQueryKey ?? "redirect";
57
+ if (!preserveRedirect)
58
+ return { to: loginTarget, external: false };
59
+ if (!loginTarget.startsWith("/") || loginTarget.startsWith("//"))
60
+ return { to: loginTarget, external: false };
61
+ const [beforeHash, hash = ""] = loginTarget.split("#", 2);
62
+ const [path, query = ""] = beforeHash.split("?", 2);
63
+ try {
64
+ const params2 = new URLSearchParams(query);
65
+ if (params2.has(redirectQueryKey))
66
+ return { to: loginTarget, external: false };
67
+ } catch {
68
+ return { to: loginTarget, external: false };
69
+ }
70
+ if (import.meta.server) {
71
+ const separator = query ? "&" : "";
72
+ const encodedRedirect = encodeURIComponent(route.fullPath);
73
+ const url = `${path}?${query}${separator}${redirectQueryKey}=${encodedRedirect}${hash ? `#${hash}` : ""}`;
74
+ return { to: url, external: true };
75
+ }
76
+ const params = new URLSearchParams(query);
77
+ const queryObj = {};
78
+ for (const [k, v] of params.entries())
79
+ queryObj[k] = v;
80
+ queryObj[redirectQueryKey] = route.fullPath;
81
+ return { to: { path, query: queryObj, ...hash ? { hash: `#${hash}` } : {} }, external: false };
82
+ }
83
+ async function getAuthRouteRules() {
84
+ if (!authRouteRulesPromise) {
85
+ authRouteRulesPromise = import("#auth/route-rules").then((mod) => mod.authRouteRules || {}).catch(() => ({}));
86
+ }
87
+ return await authRouteRulesPromise;
88
+ }
89
+ async function getRouteRulesMatcher() {
90
+ if (!routeRulesMatcherPromise) {
91
+ routeRulesMatcherPromise = getAuthRouteRules().then((authRouteRules) => {
92
+ if (!Object.keys(authRouteRules).length)
93
+ return null;
94
+ return toRouteMatcher(createRouter({ routes: authRouteRules }));
95
+ });
96
+ }
97
+ return await routeRulesMatcherPromise;
98
+ }
@@ -374,19 +374,13 @@ function getAccountActions(row) {
374
374
  <UIcon name="i-lucide-settings-2" class="size-4" /><span>Module</span>
375
375
  </div>
376
376
  <div class="config-row">
377
- <span class="config-label">Login</span><span class="font-mono">{{ configData.config.module?.redirects?.login }}</span>
378
- </div>
379
- <div class="config-row">
380
- <span class="config-label">Guest</span><span class="font-mono">{{ configData.config.module?.redirects?.guest }}</span>
381
- </div>
382
- <div class="config-row">
383
- <span class="config-label">DB</span><UBadge :color="configData.config.module?.useDatabase ? 'success' : 'neutral'" variant="subtle" size="sm">
384
- {{ configData.config.module?.useDatabase ? "Hub" : "Off" }}
377
+ <span class="config-label">DB</span><UBadge :color="configData.config.module?.databaseProvider === 'none' ? 'neutral' : 'success'" variant="subtle" size="sm">
378
+ {{ configData.config.module?.databaseProvider === "nuxthub" ? "Hub" : "Off" }}
385
379
  </UBadge>
386
380
  </div>
387
381
  <div class="config-row">
388
- <span class="config-label">KV</span><UBadge :color="configData.config.module?.secondaryStorage ? 'success' : 'neutral'" variant="subtle" size="sm">
389
- {{ configData.config.module?.secondaryStorage ? "On" : "Off" }}
382
+ <span class="config-label">KV</span><UBadge :color="configData.config.module?.hubSecondaryStorage ? 'success' : 'neutral'" variant="subtle" size="sm">
383
+ {{ configData.config.module?.hubSecondaryStorage ? configData.config.module.hubSecondaryStorage === "custom" ? "Custom" : "On" : "Off" }}
390
384
  </UBadge>
391
385
  </div>
392
386
  </div>
@@ -1,5 +1,4 @@
1
- import { defineNuxtPlugin } from "#imports";
2
- import { useUserSession } from "../composables/useUserSession.js";
1
+ import { defineNuxtPlugin, useUserSession } from "#imports";
3
2
  export default defineNuxtPlugin(async (nuxtApp) => {
4
3
  const { fetchSession } = useUserSession();
5
4
  const safeFetch = async () => {
@@ -1,41 +1,96 @@
1
- import type { BetterAuthOptions } from 'better-auth';
2
- import type { ClientOptions } from 'better-auth/client';
1
+ import type { BetterAuthOptions, BetterAuthPlugin } from 'better-auth';
2
+ import type { BetterAuthClientOptions } from 'better-auth/client';
3
+ import type { Casing } from 'drizzle-orm/utils';
3
4
  import type { ServerAuthContext } from './types/augment.js';
5
+ import { createAuthClient } from 'better-auth/vue';
4
6
  export type { ServerAuthContext };
5
7
  export interface ClientAuthContext {
6
8
  siteUrl: string;
7
9
  }
8
- type ServerAuthConfig = Omit<BetterAuthOptions, 'database' | 'secret' | 'baseURL'>;
9
- type ClientAuthConfig = Omit<ClientOptions, 'baseURL'> & {
10
+ export type ServerAuthConfig = Omit<BetterAuthOptions, 'secret' | 'baseURL'> & {
11
+ plugins?: readonly BetterAuthPlugin[];
12
+ };
13
+ export type ClientAuthConfig = Omit<BetterAuthClientOptions, 'baseURL'> & {
10
14
  baseURL?: string;
11
15
  };
12
16
  export type ServerAuthConfigFn = (ctx: ServerAuthContext) => ServerAuthConfig;
13
17
  export type ClientAuthConfigFn = (ctx: ClientAuthContext) => ClientAuthConfig;
18
+ export type ModuleDatabaseProviderId = 'none' | 'nuxthub' | (string & {});
19
+ export type EffectiveDatabaseProviderId = 'user' | ModuleDatabaseProviderId;
14
20
  export interface BetterAuthModuleOptions {
15
- /** Server config path relative to rootDir. Default: 'server/auth.config' */
21
+ /** Client-only mode - skip server setup for external auth backends */
22
+ clientOnly?: boolean;
23
+ /** Server config path. Relative paths resolve from the layer that declares them. Default: 'server/auth.config' */
16
24
  serverConfig?: string;
17
- /** Client config path relative to rootDir. Default: 'app/auth.config' */
25
+ /** Client config path. Relative paths resolve from the layer that declares them. Default: 'app/auth.config' */
18
26
  clientConfig?: string;
19
27
  redirects?: {
28
+ /** Where to redirect unauthenticated users. Default: '/login' */
20
29
  login?: string;
30
+ /** Where to redirect authenticated users on guest-only routes. Default: '/' */
21
31
  guest?: string;
32
+ /** Where to navigate after successful signIn/signUp when no onSuccess is provided. Default: no automatic navigation */
33
+ authenticated?: string;
34
+ /** Where to navigate after logout. Default: no automatic navigation */
35
+ logout?: string;
36
+ };
37
+ /**
38
+ * When redirecting unauthenticated users to the login route, append a query param
39
+ * containing the originally requested path (for safe "return-to" redirects).
40
+ *
41
+ * Default: true
42
+ */
43
+ preserveRedirect?: boolean;
44
+ /**
45
+ * Query param key used by preserveRedirect.
46
+ *
47
+ * Default: 'redirect'
48
+ */
49
+ redirectQueryKey?: string;
50
+ session?: {
51
+ /**
52
+ * When enabled, and session/user are already hydrated from SSR, skip the initial
53
+ * client `/api/auth/get-session` bootstrap request. This also skips Better Auth's
54
+ * session refresh manager on those pages.
55
+ *
56
+ * Default: false
57
+ */
58
+ skipHydratedSsrGetSession?: boolean;
22
59
  };
23
- /** Enable KV secondary storage for sessions. Requires hub.kv: true */
24
- secondaryStorage?: boolean;
60
+ /**
61
+ * Enable secondary storage for sessions.
62
+ * - `true`: Use NuxtHub KV (requires hub.kv: true)
63
+ * - `'custom'`: User provides own secondaryStorage in defineServerAuth()
64
+ * - `false` (default): No secondary storage from module
65
+ */
66
+ hubSecondaryStorage?: boolean | 'custom';
25
67
  /** Schema generation options. Must match drizzleAdapter config. */
26
68
  schema?: {
27
69
  /** Plural table names: user → users. Default: false */
28
70
  usePlural?: boolean;
71
+ /** Column/table name casing. Explicit value takes precedence over hub.db.casing. */
72
+ casing?: Casing;
29
73
  };
30
74
  }
31
75
  export interface AuthRuntimeConfig {
32
76
  redirects: {
33
77
  login: string;
34
78
  guest: string;
79
+ authenticated?: string;
80
+ logout?: string;
81
+ };
82
+ preserveRedirect: boolean;
83
+ redirectQueryKey: string;
84
+ useDatabase: boolean;
85
+ databaseProvider: EffectiveDatabaseProviderId;
86
+ clientOnly: boolean;
87
+ session: {
88
+ skipHydratedSsrGetSession: boolean;
35
89
  };
36
90
  }
37
91
  export interface AuthPrivateRuntimeConfig {
38
- secondaryStorage: boolean;
92
+ hubSecondaryStorage: boolean | 'custom';
39
93
  }
40
- export declare function defineServerAuth<T extends ServerAuthConfig>(config: (ctx: ServerAuthContext) => T): (ctx: ServerAuthContext) => T;
41
- export declare function defineClientAuth(config: ClientAuthConfigFn): ClientAuthConfigFn;
94
+ export declare function defineServerAuth<const R>(config: (ctx: ServerAuthContext) => R & ServerAuthConfig): (ctx: ServerAuthContext) => R;
95
+ export declare function defineServerAuth<const R>(config: R & ServerAuthConfig): (ctx: ServerAuthContext) => R;
96
+ export declare function defineClientAuth<T extends ClientAuthConfig>(config: T | ((ctx: ClientAuthContext) => T)): (baseURL: string) => ReturnType<typeof createAuthClient<T>>;
@@ -1,6 +1,13 @@
1
+ import { createAuthClient } from "better-auth/vue";
1
2
  export function defineServerAuth(config) {
2
- return config;
3
+ return typeof config === "function" ? config : () => config;
3
4
  }
4
5
  export function defineClientAuth(config) {
5
- return config;
6
+ return (baseURL) => {
7
+ const ctx = { siteUrl: baseURL };
8
+ const resolved = typeof config === "function" ? config(ctx) : config;
9
+ const { baseURL: configuredBaseURL, ...resolvedOptions } = resolved;
10
+ const clientOptions = { ...resolvedOptions, baseURL: configuredBaseURL ?? baseURL };
11
+ return createAuthClient(clientOptions);
12
+ };
6
13
  }
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ const SQL_LIKE_ESCAPE_RE = /[%_\\]/g;
2
3
  export const paginationQuerySchema = z.object({
3
4
  page: z.coerce.number().int().min(1).default(1),
4
5
  limit: z.coerce.number().int().min(1).max(100).default(20),
@@ -7,5 +8,5 @@ export const paginationQuerySchema = z.object({
7
8
  export function sanitizeSearchPattern(search) {
8
9
  if (!search)
9
10
  return "";
10
- return `%${search.replace(/[%_\\]/g, "\\$&")}%`;
11
+ return `%${search.replace(SQL_LIKE_ESCAPE_RE, "\\$&")}%`;
11
12
  }
@@ -2,8 +2,9 @@ import { defineEventHandler, getQuery } from "h3";
2
2
  import { paginationQuerySchema, sanitizeSearchPattern } from "./_schema.js";
3
3
  export default defineEventHandler(async (event) => {
4
4
  try {
5
- const { db, schema } = await import("hub:db");
6
- if (!schema.account)
5
+ const { db } = await import("@nuxthub/db");
6
+ const { schema } = await import("#auth/schema");
7
+ if (!schema?.account)
7
8
  return { accounts: [], total: 0, error: "Account table not found" };
8
9
  const query = paginationQuerySchema.parse(getQuery(event));
9
10
  const { page, limit, search } = query;
@@ -2,28 +2,34 @@ declare const _default: import("h3").EventHandler<import("h3").EventHandlerReque
2
2
  config: {
3
3
  module: {
4
4
  redirects: {
5
- login?: string;
6
- guest?: string;
5
+ login: string;
6
+ guest: string;
7
+ authenticated: string | undefined;
8
+ logout: string | undefined;
7
9
  };
8
- secondaryStorage: boolean;
10
+ preserveRedirect: boolean;
11
+ redirectQueryKey: string;
12
+ hubSecondaryStorage: boolean | "custom";
9
13
  useDatabase: boolean;
14
+ databaseProvider: "none" | "nuxthub";
10
15
  };
11
16
  server: {
12
- baseURL: string | undefined;
13
- basePath: string;
17
+ baseURL: any;
18
+ basePath: any;
14
19
  socialProviders: string[];
15
- plugins: string[];
16
- trustedOrigins: string[] | ((request: Request) => string[] | Promise<string[]>);
20
+ plugins: any;
21
+ trustedOrigins: any;
22
+ configuredTrustedOrigins: any;
17
23
  session: {
18
24
  expiresIn: string;
19
25
  updateAge: string;
20
- cookieCache: boolean;
26
+ cookieCache: any;
21
27
  };
22
28
  emailAndPassword: boolean;
23
- rateLimit: boolean;
29
+ rateLimit: any;
24
30
  advanced: {
25
- useSecureCookies: string | boolean;
26
- disableCSRFCheck: boolean;
31
+ useSecureCookies: any;
32
+ disableCSRFCheck: any;
27
33
  };
28
34
  };
29
35
  };
@@ -3,11 +3,14 @@ import { useRuntimeConfig } from "nitropack/runtime";
3
3
  import { serverAuth } from "../../utils/auth.js";
4
4
  export default defineEventHandler(async (event) => {
5
5
  try {
6
- const auth = await serverAuth(event);
6
+ const auth = serverAuth(event);
7
7
  const options = auth.options;
8
+ const authContext = await auth.$context;
8
9
  const runtimeConfig = useRuntimeConfig();
9
10
  const publicAuth = runtimeConfig.public?.auth;
10
11
  const privateAuth = runtimeConfig.auth;
12
+ const configuredTrustedOrigins = Array.isArray(options.trustedOrigins) ? options.trustedOrigins : [];
13
+ const effectiveTrustedOrigins = authContext?.trustedOrigins || configuredTrustedOrigins;
11
14
  const sessionConfig = options.session || {};
12
15
  const expiresInDays = sessionConfig.expiresIn ? Math.round(sessionConfig.expiresIn / 86400) : 7;
13
16
  const updateAgeDays = sessionConfig.updateAge ? Math.round(sessionConfig.updateAge / 86400) : 1;
@@ -15,9 +18,17 @@ export default defineEventHandler(async (event) => {
15
18
  config: {
16
19
  // Module config (nuxt.config.ts)
17
20
  module: {
18
- redirects: publicAuth?.redirects || { login: "/login", guest: "/" },
19
- secondaryStorage: privateAuth?.secondaryStorage ?? false,
20
- useDatabase: publicAuth?.useDatabase ?? false
21
+ redirects: {
22
+ login: publicAuth?.redirects?.login ?? "/login",
23
+ guest: publicAuth?.redirects?.guest ?? "/",
24
+ authenticated: publicAuth?.redirects?.authenticated,
25
+ logout: publicAuth?.redirects?.logout
26
+ },
27
+ preserveRedirect: publicAuth?.preserveRedirect ?? true,
28
+ redirectQueryKey: publicAuth?.redirectQueryKey ?? "redirect",
29
+ hubSecondaryStorage: privateAuth?.hubSecondaryStorage ?? false,
30
+ useDatabase: publicAuth?.useDatabase ?? false,
31
+ databaseProvider: publicAuth?.databaseProvider ?? "none"
21
32
  },
22
33
  // Server config (server/auth.config.ts)
23
34
  server: {
@@ -25,7 +36,8 @@ export default defineEventHandler(async (event) => {
25
36
  basePath: options.basePath || "/api/auth",
26
37
  socialProviders: Object.keys(options.socialProviders || {}),
27
38
  plugins: (options.plugins || []).map((p) => p.id || "unknown"),
28
- trustedOrigins: options.trustedOrigins || [],
39
+ trustedOrigins: effectiveTrustedOrigins,
40
+ configuredTrustedOrigins,
29
41
  session: {
30
42
  expiresIn: `${expiresInDays} days`,
31
43
  updateAge: `${updateAgeDays} days`,
@@ -6,8 +6,9 @@ const deleteSessionSchema = z.object({
6
6
  export default defineEventHandler(async (event) => {
7
7
  try {
8
8
  const body = deleteSessionSchema.parse(await readBody(event));
9
- const { db, schema } = await import("hub:db");
10
- if (!schema.session)
9
+ const { db } = await import("@nuxthub/db");
10
+ const { schema } = await import("#auth/schema");
11
+ if (!schema?.session)
11
12
  throw createError({ statusCode: 500, message: "Session table not found" });
12
13
  const { eq } = await import("drizzle-orm");
13
14
  await db.delete(schema.session).where(eq(schema.session.id, body.id));
@@ -1,3 +1,5 @@
1
+ import type { Session } from 'better-auth/types';
2
+ type SafeSession = Pick<Session, 'id' | 'userId' | 'createdAt' | 'updatedAt' | 'expiresAt' | 'ipAddress' | 'userAgent'>;
1
3
  declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
2
4
  sessions: never[];
3
5
  total: number;
@@ -5,7 +7,7 @@ declare const _default: import("h3").EventHandler<import("h3").EventHandlerReque
5
7
  page?: undefined;
6
8
  limit?: undefined;
7
9
  } | {
8
- sessions: any;
10
+ sessions: SafeSession[];
9
11
  total: any;
10
12
  page: number;
11
13
  limit: number;
@@ -2,8 +2,9 @@ import { defineEventHandler, getQuery } from "h3";
2
2
  import { paginationQuerySchema, sanitizeSearchPattern } from "./_schema.js";
3
3
  export default defineEventHandler(async (event) => {
4
4
  try {
5
- const { db, schema } = await import("hub:db");
6
- if (!schema.session)
5
+ const { db } = await import("@nuxthub/db");
6
+ const { schema } = await import("#auth/schema");
7
+ if (!schema?.session)
7
8
  return { sessions: [], total: 0, error: "Session table not found" };
8
9
  const query = paginationQuerySchema.parse(getQuery(event));
9
10
  const { page, limit, search } = query;
@@ -2,8 +2,9 @@ import { defineEventHandler, getQuery } from "h3";
2
2
  import { paginationQuerySchema, sanitizeSearchPattern } from "./_schema.js";
3
3
  export default defineEventHandler(async (event) => {
4
4
  try {
5
- const { db, schema } = await import("hub:db");
6
- if (!schema.user)
5
+ const { db } = await import("@nuxthub/db");
6
+ const { schema } = await import("#auth/schema");
7
+ if (!schema?.user)
7
8
  return { users: [], total: 0, error: "User table not found" };
8
9
  const query = paginationQuerySchema.parse(getQuery(event));
9
10
  const { page, limit, search } = query;
@@ -1,6 +1,6 @@
1
1
  import { defineEventHandler, toWebRequest } from "h3";
2
2
  import { serverAuth } from "../../utils/auth.js";
3
3
  export default defineEventHandler(async (event) => {
4
- const auth = await serverAuth(event);
4
+ const auth = serverAuth(event);
5
5
  return auth.handler(toWebRequest(event));
6
6
  });
@@ -1,6 +1,7 @@
1
1
  import { createError, defineEventHandler, getRequestURL } from "h3";
2
2
  import { getRouteRules } from "nitropack/runtime";
3
3
  import { matchesUser } from "../../utils/match-user.js";
4
+ import { getUserSession, requireUserSession } from "../utils/session.js";
4
5
  export default defineEventHandler(async (event) => {
5
6
  const path = getRequestURL(event).pathname;
6
7
  if (!path.startsWith("/api/"))
@@ -1,3 +1,11 @@
1
1
  {
2
- "extends": "../../../.nuxt/tsconfig.server.json"
2
+ "extends": "../../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "baseUrl": ".",
5
+ "paths": {
6
+ "#nuxt-better-auth": ["../types/augment"]
7
+ }
8
+ },
9
+ "include": ["./**/*.ts", "./**/*.d.ts"],
10
+ "exclude": ["node_modules"]
3
11
  }