@onmax/nuxt-better-auth 0.0.2-alpha.30 → 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.
- package/dist/module.d.mts +0 -9
- package/dist/module.json +1 -1
- package/dist/module.mjs +135 -53
- package/dist/runtime/app/composables/useUserSession.js +41 -161
- package/dist/runtime/app/internal/auth-action-error.js +1 -3
- package/dist/runtime/app/internal/auth-action-handles.js +1 -3
- package/dist/runtime/app/internal/redirect-helpers.d.ts +4 -0
- package/dist/runtime/app/internal/redirect-helpers.js +37 -0
- package/dist/runtime/app/internal/session-fetch.d.ts +12 -0
- package/dist/runtime/app/internal/session-fetch.js +56 -0
- package/dist/runtime/app/internal/utils.d.ts +1 -0
- package/dist/runtime/app/internal/utils.js +3 -0
- package/dist/runtime/app/internal/wrap-auth-method.d.ts +15 -0
- package/dist/runtime/app/internal/wrap-auth-method.js +66 -0
- package/dist/runtime/config.d.ts +4 -4
- package/dist/runtime/config.js +3 -1
- package/dist/runtime/server/api/_better-auth/_schema.js +2 -1
- package/package.json +7 -16
package/dist/module.d.mts
CHANGED
|
@@ -5,15 +5,6 @@ export { BetterAuthModuleOptions, defineClientAuth, defineServerAuth } from '../
|
|
|
5
5
|
import { BetterAuthOptions } from 'better-auth';
|
|
6
6
|
export { AppSession, Auth, AuthActionError, AuthMeta, AuthMode, AuthRouteRules, AuthSession, AuthSocialProviderId, AuthUser, InferSession, InferUser, RequireSessionOptions, ServerAuthContext, UserMatch } from '../dist/runtime/types.js';
|
|
7
7
|
|
|
8
|
-
interface RuntimeDefineServerAuthFn {
|
|
9
|
-
(...args: unknown[]): unknown;
|
|
10
|
-
_count: number;
|
|
11
|
-
}
|
|
12
|
-
declare global {
|
|
13
|
-
var __nuxtBetterAuthDefineServerAuth: RuntimeDefineServerAuthFn | undefined;
|
|
14
|
-
var defineServerAuth: RuntimeDefineServerAuthFn | undefined;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
8
|
type DbDialect = 'sqlite' | 'postgresql' | 'mysql';
|
|
18
9
|
|
|
19
10
|
interface BetterAuthDatabaseProviderBuildContext {
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { existsSync, writeFileSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { updateTemplates, addServerImportsDir, addServerImports, addServerScanDir, addServerHandler, addImportsDir, addPlugin, addComponentsDir, hasNuxtModule, installModule, extendPages, addTemplate, addTypeTemplate, defineNuxtModule, createResolver } from '@nuxt/kit';
|
|
3
|
+
import { getLayerDirectories, updateTemplates, addServerImportsDir, addServerImports, addServerScanDir, addServerHandler, addImportsDir, addPlugin, addComponentsDir, hasNuxtModule, installModule, extendPages, addTemplate, addTypeTemplate, defineNuxtModule, createResolver } from '@nuxt/kit';
|
|
4
4
|
import { consola as consola$1 } from 'consola';
|
|
5
|
-
import { join, dirname } from 'pathe';
|
|
5
|
+
import { isAbsolute, join, relative, dirname } from 'pathe';
|
|
6
6
|
import { defu } from 'defu';
|
|
7
7
|
import { toRouteMatcher, createRouter } from 'radix3';
|
|
8
8
|
import { generateDrizzleSchema as generateDrizzleSchema$1 } from '@better-auth/cli/api';
|
|
@@ -10,7 +10,7 @@ import { randomBytes } from 'node:crypto';
|
|
|
10
10
|
import { isCI, isTest } from 'std-env';
|
|
11
11
|
export { defineClientAuth, defineServerAuth } from '../dist/runtime/config.js';
|
|
12
12
|
|
|
13
|
-
const version = "0.0.2-alpha.
|
|
13
|
+
const version = "0.0.2-alpha.31";
|
|
14
14
|
|
|
15
15
|
function resolveDatabaseProvider(input) {
|
|
16
16
|
const enabledProviders = Object.entries(input.providers).filter(([_id, provider]) => provider.isEnabled?.(input.context) ?? true);
|
|
@@ -22,6 +22,91 @@ function resolveDatabaseProvider(input) {
|
|
|
22
22
|
return { id, definition };
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
const CONFIG_EXTENSIONS = [".ts", ".js"];
|
|
26
|
+
const CONFIG_EXTENSION_RE = /\.(?:ts|js)$/;
|
|
27
|
+
const DEFAULT_CONFIG_FILES = {
|
|
28
|
+
server: "server/auth.config",
|
|
29
|
+
client: "app/auth.config"
|
|
30
|
+
};
|
|
31
|
+
const OPTION_KEY_BY_KIND = {
|
|
32
|
+
server: "serverConfig",
|
|
33
|
+
client: "clientConfig"
|
|
34
|
+
};
|
|
35
|
+
function stripConfigExtension(path) {
|
|
36
|
+
return path.replace(CONFIG_EXTENSION_RE, "");
|
|
37
|
+
}
|
|
38
|
+
function configExists(path) {
|
|
39
|
+
return CONFIG_EXTENSIONS.some((ext) => existsSync(`${path}${ext}`));
|
|
40
|
+
}
|
|
41
|
+
function getLayerDirectoriesWithConfigs(nuxt) {
|
|
42
|
+
const directories = getLayerDirectories(nuxt);
|
|
43
|
+
const layers = nuxt.options._layers;
|
|
44
|
+
return directories.map((directory, index) => ({ directory, layer: layers[index] }));
|
|
45
|
+
}
|
|
46
|
+
function getProjectDirectory(nuxt) {
|
|
47
|
+
return getLayerDirectories(nuxt)[0];
|
|
48
|
+
}
|
|
49
|
+
function getDefaultConfigPath(nuxt, kind) {
|
|
50
|
+
const project = getProjectDirectory(nuxt);
|
|
51
|
+
return kind === "server" ? join(project.server, "auth.config") : join(project.app, "auth.config");
|
|
52
|
+
}
|
|
53
|
+
function getLayerDefaultConfigPath(nuxt, kind) {
|
|
54
|
+
for (const { directory } of getLayerDirectoriesWithConfigs(nuxt)) {
|
|
55
|
+
const candidate = kind === "server" ? join(directory.server, "auth.config") : join(directory.app, "auth.config");
|
|
56
|
+
if (configExists(candidate))
|
|
57
|
+
return candidate;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function resolveDeclaringLayerRoot(nuxt, kind, file) {
|
|
61
|
+
const optionKey = OPTION_KEY_BY_KIND[kind];
|
|
62
|
+
for (const { directory, layer } of getLayerDirectoriesWithConfigs(nuxt)) {
|
|
63
|
+
const declared = layer?.config?.auth?.[optionKey];
|
|
64
|
+
if (typeof declared === "string" && stripConfigExtension(declared) === file)
|
|
65
|
+
return directory.root;
|
|
66
|
+
}
|
|
67
|
+
return getProjectDirectory(nuxt).root;
|
|
68
|
+
}
|
|
69
|
+
function getRelativeConfigFile(nuxt, path) {
|
|
70
|
+
return relative(getProjectDirectory(nuxt).root, path);
|
|
71
|
+
}
|
|
72
|
+
function getEffectiveModuleConfigFile(nuxt, kind) {
|
|
73
|
+
const optionKey = OPTION_KEY_BY_KIND[kind];
|
|
74
|
+
const authOptions = nuxt.options.auth;
|
|
75
|
+
return authOptions?.[optionKey] ?? DEFAULT_CONFIG_FILES[kind];
|
|
76
|
+
}
|
|
77
|
+
function shouldCreateDefaultModuleConfig(nuxt, kind, file = getEffectiveModuleConfigFile(nuxt, kind)) {
|
|
78
|
+
const normalizedFile = stripConfigExtension(file);
|
|
79
|
+
if (normalizedFile !== DEFAULT_CONFIG_FILES[kind])
|
|
80
|
+
return false;
|
|
81
|
+
const resolved = resolveModuleConfigPath(nuxt, kind, normalizedFile);
|
|
82
|
+
return !configExists(resolved.path);
|
|
83
|
+
}
|
|
84
|
+
function resolveModuleConfigPath(nuxt, kind, file) {
|
|
85
|
+
const normalizedFile = stripConfigExtension(file);
|
|
86
|
+
if (isAbsolute(normalizedFile)) {
|
|
87
|
+
return {
|
|
88
|
+
file: normalizedFile,
|
|
89
|
+
path: normalizedFile,
|
|
90
|
+
isDefault: false
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (normalizedFile === DEFAULT_CONFIG_FILES[kind]) {
|
|
94
|
+
const discoveredPath = getLayerDefaultConfigPath(nuxt, kind) ?? getDefaultConfigPath(nuxt, kind);
|
|
95
|
+
return {
|
|
96
|
+
file: getRelativeConfigFile(nuxt, discoveredPath),
|
|
97
|
+
path: discoveredPath,
|
|
98
|
+
isDefault: true
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const baseRoot = resolveDeclaringLayerRoot(nuxt, kind, normalizedFile);
|
|
102
|
+
const path = join(baseRoot, normalizedFile);
|
|
103
|
+
return {
|
|
104
|
+
file: getRelativeConfigFile(nuxt, path),
|
|
105
|
+
path,
|
|
106
|
+
isDefault: false
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
25
110
|
function setupDevTools(nuxt) {
|
|
26
111
|
const hookable = nuxt;
|
|
27
112
|
hookable.hook("devtools:customTabs", (tabs) => {
|
|
@@ -167,13 +252,13 @@ function setupRuntimeConfig(input) {
|
|
|
167
252
|
return { useHubKV, secondaryStorageEnabled };
|
|
168
253
|
}
|
|
169
254
|
const currentSecret = nuxt.options.runtimeConfig.betterAuthSecret;
|
|
170
|
-
nuxt.options.runtimeConfig.betterAuthSecret = currentSecret || process.env.BETTER_AUTH_SECRET || "";
|
|
255
|
+
nuxt.options.runtimeConfig.betterAuthSecret = currentSecret || process.env.NUXT_BETTER_AUTH_SECRET || process.env.BETTER_AUTH_SECRET || "";
|
|
171
256
|
const betterAuthSecret = nuxt.options.runtimeConfig.betterAuthSecret;
|
|
172
257
|
if (!nuxt.options.dev && !nuxt.options._prepare && !betterAuthSecret) {
|
|
173
|
-
throw new Error("[nuxt-better-auth]
|
|
258
|
+
throw new Error("[nuxt-better-auth] NUXT_BETTER_AUTH_SECRET is required in production. Set NUXT_BETTER_AUTH_SECRET or BETTER_AUTH_SECRET environment variable.");
|
|
174
259
|
}
|
|
175
260
|
if (betterAuthSecret && betterAuthSecret.length < 32) {
|
|
176
|
-
throw new Error("[nuxt-better-auth]
|
|
261
|
+
throw new Error("[nuxt-better-auth] NUXT_BETTER_AUTH_SECRET must be at least 32 characters for security");
|
|
177
262
|
}
|
|
178
263
|
nuxt.options.runtimeConfig.auth = defu(nuxt.options.runtimeConfig.auth, {
|
|
179
264
|
hubSecondaryStorage: options.hubSecondaryStorage ?? false
|
|
@@ -217,14 +302,15 @@ async function loadUserAuthConfig(configPath, throwOnError = false) {
|
|
|
217
302
|
const { createJiti } = await import('jiti');
|
|
218
303
|
const { defineServerAuth: runtimeDefineServerAuth } = await import('../dist/runtime/config.js');
|
|
219
304
|
const jiti = createJiti(import.meta.url, { interopDefault: true, moduleCache: false });
|
|
220
|
-
|
|
305
|
+
const schemaGlobals = globalThis;
|
|
306
|
+
if (!schemaGlobals.__nuxtBetterAuthDefineServerAuth) {
|
|
221
307
|
runtimeDefineServerAuth._count = 0;
|
|
222
|
-
|
|
308
|
+
schemaGlobals.__nuxtBetterAuthDefineServerAuth = runtimeDefineServerAuth;
|
|
223
309
|
}
|
|
224
|
-
if (!
|
|
225
|
-
|
|
310
|
+
if (!schemaGlobals.defineServerAuth) {
|
|
311
|
+
schemaGlobals.defineServerAuth = schemaGlobals.__nuxtBetterAuthDefineServerAuth;
|
|
226
312
|
}
|
|
227
|
-
|
|
313
|
+
schemaGlobals.__nuxtBetterAuthDefineServerAuth._count++;
|
|
228
314
|
try {
|
|
229
315
|
const mod = await jiti.import(configPath);
|
|
230
316
|
const configFn = mod.default;
|
|
@@ -243,19 +329,20 @@ async function loadUserAuthConfig(configPath, throwOnError = false) {
|
|
|
243
329
|
consola$1.error("[@onmax/nuxt-better-auth] Failed to load auth config for schema generation. Schema may be incomplete:", error);
|
|
244
330
|
return {};
|
|
245
331
|
} finally {
|
|
246
|
-
const sharedDefineServerAuth =
|
|
332
|
+
const sharedDefineServerAuth = schemaGlobals.__nuxtBetterAuthDefineServerAuth;
|
|
247
333
|
if (sharedDefineServerAuth) {
|
|
248
334
|
sharedDefineServerAuth._count--;
|
|
249
335
|
if (!sharedDefineServerAuth._count) {
|
|
250
|
-
|
|
251
|
-
if (
|
|
252
|
-
|
|
336
|
+
schemaGlobals.__nuxtBetterAuthDefineServerAuth = void 0;
|
|
337
|
+
if (schemaGlobals.defineServerAuth === sharedDefineServerAuth) {
|
|
338
|
+
schemaGlobals.defineServerAuth = void 0;
|
|
253
339
|
}
|
|
254
340
|
}
|
|
255
341
|
}
|
|
256
342
|
}
|
|
257
343
|
}
|
|
258
344
|
|
|
345
|
+
const NODE_MODULES_SEGMENT_RE = /[\\/]/;
|
|
259
346
|
function resolveSchemaSecondaryStorageInjection(hubSecondaryStorage, userHasSecondaryStorage, isProduction) {
|
|
260
347
|
if (hubSecondaryStorage === true)
|
|
261
348
|
return { inject: false };
|
|
@@ -269,7 +356,7 @@ function resolveSchemaSecondaryStorageInjection(hubSecondaryStorage, userHasSeco
|
|
|
269
356
|
return { inject: false, warn: message };
|
|
270
357
|
}
|
|
271
358
|
function isInsideNodeModules(path) {
|
|
272
|
-
return path.split(
|
|
359
|
+
return path.split(NODE_MODULES_SEGMENT_RE).includes("node_modules");
|
|
273
360
|
}
|
|
274
361
|
function resolveHubSchemaPath(buildDir, rootDir, dialect, exists = existsSync) {
|
|
275
362
|
const rootTsPath = join(rootDir, ".nuxt", "better-auth", `schema.${dialect}.ts`);
|
|
@@ -348,21 +435,26 @@ async function setupBetterAuthSchema(nuxt, serverConfigPath, options, consola, h
|
|
|
348
435
|
}
|
|
349
436
|
}
|
|
350
437
|
|
|
438
|
+
const DEFAULT_SECRET_ENV = "NUXT_BETTER_AUTH_SECRET";
|
|
439
|
+
const FALLBACK_SECRET_ENV = "BETTER_AUTH_SECRET";
|
|
351
440
|
const generateSecret = () => randomBytes(32).toString("hex");
|
|
352
441
|
function readEnvFile(rootDir) {
|
|
353
442
|
const envPath = join(rootDir, ".env");
|
|
354
443
|
return existsSync(envPath) ? readFileSync(envPath, "utf-8") : "";
|
|
355
444
|
}
|
|
356
445
|
function hasEnvSecret(rootDir) {
|
|
357
|
-
const
|
|
358
|
-
return
|
|
446
|
+
const envFile = readEnvFile(rootDir);
|
|
447
|
+
return [DEFAULT_SECRET_ENV, FALLBACK_SECRET_ENV].some((name) => {
|
|
448
|
+
const match = envFile.match(new RegExp(`^${name}=(.+)$`, "m"));
|
|
449
|
+
return !!match && !!match[1] && match[1].trim().length > 0;
|
|
450
|
+
});
|
|
359
451
|
}
|
|
360
452
|
function appendSecretToEnv(rootDir, secret) {
|
|
361
453
|
const envPath = join(rootDir, ".env");
|
|
362
454
|
let content = readEnvFile(rootDir);
|
|
363
455
|
if (content.length > 0 && !content.endsWith("\n"))
|
|
364
456
|
content += "\n";
|
|
365
|
-
content +=
|
|
457
|
+
content += `${DEFAULT_SECRET_ENV}=${secret}
|
|
366
458
|
`;
|
|
367
459
|
writeFileSync(envPath, content, "utf-8");
|
|
368
460
|
}
|
|
@@ -370,20 +462,20 @@ async function promptForSecret(rootDir, consola, options = {}) {
|
|
|
370
462
|
const configuredSecret = options.configuredSecret?.trim();
|
|
371
463
|
if (configuredSecret)
|
|
372
464
|
return void 0;
|
|
373
|
-
if (process.env.BETTER_AUTH_SECRET || hasEnvSecret(rootDir))
|
|
465
|
+
if (process.env.NUXT_BETTER_AUTH_SECRET || process.env.BETTER_AUTH_SECRET || hasEnvSecret(rootDir))
|
|
374
466
|
return void 0;
|
|
375
467
|
const hasTty = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
376
468
|
if (options.prepare || !hasTty) {
|
|
377
|
-
consola.warn("[nuxt-better-auth] Skipping
|
|
469
|
+
consola.warn("[nuxt-better-auth] Skipping NUXT_BETTER_AUTH_SECRET prompt (non-interactive). Set NUXT_BETTER_AUTH_SECRET or BETTER_AUTH_SECRET.");
|
|
378
470
|
return void 0;
|
|
379
471
|
}
|
|
380
472
|
if (isCI || isTest) {
|
|
381
473
|
const secret2 = generateSecret();
|
|
382
474
|
appendSecretToEnv(rootDir, secret2);
|
|
383
|
-
consola.info("Generated
|
|
475
|
+
consola.info("Generated NUXT_BETTER_AUTH_SECRET and added to .env (CI/test mode)");
|
|
384
476
|
return secret2;
|
|
385
477
|
}
|
|
386
|
-
consola.box("
|
|
478
|
+
consola.box("NUXT_BETTER_AUTH_SECRET is required for authentication.\nThis will be appended to your .env file.\nBETTER_AUTH_SECRET is still supported as a fallback.");
|
|
387
479
|
const choice = await consola.prompt("How do you want to set it?", {
|
|
388
480
|
type: "select",
|
|
389
481
|
options: [
|
|
@@ -394,7 +486,7 @@ async function promptForSecret(rootDir, consola, options = {}) {
|
|
|
394
486
|
cancel: "null"
|
|
395
487
|
});
|
|
396
488
|
if (typeof choice === "symbol" || choice === "skip") {
|
|
397
|
-
consola.warn("Skipping
|
|
489
|
+
consola.warn("Skipping NUXT_BETTER_AUTH_SECRET. Auth will fail without it in production.");
|
|
398
490
|
return void 0;
|
|
399
491
|
}
|
|
400
492
|
let secret;
|
|
@@ -410,14 +502,14 @@ async function promptForSecret(rootDir, consola, options = {}) {
|
|
|
410
502
|
}
|
|
411
503
|
const preview = `${secret.slice(0, 8)}...${secret.slice(-4)}`;
|
|
412
504
|
const confirm = await consola.prompt(`Add to .env:
|
|
413
|
-
|
|
505
|
+
${DEFAULT_SECRET_ENV}=${preview}
|
|
414
506
|
Proceed?`, { type: "confirm", initial: true, cancel: "null" });
|
|
415
507
|
if (typeof confirm === "symbol" || !confirm) {
|
|
416
508
|
consola.info("Cancelled. Secret not written.");
|
|
417
509
|
return void 0;
|
|
418
510
|
}
|
|
419
511
|
appendSecretToEnv(rootDir, secret);
|
|
420
|
-
consola.success("Added
|
|
512
|
+
consola.success("Added NUXT_BETTER_AUTH_SECRET to .env");
|
|
421
513
|
return secret;
|
|
422
514
|
}
|
|
423
515
|
|
|
@@ -778,15 +870,13 @@ declare module '#nuxt-better-auth' {
|
|
|
778
870
|
}
|
|
779
871
|
|
|
780
872
|
const consola = consola$1.withTag("nuxt-better-auth");
|
|
781
|
-
function
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
const
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
const serverPath = join(rootDir, "server/auth.config.ts");
|
|
789
|
-
const clientPath = join(srcDir, "auth.config.ts");
|
|
873
|
+
async function createDefaultAuthConfigFiles(nuxt) {
|
|
874
|
+
const project = getLayerDirectories(nuxt)[0];
|
|
875
|
+
const rootDir = project.root;
|
|
876
|
+
const serverPath = join(project.server, "auth.config.ts");
|
|
877
|
+
const clientPath = join(project.app, "auth.config.ts");
|
|
878
|
+
const serverConfigFile = getEffectiveModuleConfigFile(nuxt, "server");
|
|
879
|
+
const clientConfigFile = getEffectiveModuleConfigFile(nuxt, "client");
|
|
790
880
|
const serverTemplate = `import { defineServerAuth } from '@onmax/nuxt-better-auth/config'
|
|
791
881
|
|
|
792
882
|
export default defineServerAuth({
|
|
@@ -797,16 +887,15 @@ export default defineServerAuth({
|
|
|
797
887
|
|
|
798
888
|
export default defineClientAuth({})
|
|
799
889
|
`;
|
|
800
|
-
if (
|
|
890
|
+
if (shouldCreateDefaultModuleConfig(nuxt, "server", serverConfigFile)) {
|
|
801
891
|
await mkdir(dirname(serverPath), { recursive: true });
|
|
802
892
|
await writeFile(serverPath, serverTemplate);
|
|
803
|
-
consola.success(
|
|
893
|
+
consola.success(`Created ${relative(rootDir, serverPath)}`);
|
|
804
894
|
}
|
|
805
|
-
if (
|
|
895
|
+
if (shouldCreateDefaultModuleConfig(nuxt, "client", clientConfigFile)) {
|
|
806
896
|
await mkdir(dirname(clientPath), { recursive: true });
|
|
807
897
|
await writeFile(clientPath, clientTemplate);
|
|
808
|
-
|
|
809
|
-
consola.success(`Created ${relativePath}`);
|
|
898
|
+
consola.success(`Created ${relative(rootDir, clientPath)}`);
|
|
810
899
|
}
|
|
811
900
|
}
|
|
812
901
|
const module$1 = defineNuxtModule({
|
|
@@ -824,32 +913,25 @@ const module$1 = defineNuxtModule({
|
|
|
824
913
|
const configuredSecret = nuxt.options.runtimeConfig?.betterAuthSecret;
|
|
825
914
|
const generatedSecret = await promptForSecret(nuxt.options.rootDir, consola, { configuredSecret, prepare: Boolean(nuxt.options._prepare) });
|
|
826
915
|
if (generatedSecret)
|
|
827
|
-
process.env.
|
|
828
|
-
await createDefaultAuthConfigFiles(nuxt
|
|
916
|
+
process.env.NUXT_BETTER_AUTH_SECRET = generatedSecret;
|
|
917
|
+
await createDefaultAuthConfigFiles(nuxt);
|
|
829
918
|
},
|
|
830
919
|
async setup(options, nuxt) {
|
|
831
920
|
const resolver = createResolver(import.meta.url);
|
|
832
|
-
resolveDefaultClientConfig(options, nuxt.options.rootDir, nuxt.options.srcDir);
|
|
833
921
|
const clientOnly = options.clientOnly;
|
|
834
922
|
const serverConfigFile = options.serverConfig;
|
|
835
923
|
const clientConfigFile = options.clientConfig;
|
|
836
|
-
const serverConfigPath =
|
|
837
|
-
const clientConfigPath =
|
|
924
|
+
const { file: resolvedServerConfigFile, path: serverConfigPath } = resolveModuleConfigPath(nuxt, "server", serverConfigFile);
|
|
925
|
+
const { file: resolvedClientConfigFile, path: clientConfigPath } = resolveModuleConfigPath(nuxt, "client", clientConfigFile);
|
|
838
926
|
const serverConfigExists = existsSync(`${serverConfigPath}.ts`) || existsSync(`${serverConfigPath}.js`);
|
|
839
927
|
const clientConfigExists = existsSync(`${clientConfigPath}.ts`) || existsSync(`${clientConfigPath}.js`);
|
|
840
928
|
if (!clientOnly && !serverConfigExists)
|
|
841
|
-
throw new Error(`[nuxt-better-auth] Missing ${
|
|
929
|
+
throw new Error(`[nuxt-better-auth] Missing ${resolvedServerConfigFile}.ts - export default defineServerAuth(...)`);
|
|
842
930
|
if (!clientConfigExists)
|
|
843
|
-
throw new Error(`[nuxt-better-auth] Missing ${
|
|
931
|
+
throw new Error(`[nuxt-better-auth] Missing ${resolvedClientConfigFile}.ts - export default defineClientAuth(...)`);
|
|
844
932
|
const hasNuxtHub = hasNuxtModule("@nuxthub/core", nuxt);
|
|
845
933
|
const hub = hasNuxtHub ? nuxt.options.hub : void 0;
|
|
846
934
|
const hasHubDbAvailable = !clientOnly && hasNuxtHub && !!hub?.db;
|
|
847
|
-
const deprecatedProvider = options.database?.provider;
|
|
848
|
-
if (deprecatedProvider) {
|
|
849
|
-
throw new Error(
|
|
850
|
-
`[nuxt-better-auth] auth.database.provider has been removed. Remove auth.database.provider="${deprecatedProvider}". To configure a database, either set "database" directly in server/auth.config.ts (defineServerAuth) or install a provider module that registers better-auth:database:providers.`
|
|
851
|
-
);
|
|
852
|
-
}
|
|
853
935
|
let databaseProvider = "none";
|
|
854
936
|
let hasHubDb = false;
|
|
855
937
|
nuxt.options.alias["#nuxt-better-auth"] = resolver.resolve("./runtime/types/augment");
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import createAppAuthClient from "#auth/client";
|
|
2
|
-
import { computed, navigateTo, nextTick, useNuxtApp,
|
|
2
|
+
import { computed, navigateTo, nextTick, useNuxtApp, useRequestURL, useRuntimeConfig, useState, watch } from "#imports";
|
|
3
3
|
import { normalizeAuthActionError } from "../internal/auth-action-error.js";
|
|
4
|
+
import { resolvePostAuthSuccessRedirect, withFallbackSocialCallbackURL } from "../internal/redirect-helpers.js";
|
|
5
|
+
import { fetchSessionClient, fetchSessionServer, stripToken } from "../internal/session-fetch.js";
|
|
6
|
+
import { isRecord } from "../internal/utils.js";
|
|
7
|
+
import { wrapAuthMethod } from "../internal/wrap-auth-method.js";
|
|
4
8
|
let _sessionSignalListenerBound = false;
|
|
9
|
+
let _signOutPromise = null;
|
|
5
10
|
let _client = null;
|
|
6
|
-
function isRecord(value) {
|
|
7
|
-
return Boolean(value && typeof value === "object");
|
|
8
|
-
}
|
|
9
11
|
function getClient(baseURL) {
|
|
10
12
|
if (!_client)
|
|
11
13
|
_client = createAppAuthClient(baseURL);
|
|
@@ -88,6 +90,12 @@ export function useUserSession() {
|
|
|
88
90
|
session.value = null;
|
|
89
91
|
user.value = null;
|
|
90
92
|
}
|
|
93
|
+
async function fetchSession(options = {}) {
|
|
94
|
+
if (runtimeFlags.server)
|
|
95
|
+
return fetchSessionServer(session, user, authReady, options);
|
|
96
|
+
if (client)
|
|
97
|
+
return fetchSessionClient(client, session, user, authReady, options);
|
|
98
|
+
}
|
|
91
99
|
async function updateUser(updates) {
|
|
92
100
|
if (!user.value)
|
|
93
101
|
return;
|
|
@@ -122,8 +130,7 @@ export function useUserSession() {
|
|
|
122
130
|
if (shouldWaitForPrerenderResolution)
|
|
123
131
|
return;
|
|
124
132
|
if (newSession?.data?.session && newSession?.data?.user) {
|
|
125
|
-
|
|
126
|
-
session.value = safeSession;
|
|
133
|
+
session.value = stripToken(newSession.data.session);
|
|
127
134
|
user.value = newSession.data.user;
|
|
128
135
|
} else if (!newSession?.isPending && !newSession?.isRefetching) {
|
|
129
136
|
const isHydrationEmptySnapshot = nuxtApp.isHydrating && nuxtApp.payload.serverRendered && Boolean(session.value && user.value) && !newSession?.data?.session && !newSession?.data?.user;
|
|
@@ -163,107 +170,12 @@ export function useUserSession() {
|
|
|
163
170
|
}, 5e3);
|
|
164
171
|
});
|
|
165
172
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
function resolvePostAuthRedirect() {
|
|
174
|
-
const authConfig = runtimeConfig.public.auth;
|
|
175
|
-
const redirectQueryKey = authConfig?.redirectQueryKey ?? "redirect";
|
|
176
|
-
const queryRedirect = requestURL.searchParams?.get(redirectQueryKey);
|
|
177
|
-
const safeQueryRedirect = isSafeLocalRedirect(queryRedirect);
|
|
178
|
-
if (safeQueryRedirect)
|
|
179
|
-
return safeQueryRedirect;
|
|
180
|
-
return isSafeLocalRedirect(authConfig?.redirects?.authenticated);
|
|
181
|
-
}
|
|
182
|
-
function resolvePostAuthSuccessRedirect() {
|
|
183
|
-
const target = resolvePostAuthRedirect();
|
|
184
|
-
if (!target)
|
|
185
|
-
return;
|
|
186
|
-
return async () => {
|
|
187
|
-
await navigateTo(target);
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
function withFallbackSocialCallbackURL(data) {
|
|
191
|
-
const callbackURL = resolvePostAuthRedirect();
|
|
192
|
-
if (!callbackURL)
|
|
193
|
-
return data;
|
|
194
|
-
if (!isRecord(data))
|
|
195
|
-
return { callbackURL };
|
|
196
|
-
if (typeof data.callbackURL === "string")
|
|
197
|
-
return data;
|
|
198
|
-
return {
|
|
199
|
-
...data,
|
|
200
|
-
callbackURL
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
function wrapOnSuccess(cb) {
|
|
204
|
-
return async (ctx) => {
|
|
205
|
-
await fetchSession({ force: true });
|
|
206
|
-
if (!loggedIn.value)
|
|
207
|
-
await waitForSession();
|
|
208
|
-
await nextTick();
|
|
209
|
-
await cb(ctx);
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
function wrapAuthMethod(method, wrapOptions = {}) {
|
|
213
|
-
return (async (...args) => {
|
|
214
|
-
const originalData = args[0];
|
|
215
|
-
const options = args[1];
|
|
216
|
-
const data = wrapOptions.transformData?.(originalData, options) ?? originalData;
|
|
217
|
-
const dataRecord = isRecord(data) ? data : void 0;
|
|
218
|
-
const optionsRecord = isRecord(options) ? options : void 0;
|
|
219
|
-
if (wrapOptions.shouldSkipSessionSync?.(data, options))
|
|
220
|
-
return method(data, options);
|
|
221
|
-
const fetchOptions = isRecord(dataRecord?.fetchOptions) ? dataRecord.fetchOptions : void 0;
|
|
222
|
-
const nestedOnSuccess = fetchOptions?.onSuccess;
|
|
223
|
-
const topLevelOnSuccess = optionsRecord?.onSuccess;
|
|
224
|
-
const fallbackOnSuccess = resolvePostAuthSuccessRedirect();
|
|
225
|
-
const wrappedFallbackOnSuccess = fallbackOnSuccess && wrapOnSuccess(async () => {
|
|
226
|
-
if (!loggedIn.value)
|
|
227
|
-
return;
|
|
228
|
-
await fallbackOnSuccess();
|
|
229
|
-
});
|
|
230
|
-
if (typeof nestedOnSuccess === "function") {
|
|
231
|
-
const nextData = {
|
|
232
|
-
...dataRecord,
|
|
233
|
-
fetchOptions: {
|
|
234
|
-
...fetchOptions,
|
|
235
|
-
onSuccess: wrapOnSuccess(nestedOnSuccess)
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
return method(nextData, options);
|
|
239
|
-
}
|
|
240
|
-
if (typeof topLevelOnSuccess === "function") {
|
|
241
|
-
const nextOptions = {
|
|
242
|
-
...optionsRecord,
|
|
243
|
-
onSuccess: wrapOnSuccess(topLevelOnSuccess)
|
|
244
|
-
};
|
|
245
|
-
return method(data, nextOptions);
|
|
246
|
-
}
|
|
247
|
-
if (wrappedFallbackOnSuccess) {
|
|
248
|
-
if (fetchOptions) {
|
|
249
|
-
const nextData = {
|
|
250
|
-
...dataRecord,
|
|
251
|
-
fetchOptions: {
|
|
252
|
-
...fetchOptions,
|
|
253
|
-
onSuccess: wrappedFallbackOnSuccess
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
return method(nextData, options);
|
|
257
|
-
}
|
|
258
|
-
const nextOptions = {
|
|
259
|
-
...optionsRecord,
|
|
260
|
-
onSuccess: wrappedFallbackOnSuccess
|
|
261
|
-
};
|
|
262
|
-
return method(data, nextOptions);
|
|
263
|
-
}
|
|
264
|
-
return method(data, options);
|
|
265
|
-
});
|
|
266
|
-
}
|
|
173
|
+
const wrapDeps = {
|
|
174
|
+
fetchSession,
|
|
175
|
+
loggedIn,
|
|
176
|
+
waitForSession,
|
|
177
|
+
resolvePostAuthSuccessRedirect: () => resolvePostAuthSuccessRedirect(requestURL)
|
|
178
|
+
};
|
|
267
179
|
const signIn = client?.signIn ? new Proxy(client.signIn, {
|
|
268
180
|
get(target, prop) {
|
|
269
181
|
const targetRecord = target;
|
|
@@ -274,9 +186,10 @@ export function useUserSession() {
|
|
|
274
186
|
const socialData = isRecord(data) ? data : void 0;
|
|
275
187
|
return socialData?.disableRedirect !== true;
|
|
276
188
|
} : void 0;
|
|
277
|
-
const transformData = prop === "social" ? withFallbackSocialCallbackURL : void 0;
|
|
189
|
+
const transformData = prop === "social" ? (data) => withFallbackSocialCallbackURL(data, requestURL) : void 0;
|
|
278
190
|
return wrapAuthMethod(
|
|
279
191
|
(...args) => targetRecord[prop](...args),
|
|
192
|
+
wrapDeps,
|
|
280
193
|
{ shouldSkipSessionSync, transformData }
|
|
281
194
|
);
|
|
282
195
|
}
|
|
@@ -291,73 +204,40 @@ export function useUserSession() {
|
|
|
291
204
|
const method = targetRecord[prop];
|
|
292
205
|
if (typeof method !== "function")
|
|
293
206
|
return method;
|
|
294
|
-
return wrapAuthMethod((...args) => targetRecord[prop](...args));
|
|
207
|
+
return wrapAuthMethod((...args) => targetRecord[prop](...args), wrapDeps);
|
|
295
208
|
}
|
|
296
209
|
}) : new Proxy({}, {
|
|
297
210
|
get: (_, prop) => {
|
|
298
211
|
throw new Error(`signUp.${String(prop)}() can only be called on client-side`);
|
|
299
212
|
}
|
|
300
213
|
});
|
|
301
|
-
async function fetchSession(options = {}) {
|
|
302
|
-
if (runtimeFlags.server) {
|
|
303
|
-
try {
|
|
304
|
-
const headers = options.headers || useRequestHeaders(["cookie"]);
|
|
305
|
-
const requestFetch = useRequestFetch();
|
|
306
|
-
const data = await requestFetch("/api/auth/get-session", { headers });
|
|
307
|
-
if (data?.session && data?.user) {
|
|
308
|
-
const { token: _, ...safeSession } = data.session;
|
|
309
|
-
session.value = safeSession;
|
|
310
|
-
user.value = data.user;
|
|
311
|
-
} else {
|
|
312
|
-
clearSession();
|
|
313
|
-
}
|
|
314
|
-
} catch {
|
|
315
|
-
clearSession();
|
|
316
|
-
} finally {
|
|
317
|
-
if (!authReady.value)
|
|
318
|
-
authReady.value = true;
|
|
319
|
-
}
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
if (client) {
|
|
323
|
-
try {
|
|
324
|
-
const headers = options.headers || useRequestHeaders(["cookie"]);
|
|
325
|
-
const fetchOptions = headers ? { headers } : void 0;
|
|
326
|
-
const query = options.force ? { disableCookieCache: true } : void 0;
|
|
327
|
-
const result = await client.getSession({ query }, fetchOptions);
|
|
328
|
-
const data = result.data;
|
|
329
|
-
if (data?.session && data?.user) {
|
|
330
|
-
const { token: _, ...safeSession } = data.session;
|
|
331
|
-
session.value = safeSession;
|
|
332
|
-
user.value = data.user;
|
|
333
|
-
} else {
|
|
334
|
-
clearSession();
|
|
335
|
-
}
|
|
336
|
-
} catch (error) {
|
|
337
|
-
clearSession();
|
|
338
|
-
console.error("[nuxt-better-auth] Failed to fetch session:", error);
|
|
339
|
-
} finally {
|
|
340
|
-
if (!authReady.value)
|
|
341
|
-
authReady.value = true;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
214
|
if (runtimeFlags.client && client && shouldSkipInitialClientSessionFetch.value) {
|
|
346
215
|
ensureSessionSignalListener(client, () => fetchSession({ force: true }));
|
|
347
216
|
}
|
|
348
217
|
async function signOut(options) {
|
|
349
218
|
if (!client)
|
|
350
219
|
throw new Error("signOut can only be called on client-side");
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
if (options?.onSuccess) {
|
|
354
|
-
await options.onSuccess();
|
|
220
|
+
if (_signOutPromise) {
|
|
221
|
+
await _signOutPromise;
|
|
355
222
|
return;
|
|
356
223
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
224
|
+
_signOutPromise = (async () => {
|
|
225
|
+
await client.signOut();
|
|
226
|
+
clearSession();
|
|
227
|
+
if (options?.onSuccess) {
|
|
228
|
+
await options.onSuccess();
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const authConfig = runtimeConfig.public.auth;
|
|
232
|
+
const logoutRedirect = authConfig?.redirects?.logout;
|
|
233
|
+
if (logoutRedirect) {
|
|
234
|
+
await nextTick();
|
|
235
|
+
await navigateTo(logoutRedirect);
|
|
236
|
+
}
|
|
237
|
+
})().finally(() => {
|
|
238
|
+
_signOutPromise = null;
|
|
239
|
+
});
|
|
240
|
+
await _signOutPromise;
|
|
361
241
|
}
|
|
362
242
|
return {
|
|
363
243
|
client,
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
+
import { isRecord } from "./utils.js";
|
|
1
2
|
export const DEFAULT_AUTH_ACTION_ERROR_MESSAGE = "Request failed. Please try again.";
|
|
2
|
-
function isRecord(value) {
|
|
3
|
-
return Boolean(value && typeof value === "object");
|
|
4
|
-
}
|
|
5
3
|
function getMessage(value) {
|
|
6
4
|
if (value instanceof Error)
|
|
7
5
|
return value.message;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { ref } from "#imports";
|
|
2
2
|
import { normalizeAuthActionError } from "./auth-action-error.js";
|
|
3
|
-
|
|
4
|
-
return Boolean(value && typeof value === "object");
|
|
5
|
-
}
|
|
3
|
+
import { isRecord } from "./utils.js";
|
|
6
4
|
function isErrorResult(value) {
|
|
7
5
|
if (!isRecord(value))
|
|
8
6
|
return false;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function isSafeLocalRedirect(redirect: unknown): string | undefined;
|
|
2
|
+
export declare function resolvePostAuthRedirect(requestURL: URL): string | undefined;
|
|
3
|
+
export declare function resolvePostAuthSuccessRedirect(requestURL: URL): (() => Promise<void>) | undefined;
|
|
4
|
+
export declare function withFallbackSocialCallbackURL(data: unknown, requestURL: URL): unknown;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { navigateTo, useRuntimeConfig } from "#imports";
|
|
2
|
+
import { isRecord } from "./utils.js";
|
|
3
|
+
export function isSafeLocalRedirect(redirect) {
|
|
4
|
+
if (typeof redirect !== "string")
|
|
5
|
+
return;
|
|
6
|
+
if (!redirect.startsWith("/") || redirect.startsWith("//"))
|
|
7
|
+
return;
|
|
8
|
+
return redirect;
|
|
9
|
+
}
|
|
10
|
+
export function resolvePostAuthRedirect(requestURL) {
|
|
11
|
+
const runtimeConfig = useRuntimeConfig();
|
|
12
|
+
const authConfig = runtimeConfig.public.auth;
|
|
13
|
+
const redirectQueryKey = authConfig?.redirectQueryKey ?? "redirect";
|
|
14
|
+
const queryRedirect = requestURL.searchParams?.get(redirectQueryKey);
|
|
15
|
+
const safeQueryRedirect = isSafeLocalRedirect(queryRedirect);
|
|
16
|
+
if (safeQueryRedirect)
|
|
17
|
+
return safeQueryRedirect;
|
|
18
|
+
return isSafeLocalRedirect(authConfig?.redirects?.authenticated);
|
|
19
|
+
}
|
|
20
|
+
export function resolvePostAuthSuccessRedirect(requestURL) {
|
|
21
|
+
const target = resolvePostAuthRedirect(requestURL);
|
|
22
|
+
if (!target)
|
|
23
|
+
return;
|
|
24
|
+
return async () => {
|
|
25
|
+
await navigateTo(target);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function withFallbackSocialCallbackURL(data, requestURL) {
|
|
29
|
+
const callbackURL = resolvePostAuthRedirect(requestURL);
|
|
30
|
+
if (!callbackURL)
|
|
31
|
+
return data;
|
|
32
|
+
if (!isRecord(data))
|
|
33
|
+
return { callbackURL };
|
|
34
|
+
if (typeof data.callbackURL === "string")
|
|
35
|
+
return data;
|
|
36
|
+
return { ...data, callbackURL };
|
|
37
|
+
}
|
|
@@ -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,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
|
+
}
|
package/dist/runtime/config.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BetterAuthOptions, BetterAuthPlugin } from 'better-auth';
|
|
2
2
|
import type { BetterAuthClientOptions } from 'better-auth/client';
|
|
3
|
-
import type {
|
|
3
|
+
import type { Casing } from 'drizzle-orm/utils';
|
|
4
4
|
import type { ServerAuthContext } from './types/augment.js';
|
|
5
5
|
import { createAuthClient } from 'better-auth/vue';
|
|
6
6
|
export type { ServerAuthContext };
|
|
@@ -20,9 +20,9 @@ export type EffectiveDatabaseProviderId = 'user' | ModuleDatabaseProviderId;
|
|
|
20
20
|
export interface BetterAuthModuleOptions {
|
|
21
21
|
/** Client-only mode - skip server setup for external auth backends */
|
|
22
22
|
clientOnly?: boolean;
|
|
23
|
-
/** Server config path
|
|
23
|
+
/** Server config path. Relative paths resolve from the layer that declares them. Default: 'server/auth.config' */
|
|
24
24
|
serverConfig?: string;
|
|
25
|
-
/** Client config path
|
|
25
|
+
/** Client config path. Relative paths resolve from the layer that declares them. Default: 'app/auth.config' */
|
|
26
26
|
clientConfig?: string;
|
|
27
27
|
redirects?: {
|
|
28
28
|
/** Where to redirect unauthenticated users. Default: '/login' */
|
|
@@ -69,7 +69,7 @@ export interface BetterAuthModuleOptions {
|
|
|
69
69
|
/** Plural table names: user → users. Default: false */
|
|
70
70
|
usePlural?: boolean;
|
|
71
71
|
/** Column/table name casing. Explicit value takes precedence over hub.db.casing. */
|
|
72
|
-
casing?:
|
|
72
|
+
casing?: Casing;
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
75
|
export interface AuthRuntimeConfig {
|
package/dist/runtime/config.js
CHANGED
|
@@ -6,6 +6,8 @@ export function defineClientAuth(config) {
|
|
|
6
6
|
return (baseURL) => {
|
|
7
7
|
const ctx = { siteUrl: baseURL };
|
|
8
8
|
const resolved = typeof config === "function" ? config(ctx) : config;
|
|
9
|
-
|
|
9
|
+
const { baseURL: configuredBaseURL, ...resolvedOptions } = resolved;
|
|
10
|
+
const clientOptions = { ...resolvedOptions, baseURL: configuredBaseURL ?? baseURL };
|
|
11
|
+
return createAuthClient(clientOptions);
|
|
10
12
|
};
|
|
11
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(
|
|
11
|
+
return `%${search.replace(SQL_LIKE_ESCAPE_RE, "\\$&")}%`;
|
|
11
12
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onmax/nuxt-better-auth",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.2-alpha.
|
|
4
|
+
"version": "0.0.2-alpha.31",
|
|
5
5
|
"packageManager": "pnpm@10.15.1",
|
|
6
6
|
"description": "Nuxt module for Better Auth integration with NuxtHub, route protection, session management, and role-based access",
|
|
7
7
|
"author": "onmax",
|
|
@@ -11,14 +11,6 @@
|
|
|
11
11
|
"type": "git",
|
|
12
12
|
"url": "https://github.com/nuxt-modules/better-auth"
|
|
13
13
|
},
|
|
14
|
-
"agents": {
|
|
15
|
-
"skills": [
|
|
16
|
-
{
|
|
17
|
-
"name": "nuxt-better-auth",
|
|
18
|
-
"path": "./skills/nuxt-better-auth"
|
|
19
|
-
}
|
|
20
|
-
]
|
|
21
|
-
},
|
|
22
14
|
"exports": {
|
|
23
15
|
".": {
|
|
24
16
|
"types": "./dist/types.d.mts",
|
|
@@ -41,8 +33,7 @@
|
|
|
41
33
|
}
|
|
42
34
|
},
|
|
43
35
|
"files": [
|
|
44
|
-
"dist"
|
|
45
|
-
"skills"
|
|
36
|
+
"dist"
|
|
46
37
|
],
|
|
47
38
|
"scripts": {
|
|
48
39
|
"prepack": "nuxt-module-build build",
|
|
@@ -77,13 +68,13 @@
|
|
|
77
68
|
"jiti": "^2.6.1",
|
|
78
69
|
"pathe": "^2.0.3",
|
|
79
70
|
"radix3": "^1.1.2",
|
|
80
|
-
"std-env": "^
|
|
71
|
+
"std-env": "^4.0.0"
|
|
81
72
|
},
|
|
82
73
|
"devDependencies": {
|
|
83
74
|
"@antfu/eslint-config": "^7.6.1",
|
|
84
75
|
"@libsql/client": "^0.17.0",
|
|
85
76
|
"@libsql/linux-x64-gnu": "0.5.22",
|
|
86
|
-
"@nuxt/devtools": "^3.2.
|
|
77
|
+
"@nuxt/devtools": "^3.2.3",
|
|
87
78
|
"@nuxt/devtools-kit": "^3.2.2",
|
|
88
79
|
"@nuxt/module-builder": "^1.0.2",
|
|
89
80
|
"@nuxt/schema": "^4.3.1",
|
|
@@ -91,14 +82,14 @@
|
|
|
91
82
|
"@nuxthub/core": "^0.10.6",
|
|
92
83
|
"@types/better-sqlite3": "^7.6.13",
|
|
93
84
|
"@types/node": "latest",
|
|
94
|
-
"better-auth": "1.5.
|
|
85
|
+
"better-auth": "1.5.5",
|
|
95
86
|
"better-sqlite3": "^12.6.2",
|
|
96
|
-
"bumpp": "^
|
|
87
|
+
"bumpp": "^11.0.0",
|
|
97
88
|
"changelogen": "^0.6.2",
|
|
98
89
|
"consola": "^3.4.2",
|
|
99
90
|
"drizzle-kit": "^0.31.9",
|
|
100
91
|
"drizzle-orm": "^0.45.1",
|
|
101
|
-
"eslint": "^10.0.
|
|
92
|
+
"eslint": "^10.0.3",
|
|
102
93
|
"npm-agentskills": "https://pkg.pr.new/onmax/npm-agentskills@394499e",
|
|
103
94
|
"nuxt": "^4.3.1",
|
|
104
95
|
"tinyexec": "^1.0.2",
|