diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d7094d5..61937bb 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -73,12 +73,6 @@ yarn lint
yarn types
```
-To fix the issues automatically, you can run the following command:
-
-```bash
-yarn fix:fmt
-```
-
# Releasing
Once all the changes are merged into main branch, run the following command to release a new version:
diff --git a/playground/error.vue b/playground/error.vue
new file mode 100644
index 0000000..08e14bd
--- /dev/null
+++ b/playground/error.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+ Nuxt - Laravel Sanctum application sample
+
+
+
+
+
+ Error
+ {{ props.error }}
+
+
+
diff --git a/playground/tsconfig.json b/playground/tsconfig.json
index b575a24..f5f754b 100644
--- a/playground/tsconfig.json
+++ b/playground/tsconfig.json
@@ -1,3 +1,4 @@
{
"extends": "./.nuxt/tsconfig.json",
+ "exclude": ["../dist"],
}
diff --git a/src/index.d.ts b/src/index.d.ts
index cc7e98e..dd4e37e 100644
--- a/src/index.d.ts
+++ b/src/index.d.ts
@@ -5,3 +5,9 @@ declare module 'nuxt/schema' {
sanctum: Partial;
}
}
+
+declare module '#app' {
+ interface PageMeta {
+ excludeFromSanctum?: boolean;
+ }
+}
diff --git a/src/module.ts b/src/module.ts
index be66eb1..70dc799 100644
--- a/src/module.ts
+++ b/src/module.ts
@@ -8,7 +8,11 @@ import {
import { defu } from 'defu';
import type { SanctumModuleOptions } from './types';
-export default defineNuxtModule>({
+type DeepPartial = {
+ [P in keyof T]?: T[P] extends object ? DeepPartial : T[P];
+};
+
+export default defineNuxtModule>({
meta: {
name: 'nuxt-auth-sanctum',
configKey: 'sanctum',
@@ -40,30 +44,40 @@ export default defineNuxtModule>({
onAuthOnly: '/login',
onGuestOnly: '/',
},
+ globalMiddleware: {
+ enabled: false,
+ allow404WithoutAuth: true,
+ },
},
setup(options, nuxt) {
const resolver = createResolver(import.meta.url);
- const publicConfig = nuxt.options.runtimeConfig.public;
- const userModuleConfig = publicConfig.sanctum;
-
- nuxt.options.runtimeConfig.public.sanctum = defu(
- userModuleConfig as any,
- options
- );
+ const runtimeConfigOverrides =
+ nuxt.options.runtimeConfig.public.sanctum;
- addImportsDir(resolver.resolve('./runtime/composables'));
+ const sanctumConfig = defu(runtimeConfigOverrides as any, options);
- addRouteMiddleware({
- name: 'sanctum:auth',
- path: resolver.resolve('./runtime/middleware/sanctum.auth'),
- });
- addRouteMiddleware({
- name: 'sanctum:guest',
- path: resolver.resolve('./runtime/middleware/sanctum.guest'),
- });
+ nuxt.options.runtimeConfig.public.sanctum = sanctumConfig;
addPlugin(resolver.resolve('./runtime/plugin'));
+ addImportsDir(resolver.resolve('./runtime/composables'));
+
+ if (sanctumConfig.globalMiddleware.enabled) {
+ addRouteMiddleware({
+ name: 'sanctum:auth:global',
+ path: resolver.resolve('./runtime/middleware/sanctum.global'),
+ global: true,
+ });
+ } else {
+ addRouteMiddleware({
+ name: 'sanctum:auth',
+ path: resolver.resolve('./runtime/middleware/sanctum.auth'),
+ });
+ addRouteMiddleware({
+ name: 'sanctum:guest',
+ path: resolver.resolve('./runtime/middleware/sanctum.guest'),
+ });
+ }
},
});
diff --git a/src/runtime/httpFactory.ts b/src/runtime/httpFactory.ts
index 2945ec6..5ab71fd 100644
--- a/src/runtime/httpFactory.ts
+++ b/src/runtime/httpFactory.ts
@@ -3,14 +3,17 @@ import {
useCookie,
useRequestEvent,
useRequestHeaders,
+ useRequestURL,
navigateTo,
useNuxtApp,
} from '#app';
import { useSanctumUser } from './composables/useSanctumUser';
-import { useRequestURL } from 'nuxt/app';
import { useSanctumConfig } from './composables/useSanctumConfig';
-export const SECURE_METHODS = new Set(['post', 'delete', 'put', 'patch']);
+type Headers = HeadersInit | undefined;
+
+const SECURE_METHODS = new Set(['post', 'delete', 'put', 'patch']);
+const COOKIE_OPTIONS: { readonly: true } = { readonly: true };
export function createHttpClient(): $Fetch {
const options = useSanctumConfig();
@@ -23,17 +26,13 @@ export function createHttpClient(): $Fetch {
* @param headers Headers collection to extend
* @returns {HeadersInit}
*/
- async function buildClientHeaders(
- headers: HeadersInit | undefined
- ): Promise {
+ async function buildClientHeaders(headers: Headers): Promise {
await $fetch(options.endpoints.csrf, {
baseURL: options.baseUrl,
credentials: 'include',
});
- const csrfToken = useCookie(options.csrf.cookie, {
- readonly: true,
- }).value;
+ const csrfToken = useCookie(options.csrf.cookie, COOKIE_OPTIONS).value;
return {
...headers,
@@ -46,10 +45,8 @@ export function createHttpClient(): $Fetch {
* @param headers Headers collection to extend
* @returns { HeadersInit }
*/
- function buildServerHeaders(headers: HeadersInit | undefined): HeadersInit {
- const csrfToken = useCookie(options.csrf.cookie, {
- readonly: true,
- }).value;
+ function buildServerHeaders(headers: Headers): HeadersInit {
+ const csrfToken = useCookie(options.csrf.cookie, COOKIE_OPTIONS).value;
const clientCookies = useRequestHeaders(['cookie']);
const origin = options.origin ?? useRequestURL().origin;
@@ -123,7 +120,8 @@ export function createHttpClient(): $Fetch {
if (
options.redirect.onLogout === false ||
options.redirect.onLogout === currentRoute.path ||
- options.redirect.onAuthOnly === currentRoute.path
+ options.redirect.onAuthOnly === currentRoute.path ||
+ options.globalMiddleware.enabled === true
) {
return;
}
diff --git a/src/runtime/middleware/sanctum.auth.ts b/src/runtime/middleware/sanctum.auth.ts
index 7aa8593..68b1aab 100644
--- a/src/runtime/middleware/sanctum.auth.ts
+++ b/src/runtime/middleware/sanctum.auth.ts
@@ -1,15 +1,13 @@
import { defineNuxtRouteMiddleware, navigateTo, createError } from '#app';
import type { RouteLocationRaw } from 'vue-router';
-import { useSanctumUser } from '../composables/useSanctumUser';
import { useSanctumConfig } from '../composables/useSanctumConfig';
+import { useSanctumAuth } from '../composables/useSanctumAuth';
export default defineNuxtRouteMiddleware((to) => {
- const user = useSanctumUser();
const options = useSanctumConfig();
+ const { isAuthenticated } = useSanctumAuth();
- const isAuthenticated = user.value !== null;
-
- if (isAuthenticated === true) {
+ if (isAuthenticated.value === true) {
return;
}
diff --git a/src/runtime/middleware/sanctum.global.ts b/src/runtime/middleware/sanctum.global.ts
new file mode 100644
index 0000000..9a81e67
--- /dev/null
+++ b/src/runtime/middleware/sanctum.global.ts
@@ -0,0 +1,53 @@
+import { defineNuxtRouteMiddleware, navigateTo } from '#app';
+import type { RouteLocationRaw } from 'vue-router';
+import { useSanctumConfig } from '../composables/useSanctumConfig';
+import { useSanctumAuth } from '../composables/useSanctumAuth';
+
+export default defineNuxtRouteMiddleware((to) => {
+ const options = useSanctumConfig();
+ const { isAuthenticated } = useSanctumAuth();
+
+ const [homePage, loginPage] = [
+ options.redirect.onGuestOnly,
+ options.redirect.onAuthOnly,
+ ];
+
+ if (homePage === false) {
+ throw new Error(
+ 'You must define onGuestOnly route when using global middleware.'
+ );
+ }
+
+ if (loginPage === false) {
+ throw new Error(
+ 'You must define onAuthOnly route when using global middleware.'
+ );
+ }
+
+ if (isAuthenticated.value === true) {
+ if (to.path === loginPage) {
+ return navigateTo(homePage, { replace: true });
+ }
+
+ return;
+ }
+
+ if (to.path === loginPage || to.meta.excludeFromSanctum === true) {
+ return;
+ }
+
+ if (
+ options.globalMiddleware.allow404WithoutAuth &&
+ to.matched.length === 0
+ ) {
+ return;
+ }
+
+ const redirect: RouteLocationRaw = { path: loginPage };
+
+ if (options.redirect.keepRequestedRoute) {
+ redirect.query = { redirect: to.fullPath };
+ }
+
+ return navigateTo(redirect, { replace: true });
+});
diff --git a/src/runtime/middleware/sanctum.guest.ts b/src/runtime/middleware/sanctum.guest.ts
index de44768..432c878 100644
--- a/src/runtime/middleware/sanctum.guest.ts
+++ b/src/runtime/middleware/sanctum.guest.ts
@@ -1,14 +1,12 @@
import { defineNuxtRouteMiddleware, navigateTo, createError } from '#app';
+import { useSanctumAuth } from '../composables/useSanctumAuth';
import { useSanctumConfig } from '../composables/useSanctumConfig';
-import { useSanctumUser } from '../composables/useSanctumUser';
export default defineNuxtRouteMiddleware(() => {
- const user = useSanctumUser();
const options = useSanctumConfig();
+ const { isAuthenticated } = useSanctumAuth();
- const isAuthenticated = user.value !== null;
-
- if (isAuthenticated === false) {
+ if (isAuthenticated.value === false) {
return;
}
diff --git a/src/types.ts b/src/types.ts
index c8f9a55..019c328 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -74,6 +74,20 @@ export interface RedirectOptions {
onGuestOnly: string | false;
}
+/**
+ * Configuration of the global application-wide middleware.
+ */
+export interface GlobalMiddlewareOptions {
+ /**
+ * Determines whether the global middleware is enabled.
+ */
+ enabled: boolean;
+ /**
+ * Determines whether to allow 404 pages without authentication.
+ */
+ allow404WithoutAuth: boolean;
+}
+
/**
* Options to be passed to the plugin.
*/
@@ -110,4 +124,8 @@ export interface SanctumModuleOptions {
* Behavior of the plugin redirects when user is authenticated or not.
*/
redirect: RedirectOptions;
+ /**
+ * Behavior of the global middleware.
+ */
+ globalMiddleware: GlobalMiddlewareOptions;
}
diff --git a/tsconfig.json b/tsconfig.json
index 4fd07c0..6966e2e 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,10 +1,4 @@
{
"extends": "./.nuxt/tsconfig.json",
- "exclude": [
- "./node_modules",
- "./node_modules/nuxt/node_modules",
- "./dist",
- "./.output",
- "./playground",
- ],
+ "exclude": ["./node_modules", "./dist", "./playground"],
}