@onmax/nuxt-better-auth 0.0.2-alpha.9 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -17
- package/dist/module.d.mts +28 -1
- package/dist/module.json +3 -3
- package/dist/module.mjs +1258 -351
- package/dist/runtime/app/components/BetterAuthState.d.vue.ts +4 -4
- package/dist/runtime/app/components/BetterAuthState.vue +1 -0
- package/dist/runtime/app/components/BetterAuthState.vue.d.ts +4 -4
- package/dist/runtime/app/composables/runWithSessionRefresh.d.ts +1 -0
- package/dist/runtime/app/composables/runWithSessionRefresh.js +12 -0
- package/dist/runtime/app/composables/useAction.d.ts +2 -0
- package/dist/runtime/app/composables/useAction.js +6 -0
- package/dist/runtime/app/composables/useAuthAsyncData.d.ts +6 -0
- package/dist/runtime/app/composables/useAuthAsyncData.js +16 -0
- package/dist/runtime/app/composables/useAuthClient.d.ts +9 -0
- package/dist/runtime/app/composables/useAuthClient.js +34 -0
- package/dist/runtime/app/composables/useAuthClientAction.d.ts +3 -0
- package/dist/runtime/app/composables/useAuthClientAction.js +15 -0
- package/dist/runtime/app/composables/useAuthRequestFetch.d.ts +11 -0
- package/dist/runtime/app/composables/useAuthRequestFetch.js +4 -0
- package/dist/runtime/app/composables/useSignIn.d.ts +16 -0
- package/dist/runtime/app/composables/useSignIn.js +8 -0
- package/dist/runtime/app/composables/useSignUp.d.ts +5 -0
- package/dist/runtime/app/composables/useSignUp.js +8 -0
- package/dist/runtime/app/composables/useUserSession.d.ts +6 -5
- package/dist/runtime/app/composables/useUserSession.js +189 -100
- package/dist/runtime/app/composables/useUserSessionState.d.ts +3 -0
- package/dist/runtime/app/composables/useUserSessionState.js +4 -0
- package/dist/runtime/app/internal/auth-action-error.d.ts +3 -0
- package/dist/runtime/app/internal/auth-action-error.js +33 -0
- package/dist/runtime/app/internal/auth-action-handles.d.ts +21 -0
- package/dist/runtime/app/internal/auth-action-handles.js +105 -0
- 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/vue-safe-auth-proxy.d.ts +3 -0
- package/dist/runtime/app/internal/vue-safe-auth-proxy.js +68 -0
- package/dist/runtime/app/internal/wrap-auth-method.d.ts +18 -0
- package/dist/runtime/app/internal/wrap-auth-method.js +69 -0
- package/dist/runtime/app/middleware/auth.global.js +80 -15
- package/dist/runtime/app/pages/__better-auth-devtools.vue +293 -339
- package/dist/runtime/composables.d.ts +12 -0
- package/dist/runtime/composables.js +10 -0
- package/dist/runtime/config.d.ts +69 -15
- package/dist/runtime/config.js +9 -2
- package/dist/runtime/internal/auth-route-rules.d.ts +1 -0
- package/dist/runtime/internal/auth-route-rules.js +24 -0
- package/dist/runtime/server/api/_better-auth/_schema.d.ts +1 -5
- package/dist/runtime/server/api/_better-auth/_schema.js +2 -1
- package/dist/runtime/server/api/_better-auth/accounts.get.d.ts +2 -2
- package/dist/runtime/server/api/_better-auth/accounts.get.js +3 -2
- package/dist/runtime/server/api/_better-auth/config.get.d.ts +9 -3
- package/dist/runtime/server/api/_better-auth/config.get.js +18 -6
- package/dist/runtime/server/api/_better-auth/sessions.delete.js +3 -2
- package/dist/runtime/server/api/_better-auth/sessions.get.d.ts +5 -3
- package/dist/runtime/server/api/_better-auth/sessions.get.js +3 -2
- package/dist/runtime/server/api/_better-auth/users.get.d.ts +2 -2
- package/dist/runtime/server/api/_better-auth/users.get.js +3 -2
- package/dist/runtime/server/api/auth/[...all].js +1 -1
- package/dist/runtime/server/middleware/route-access.js +4 -2
- package/dist/runtime/server/tsconfig.json +9 -1
- package/dist/runtime/server/utils/auth.d.ts +16 -8
- package/dist/runtime/server/utils/auth.js +229 -23
- package/dist/runtime/server/utils/custom-secondary-storage.d.ts +6 -0
- package/dist/runtime/server/utils/custom-secondary-storage.js +8 -0
- package/dist/runtime/server/utils/session.d.ts +7 -8
- package/dist/runtime/server/utils/session.js +256 -5
- package/dist/runtime/server/virtual-modules.d.ts +27 -0
- package/dist/runtime/types/augment.d.ts +18 -4
- package/dist/runtime/types.d.ts +12 -13
- package/dist/types.d.mts +1 -1
- package/package.json +53 -47
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { ref } from "#imports";
|
|
2
|
+
import { normalizeAuthActionError } from "./auth-action-error.js";
|
|
3
|
+
import { isRecord } from "./utils.js";
|
|
4
|
+
export function isAuthActionErrorResult(value) {
|
|
5
|
+
if (!isRecord(value))
|
|
6
|
+
return false;
|
|
7
|
+
if (!("error" in value))
|
|
8
|
+
return false;
|
|
9
|
+
return Boolean(value.error);
|
|
10
|
+
}
|
|
11
|
+
function isRedirectResult(value) {
|
|
12
|
+
if (!isRecord(value))
|
|
13
|
+
return false;
|
|
14
|
+
return value.redirect === true && typeof value.url === "string" && value.url.length > 0;
|
|
15
|
+
}
|
|
16
|
+
function getRedirectResult(value) {
|
|
17
|
+
if (isRedirectResult(value))
|
|
18
|
+
return value;
|
|
19
|
+
if (!isRecord(value))
|
|
20
|
+
return null;
|
|
21
|
+
const nested = value.data;
|
|
22
|
+
if (isRedirectResult(nested))
|
|
23
|
+
return nested;
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const REDIRECT_PENDING_FALLBACK_MS = 1e4;
|
|
27
|
+
export function createActionHandle(getMethod, options = {}) {
|
|
28
|
+
const status = ref("idle");
|
|
29
|
+
const data = ref(null);
|
|
30
|
+
const error = ref(null);
|
|
31
|
+
let latestCallId = 0;
|
|
32
|
+
const run = async (...args) => {
|
|
33
|
+
const callId = ++latestCallId;
|
|
34
|
+
status.value = "pending";
|
|
35
|
+
data.value = null;
|
|
36
|
+
error.value = null;
|
|
37
|
+
try {
|
|
38
|
+
const result = await getMethod()(...args);
|
|
39
|
+
if (isAuthActionErrorResult(result)) {
|
|
40
|
+
const normalizedError = normalizeAuthActionError(result.error);
|
|
41
|
+
if (callId === latestCallId) {
|
|
42
|
+
status.value = "error";
|
|
43
|
+
data.value = null;
|
|
44
|
+
error.value = normalizedError;
|
|
45
|
+
}
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (callId === latestCallId) {
|
|
49
|
+
if (options.keepPendingOnRedirect !== false) {
|
|
50
|
+
const redirectResult = getRedirectResult(result);
|
|
51
|
+
if (redirectResult) {
|
|
52
|
+
status.value = "pending";
|
|
53
|
+
data.value = result;
|
|
54
|
+
error.value = null;
|
|
55
|
+
setTimeout(() => {
|
|
56
|
+
if (callId !== latestCallId || status.value !== "pending")
|
|
57
|
+
return;
|
|
58
|
+
status.value = "success";
|
|
59
|
+
}, REDIRECT_PENDING_FALLBACK_MS);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
status.value = "success";
|
|
64
|
+
data.value = result;
|
|
65
|
+
error.value = null;
|
|
66
|
+
}
|
|
67
|
+
} catch (thrown) {
|
|
68
|
+
const normalizedError = normalizeAuthActionError(thrown);
|
|
69
|
+
if (callId === latestCallId) {
|
|
70
|
+
status.value = "error";
|
|
71
|
+
data.value = null;
|
|
72
|
+
error.value = normalizedError;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const execute = (async (...args) => {
|
|
77
|
+
await run(...args);
|
|
78
|
+
});
|
|
79
|
+
return {
|
|
80
|
+
execute,
|
|
81
|
+
status,
|
|
82
|
+
data,
|
|
83
|
+
error
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
export function createActionHandles(getTarget, targetName) {
|
|
87
|
+
const handles = /* @__PURE__ */ new Map();
|
|
88
|
+
return new Proxy({}, {
|
|
89
|
+
get(_target, prop) {
|
|
90
|
+
if (prop === "then")
|
|
91
|
+
return void 0;
|
|
92
|
+
if (handles.has(prop))
|
|
93
|
+
return handles.get(prop);
|
|
94
|
+
const handle = createActionHandle(() => {
|
|
95
|
+
const target = getTarget();
|
|
96
|
+
const method = target[prop];
|
|
97
|
+
if (typeof method !== "function")
|
|
98
|
+
throw new TypeError(`${targetName}.${String(prop)}() is not a function`);
|
|
99
|
+
return method;
|
|
100
|
+
});
|
|
101
|
+
handles.set(prop, handle);
|
|
102
|
+
return handle;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
@@ -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 { Ref } from 'vue';
|
|
2
|
+
import type { AppAuthClient, AuthSession, AuthUser } from '#nuxt-better-auth';
|
|
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,68 @@
|
|
|
1
|
+
const promiseProbeKeys = /* @__PURE__ */ new Set(["then", "catch", "finally"]);
|
|
2
|
+
export function isAuthProxyProbeKey(prop) {
|
|
3
|
+
return typeof prop !== "string" || promiseProbeKeys.has(prop) || prop.startsWith("__v");
|
|
4
|
+
}
|
|
5
|
+
function isObjectLike(value) {
|
|
6
|
+
return typeof value === "object" && value !== null || typeof value === "function";
|
|
7
|
+
}
|
|
8
|
+
export function createVueSafeAuthFacade(resolve) {
|
|
9
|
+
const propertyCache = /* @__PURE__ */ new Map();
|
|
10
|
+
const facadeTarget = {};
|
|
11
|
+
Object.defineProperty(facadeTarget, "__v_skip", {
|
|
12
|
+
value: true,
|
|
13
|
+
configurable: true
|
|
14
|
+
});
|
|
15
|
+
return new Proxy(facadeTarget, {
|
|
16
|
+
get(_target, prop, receiver) {
|
|
17
|
+
if (prop === "__v_skip")
|
|
18
|
+
return true;
|
|
19
|
+
if (isAuthProxyProbeKey(prop))
|
|
20
|
+
return void 0;
|
|
21
|
+
if (propertyCache.has(prop))
|
|
22
|
+
return propertyCache.get(prop);
|
|
23
|
+
const value = resolve(prop, receiver);
|
|
24
|
+
propertyCache.set(prop, value);
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export function createVueSafeAuthProxy(target) {
|
|
30
|
+
if (!isObjectLike(target))
|
|
31
|
+
return target;
|
|
32
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
33
|
+
const wrap = (value) => {
|
|
34
|
+
if (!isObjectLike(value))
|
|
35
|
+
return value;
|
|
36
|
+
const cached = cache.get(value);
|
|
37
|
+
if (cached)
|
|
38
|
+
return cached;
|
|
39
|
+
const propertyCache = /* @__PURE__ */ new Map();
|
|
40
|
+
const handler = {
|
|
41
|
+
get(target2, prop, receiver) {
|
|
42
|
+
if (prop === "__v_skip")
|
|
43
|
+
return true;
|
|
44
|
+
if (isAuthProxyProbeKey(prop))
|
|
45
|
+
return void 0;
|
|
46
|
+
if (propertyCache.has(prop))
|
|
47
|
+
return propertyCache.get(prop);
|
|
48
|
+
const wrapped = wrap(Reflect.get(target2, prop, receiver));
|
|
49
|
+
propertyCache.set(prop, wrapped);
|
|
50
|
+
return wrapped;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
if (typeof value === "function") {
|
|
54
|
+
handler.apply = (target2, thisArg, args) => Reflect.apply(target2, thisArg, args);
|
|
55
|
+
}
|
|
56
|
+
const proxy = new Proxy(value, handler);
|
|
57
|
+
cache.set(value, proxy);
|
|
58
|
+
return proxy;
|
|
59
|
+
};
|
|
60
|
+
const createRootFacade = (value) => {
|
|
61
|
+
if (!isObjectLike(value))
|
|
62
|
+
return value;
|
|
63
|
+
const proxy = createVueSafeAuthFacade((prop, receiver) => wrap(Reflect.get(value, prop, receiver)));
|
|
64
|
+
cache.set(value, proxy);
|
|
65
|
+
return proxy;
|
|
66
|
+
};
|
|
67
|
+
return createRootFacade(target);
|
|
68
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ComputedRef } from 'vue';
|
|
2
|
+
export declare function refreshSessionAfterAuthAction(fetchSession: (options?: {
|
|
3
|
+
force?: boolean;
|
|
4
|
+
}) => Promise<void>, loggedIn: ComputedRef<boolean>, waitForSession: () => Promise<void>): Promise<void>;
|
|
5
|
+
export declare function wrapOnSuccess(fetchSession: (options?: {
|
|
6
|
+
force?: boolean;
|
|
7
|
+
}) => Promise<void>, loggedIn: ComputedRef<boolean>, waitForSession: () => Promise<void>, cb: (ctx: unknown) => void | Promise<void>): (ctx: unknown) => Promise<void>;
|
|
8
|
+
export declare function wrapAuthMethod<T extends (...args: unknown[]) => Promise<unknown>>(method: T, deps: {
|
|
9
|
+
fetchSession: (options?: {
|
|
10
|
+
force?: boolean;
|
|
11
|
+
}) => Promise<void>;
|
|
12
|
+
loggedIn: ComputedRef<boolean>;
|
|
13
|
+
waitForSession: () => Promise<void>;
|
|
14
|
+
resolvePostAuthSuccessRedirect: () => (() => Promise<void>) | undefined;
|
|
15
|
+
}, wrapOptions?: {
|
|
16
|
+
shouldSkipSessionSync?: (data: unknown, options: unknown) => boolean;
|
|
17
|
+
transformData?: (data: unknown, options: unknown) => unknown;
|
|
18
|
+
}): T;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { nextTick } from "#imports";
|
|
2
|
+
import { isRecord } from "./utils.js";
|
|
3
|
+
export async function refreshSessionAfterAuthAction(fetchSession, loggedIn, waitForSession) {
|
|
4
|
+
await fetchSession({ force: true });
|
|
5
|
+
if (!loggedIn.value)
|
|
6
|
+
await waitForSession();
|
|
7
|
+
await nextTick();
|
|
8
|
+
}
|
|
9
|
+
export function wrapOnSuccess(fetchSession, loggedIn, waitForSession, cb) {
|
|
10
|
+
return async (ctx) => {
|
|
11
|
+
await refreshSessionAfterAuthAction(fetchSession, loggedIn, waitForSession);
|
|
12
|
+
await cb(ctx);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function wrapAuthMethod(method, deps, wrapOptions = {}) {
|
|
16
|
+
return (async (...args) => {
|
|
17
|
+
const originalData = args[0];
|
|
18
|
+
const options = args[1];
|
|
19
|
+
const data = wrapOptions.transformData?.(originalData, options) ?? originalData;
|
|
20
|
+
const dataRecord = isRecord(data) ? data : void 0;
|
|
21
|
+
const optionsRecord = isRecord(options) ? options : void 0;
|
|
22
|
+
if (wrapOptions.shouldSkipSessionSync?.(data, options))
|
|
23
|
+
return method(data, options);
|
|
24
|
+
const fetchOptions = isRecord(dataRecord?.fetchOptions) ? dataRecord.fetchOptions : void 0;
|
|
25
|
+
const nestedOnSuccess = fetchOptions?.onSuccess;
|
|
26
|
+
const topLevelOnSuccess = optionsRecord?.onSuccess;
|
|
27
|
+
const fallbackOnSuccess = deps.resolvePostAuthSuccessRedirect();
|
|
28
|
+
const wrappedFallbackOnSuccess = fallbackOnSuccess && wrapOnSuccess(deps.fetchSession, deps.loggedIn, deps.waitForSession, async () => {
|
|
29
|
+
if (!deps.loggedIn.value)
|
|
30
|
+
return;
|
|
31
|
+
await fallbackOnSuccess();
|
|
32
|
+
});
|
|
33
|
+
if (typeof nestedOnSuccess === "function") {
|
|
34
|
+
const nextData = {
|
|
35
|
+
...dataRecord,
|
|
36
|
+
fetchOptions: {
|
|
37
|
+
...fetchOptions,
|
|
38
|
+
onSuccess: wrapOnSuccess(deps.fetchSession, deps.loggedIn, deps.waitForSession, nestedOnSuccess)
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
return method(nextData, options);
|
|
42
|
+
}
|
|
43
|
+
if (typeof topLevelOnSuccess === "function") {
|
|
44
|
+
const nextOptions = {
|
|
45
|
+
...optionsRecord,
|
|
46
|
+
onSuccess: wrapOnSuccess(deps.fetchSession, deps.loggedIn, deps.waitForSession, topLevelOnSuccess)
|
|
47
|
+
};
|
|
48
|
+
return method(data, nextOptions);
|
|
49
|
+
}
|
|
50
|
+
if (wrappedFallbackOnSuccess) {
|
|
51
|
+
if (fetchOptions) {
|
|
52
|
+
const nextData = {
|
|
53
|
+
...dataRecord,
|
|
54
|
+
fetchOptions: {
|
|
55
|
+
...fetchOptions,
|
|
56
|
+
onSuccess: wrappedFallbackOnSuccess
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
return method(nextData, options);
|
|
60
|
+
}
|
|
61
|
+
const nextOptions = {
|
|
62
|
+
...optionsRecord,
|
|
63
|
+
onSuccess: wrappedFallbackOnSuccess
|
|
64
|
+
};
|
|
65
|
+
return method(data, nextOptions);
|
|
66
|
+
}
|
|
67
|
+
return method(data, options);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
@@ -1,37 +1,102 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defu } from "defu";
|
|
2
|
+
import { createRouter, toRouteMatcher } from "radix3";
|
|
3
|
+
import { createError, defineNuxtRouteMiddleware, getRouteRules, navigateTo, useNuxtApp, useRequestHeaders, useRuntimeConfig } from "#imports";
|
|
4
|
+
import { shouldSkipAuthRouteRules } from "../../internal/auth-route-rules.js";
|
|
2
5
|
import { matchesUser } from "../../utils/match-user.js";
|
|
6
|
+
import { useUserSession } from "../composables/useUserSession.js";
|
|
7
|
+
let authRouteRulesPromise = null;
|
|
8
|
+
let routeRulesMatcherPromise = null;
|
|
3
9
|
export default defineNuxtRouteMiddleware(async (to) => {
|
|
10
|
+
if (shouldSkipAuthRouteRules(to.path))
|
|
11
|
+
return;
|
|
4
12
|
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
13
|
if (to.meta.auth === void 0) {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
const routeRulesMatcher = await getRouteRulesMatcher();
|
|
15
|
+
const matches = routeRulesMatcher?.matchAll(to.path);
|
|
16
|
+
if (matches?.length) {
|
|
17
|
+
const merged = defu({}, ...matches.reverse());
|
|
18
|
+
if (merged.auth !== void 0)
|
|
19
|
+
to.meta.auth = merged.auth;
|
|
20
|
+
}
|
|
21
|
+
if (to.meta.auth === void 0) {
|
|
22
|
+
const rules = await getRouteRules({ path: to.path });
|
|
23
|
+
if (rules.auth !== void 0)
|
|
24
|
+
to.meta.auth = rules.auth;
|
|
25
|
+
}
|
|
14
26
|
}
|
|
15
27
|
const auth = to.meta.auth;
|
|
16
28
|
if (auth === void 0 || auth === false)
|
|
17
29
|
return;
|
|
18
30
|
const config = useRuntimeConfig().public.auth;
|
|
19
31
|
const { fetchSession, user, loggedIn } = useUserSession();
|
|
20
|
-
if (!loggedIn.value) {
|
|
21
|
-
const headers = import.meta.server ? useRequestHeaders(["cookie"]) : void 0;
|
|
22
|
-
await fetchSession({ headers });
|
|
23
|
-
}
|
|
24
32
|
const mode = typeof auth === "string" ? auth : auth?.only ?? "user";
|
|
25
33
|
const redirectTo = typeof auth === "object" ? auth.redirectTo : void 0;
|
|
34
|
+
if (!loggedIn.value || mode === "guest") {
|
|
35
|
+
const headers = import.meta.server ? useRequestHeaders(["cookie"]) : void 0;
|
|
36
|
+
const isHydratedPrerenderPayload = (import.meta.client || !import.meta.server) && nuxtApp.isHydrating && Boolean(nuxtApp.payload.prerenderedAt || nuxtApp.payload.isCached);
|
|
37
|
+
await fetchSession({ headers, ...isHydratedPrerenderPayload ? { force: true } : {} });
|
|
38
|
+
}
|
|
26
39
|
if (mode === "guest") {
|
|
27
40
|
if (loggedIn.value)
|
|
28
41
|
return navigateTo(redirectTo ?? config?.redirects?.guest ?? "/");
|
|
29
42
|
return;
|
|
30
43
|
}
|
|
31
|
-
if (!loggedIn.value)
|
|
32
|
-
|
|
44
|
+
if (!loggedIn.value) {
|
|
45
|
+
const resolved = resolveLoginRedirect({
|
|
46
|
+
route: to,
|
|
47
|
+
loginTarget: redirectTo ?? config?.redirects?.login ?? "/login",
|
|
48
|
+
config
|
|
49
|
+
});
|
|
50
|
+
return resolved.external ? navigateTo(resolved.to, { external: true }) : navigateTo(resolved.to);
|
|
51
|
+
}
|
|
33
52
|
if (typeof auth === "object" && auth.user) {
|
|
34
53
|
if (!user.value || !matchesUser(user.value, auth.user))
|
|
35
54
|
throw createError({ statusCode: 403, statusMessage: "Access denied" });
|
|
36
55
|
}
|
|
37
56
|
});
|
|
57
|
+
function resolveLoginRedirect(input) {
|
|
58
|
+
const { route, loginTarget, config } = input;
|
|
59
|
+
const preserveRedirect = config?.preserveRedirect ?? true;
|
|
60
|
+
const redirectQueryKey = config?.redirectQueryKey ?? "redirect";
|
|
61
|
+
if (!preserveRedirect)
|
|
62
|
+
return { to: loginTarget, external: false };
|
|
63
|
+
if (!loginTarget.startsWith("/") || loginTarget.startsWith("//"))
|
|
64
|
+
return { to: loginTarget, external: false };
|
|
65
|
+
const [beforeHash, hash = ""] = loginTarget.split("#", 2);
|
|
66
|
+
const [path, query = ""] = beforeHash.split("?", 2);
|
|
67
|
+
try {
|
|
68
|
+
const params2 = new URLSearchParams(query);
|
|
69
|
+
if (params2.has(redirectQueryKey))
|
|
70
|
+
return { to: loginTarget, external: false };
|
|
71
|
+
} catch {
|
|
72
|
+
return { to: loginTarget, external: false };
|
|
73
|
+
}
|
|
74
|
+
if (import.meta.server) {
|
|
75
|
+
const separator = query ? "&" : "";
|
|
76
|
+
const encodedRedirect = encodeURIComponent(route.fullPath);
|
|
77
|
+
const url = `${path}?${query}${separator}${redirectQueryKey}=${encodedRedirect}${hash ? `#${hash}` : ""}`;
|
|
78
|
+
return { to: url, external: true };
|
|
79
|
+
}
|
|
80
|
+
const params = new URLSearchParams(query);
|
|
81
|
+
const queryObj = {};
|
|
82
|
+
for (const [k, v] of params.entries())
|
|
83
|
+
queryObj[k] = v;
|
|
84
|
+
queryObj[redirectQueryKey] = route.fullPath;
|
|
85
|
+
return { to: { path, query: queryObj, ...hash ? { hash: `#${hash}` } : {} }, external: false };
|
|
86
|
+
}
|
|
87
|
+
async function getAuthRouteRules() {
|
|
88
|
+
if (!authRouteRulesPromise) {
|
|
89
|
+
authRouteRulesPromise = import("#auth/route-rules").then((mod) => mod.authRouteRules || {}).catch(() => ({}));
|
|
90
|
+
}
|
|
91
|
+
return await authRouteRulesPromise;
|
|
92
|
+
}
|
|
93
|
+
async function getRouteRulesMatcher() {
|
|
94
|
+
if (!routeRulesMatcherPromise) {
|
|
95
|
+
routeRulesMatcherPromise = getAuthRouteRules().then((authRouteRules) => {
|
|
96
|
+
if (!Object.keys(authRouteRules).length)
|
|
97
|
+
return null;
|
|
98
|
+
return toRouteMatcher(createRouter({ routes: authRouteRules }));
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return await routeRulesMatcherPromise;
|
|
102
|
+
}
|