Appearance
Middleware ​
All middleware lives in app/middleware/. Nuxt 4 route guards — global ones run first on every navigation, named ones are applied per-page in declaration order.
Global Middleware ​
Runs on every navigation. Declared with the .global.ts suffix.
| File | Description |
|---|---|
auth-hydrate.global.ts | Client-side only (skips SSR). Sets isExternalSession on useAuth() based on whether the destination is an external route name. If a token is present but user is null (e.g., hard refresh), calls fetchUser() so all downstream named guards see the real user rather than null. |
Named Middleware ​
Declared per-page via definePageMeta({ middleware: [...] }). Applied in declaration order.
Internal auth and access guards ​
| File | Guards | Description |
|---|---|---|
auth-check.ts | All authenticated internal pages | Primary auth gate for the internal app. Skips auth pages (login, forgot-password, etc.). On SSR, trusts the auth_token cookie and returns early. On the client, fetches the user if not yet loaded. Redirects external users to their workspace upload page. Validates workspace_id format and membership; stores a referring_url_data cookie and redirects unauthenticated users to /. |
can-access.ts | Pages that require module-level access | Post-auth module guard. Checks hasDamAccess, hasLeadAccess, and hasGeneralSettingsAccess from getUserModulesAndRoles. Redirects to the user's landing path if the requested module is off-limits. Also tracks referringUrl* cookies for post-login redirect. |
can-access-dam-module.ts | DAM pages (workspace_id-dam-*) | Verifies the user has DAM access and a valid role (admin, manager, or viewer). The uploaded-assets page additionally requires canUploadContent (manager/admin only). If no DAM instance exists, admins are sent to add-instance; non-admins receive a 503 fatal error. |
can-access-dam-settings.ts | DAM portal/branding settings pages | Requires hasDamAccess at minimum. Viewers can reach the settings shell but individual panels are further gated by per-component canManage* checks. |
can-access-general-settings.ts | General settings pages | Requires hasGeneralSettingsAccess from getUserModulesAndRoles. Returns a 401 fatal error if the user lacks access. |
Workspace guards ​
| File | Guards | Description |
|---|---|---|
check-workspace.ts | Pages that depend on a current workspace | Verifies the currentWorkspace cookie is set and has a non-zero id. If the user is authenticated but the cookie is missing or invalid, logs them out and redirects to /. |
check-workspace-access.ts | Any page with a workspace_id route param | Lightweight membership check: confirms the workspace_id param appears in user.accessibleWorkspaces. Returns a 403 fatal error if not found. |
check-if-suspended.ts | DAM pages inside suspended workspaces | When the workspace is_suspended flag is set, DAM admins are restricted to the dashboard only (all other routes redirect there). Non-admins receive a 401 fatal error. |
check-dam-instance.ts | add-instance setup page | Prevents duplicate instance creation. Redirects to the DAM dashboard if a DAM instance already exists for the workspace. Allows through when no instance exists so the user can complete setup. |
External partner flow guards ​
| File | Guards | Description |
|---|---|---|
check-external-access.ts | External routes (/external/*) | For authenticated active external users, calls the check-external-access API with the origin_url. Clears the auth token and redirects to the request-access page if the API returns has_access: false or a 401/402/403/404 status. No-ops for unauthenticated or internal users. |
external-guest-redirect.ts | Request-access page | If the visitor already has a valid token and is an active external user for this workspace, redirects them straight to the upload page. Pass-through otherwise. |
external-otp-verify.ts | OTP verify page | Redirects already-active external users to upload. For everyone else, checks that the verifyEmail cookie exists and has not passed its 10-minute expiry; if invalid, redirects back to request-access. |
external-upload-auth.ts | External upload and request-submitted pages | Requires an active token and an active user. No token or inactive user → redirects to OTP verify. Active user whose workspace_url_slug does not match the current workspace → redirects to request-access for the correct workspace. |
Utility and redirect guards ​
| File | Guards | Description |
|---|---|---|
guest-check.ts | Login / index route | Redirects already-authenticated users away from the login page. Resolves the target workspace from the currentWorkspace cookie or accessibleWorkspaces, then navigates to the appropriate DAM dashboard using role/module-aware landing logic. |
Middleware Execution Order ​
Nuxt runs global middleware first, then named middleware in array order. Example for a DAM settings page:
ts
definePageMeta({
middleware: ['auth-check', 'check-workspace-access', 'can-access-dam-module', 'can-access-dam-settings'],
})auth-hydrate.global.ts— hydrates user state from the token if neededauth-check— verifies authentication, validates workspace membershipcheck-workspace-access— confirms workspace_id is in accessibleWorkspacescan-access-dam-module— confirms DAM access and role, checks for DAM instancecan-access-dam-settings— confirms the user can reach settings pages
If any guard returns navigateTo or showError, subsequent guards do not run.
Writing New Middleware ​
ts
// app/middleware/my-guard.ts
export default defineNuxtRouteMiddleware((to, from) => {
const { user } = useAuth()
if (!user.value) {
return navigateTo('/', { replace: true })
}
})Rules:
- Never call
useRoute()oruseRouter()in middleware — use theto/fromparams - Guard only; fetch heavy data in the page itself
- Return
navigateTo(path)to redirect,showError({ statusCode, message, fatal: true })for hard errors, or nothing to continue - Skip SSR API calls with
if (import.meta.server) returnwhen the call would fail without a browser origin
Related Documentation ​
- Pages
- Layouts
- Composables —
useAuth,useHelpers,useDamStore