Skip to content

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.

FileDescription
auth-hydrate.global.tsClient-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 ​

FileGuardsDescription
auth-check.tsAll authenticated internal pagesPrimary 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.tsPages that require module-level accessPost-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.tsDAM 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.tsDAM portal/branding settings pagesRequires hasDamAccess at minimum. Viewers can reach the settings shell but individual panels are further gated by per-component canManage* checks.
can-access-general-settings.tsGeneral settings pagesRequires hasGeneralSettingsAccess from getUserModulesAndRoles. Returns a 401 fatal error if the user lacks access.

Workspace guards ​

FileGuardsDescription
check-workspace.tsPages that depend on a current workspaceVerifies 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.tsAny page with a workspace_id route paramLightweight membership check: confirms the workspace_id param appears in user.accessibleWorkspaces. Returns a 403 fatal error if not found.
check-if-suspended.tsDAM pages inside suspended workspacesWhen 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.tsadd-instance setup pagePrevents 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 ​

FileGuardsDescription
check-external-access.tsExternal 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.tsRequest-access pageIf 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.tsOTP verify pageRedirects 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.tsExternal upload and request-submitted pagesRequires 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 ​

FileGuardsDescription
guest-check.tsLogin / index routeRedirects 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'],
})
  1. auth-hydrate.global.ts — hydrates user state from the token if needed
  2. auth-check — verifies authentication, validates workspace membership
  3. check-workspace-access — confirms workspace_id is in accessibleWorkspaces
  4. can-access-dam-module — confirms DAM access and role, checks for DAM instance
  5. can-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() or useRouter() in middleware — use the to/from params
  • 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) return when the call would fail without a browser origin