Authentication

How mock authentication works, demo users and roles, and how to connect a real auth backend.

How Mock Auth Works

Haze Dashboard uses a Pinia store ( app/stores/auth.ts) for mock authentication. There is no real backend or database — authentication is simulated entirely in the browser. The store maintains the current user, computes their role-based permissions, and provides login/logout methods.

On initial load, the store automatically logs in as the Admin user for convenience during development. You can change this behavior by modifying the initial user ref in the store.

Auth Store API

The auth store exposes the following reactive state and methods:

typescript
const authStore = useAuthStore()

// Reactive state
authStore.user              // Current user object (or null)
authStore.isAuthenticated   // Computed boolean
authStore.permissions       // Computed string[] based on user's role

// Methods
authStore.hasPermission('edit_orders')  // Check a specific permission
authStore.login('admin@haze.dev')       // Log in by email (matches demo users)
authStore.logout()                       // Clear the current user

Demo Users

Three demo users are defined in app/utils/permissions.ts:

NameEmailRoleCapabilities
Alex Johnsonadmin@haze.devAdminAll permissions (view, create, edit, delete for all resources, manage roles)
Sarah Cheneditor@haze.devEditorView + create + edit (no delete, no role management)
Marcus Webbviewer@haze.devViewerView only (no create, edit, or delete)

Roles & Permissions

Permissions follow the pattern action_resource (e.g., view_orders, create_products, delete_customers). The full permission matrix is defined in app/utils/permissions.ts:

typescript
// app/utils/permissions.ts
export const rolePermissions: Record = {
  admin: [
    'view_orders', 'create_orders', 'edit_orders', 'delete_orders',
    'view_products', 'create_products', 'edit_products', 'delete_products',
    'view_customers', 'create_customers', 'edit_customers', 'delete_customers',
    'view_invoices', 'create_invoices', 'edit_invoices', 'delete_invoices',
    'view_users', 'create_users', 'edit_users', 'delete_users',
    'manage_roles',
  ],
  editor: [
    'view_orders', 'create_orders', 'edit_orders',
    'view_products', 'create_products', 'edit_products',
    'view_customers', 'create_customers', 'edit_customers',
    'view_invoices', 'create_invoices', 'edit_invoices',
    'view_users',
  ],
  viewer: [
    'view_orders', 'view_products', 'view_customers',
    'view_invoices', 'view_users',
  ],
}

Role-Based UI

Use the auth store's hasPermission() method to conditionally show or hide UI elements based on the user's role:

vue


This pattern is used throughout the template to hide create/edit/delete buttons for users without the required permissions.

Route Protection (Middleware)

You can protect routes with Nuxt middleware. Create a middleware that checks authentication status and redirects unauthenticated users to the login page:

typescript
// app/middleware/auth.ts
export default defineNuxtRouteMiddleware((to) => {
  const authStore = useAuthStore()

  if (!authStore.isAuthenticated) {
    return navigateTo('/auth/login')
  }
})

Apply the middleware to specific pages:

vue

Connecting Real Authentication

To replace the mock auth with a real backend, you will need to modify:

  • 1. Auth store — Replace the in-memory user ref with API calls. The login() method should POST credentials to your auth endpoint and store the returned token/session. The logout() method should clear the token and call the logout endpoint.
  • 2. Auth middleware — Check for a valid token/session instead of the mock isAuthenticated flag. Redirect to login on 401 responses.
  • 3. Permissions — Fetch the user's permissions from your backend instead of using the static rolePermissions map.
  • 4. Login/Register pages — Connect the form submissions to your real auth API endpoints.

The rest of the dashboard (components, layouts, pages) does not need to change — it already uses authStore.user and authStore.hasPermission(), which will continue to work with your real data.

Example: Real Login Implementation

Here is how the auth store login method might look when connected to a real API:

typescript
// Modified auth store with real API
export const useAuthStore = defineStore('auth', () => {
  const user = ref(null)
  const token = useCookie('auth-token')

  async function login(email: string, password: string) {
    const { data } = await useFetch('/api/auth/login', {
      method: 'POST',
      body: { email, password },
    })

    if (data.value) {
      user.value = data.value.user
      token.value = data.value.token
      return true
    }
    return false
  }

  async function logout() {
    await useFetch('/api/auth/logout', { method: 'POST' })
    user.value = null
    token.value = null
    navigateTo('/auth/login')
  }

  async function fetchUser() {
    if (token.value) {
      const { data } = await useFetch('/api/auth/me')
      user.value = data.value
    }
  }

  return { user, token, login, logout, fetchUser }
})

Tip

For SSR-compatible authentication, consider using useCookie() to store the auth token instead of localStorage. Cookies are sent with every request, so Nuxt can verify authentication during server-side rendering.

Next Steps

See the Mock API guide to understand the data layer, or explore Components for the full shared component reference.