Skip to content

Authentication (Client-side)

Stack

@nuxtjs/auth ^4.9.1 with the local strategy only. No OAuth, no SSO.

auth: {
  strategies: {
    local: {
      endpoints: {
        login: { url: 'webauthenticate', method: 'post', propertyName: 'data' },
        user: { url: 'me', method: 'get', propertyName: 'data' },
        logout: false
      }
    }
  }
}

Token storage

@nuxtjs/auth's default for the local strategy is localStorage (and a cookie for cross-tab). Verify by inspecting the actual storage in a logged-in browser session.

For the user record, there's also an explicit cookie: userdetail (set by the login flow; read by middleware/guest.js):

// middleware/guest.js
var cookie = store.$cookies.get("userdetail");
if (cookie) {
  store.$auth.setUser(cookie);
}

So the user record is in a cookie (not httpOnly — both the FE and an XSS attacker can read it).

Login flow

  1. User submits login form on pages/login.vue
  2. $auth.loginWith('local', { data: { email, password } }) POSTs to ${baseURL}/webauthenticate
  3. On success, @nuxtjs/auth reads the response's data.token and stores it
  4. $auth.user is populated from the data field (per propertyName: 'data')
  5. The userdetail cookie is set somewhere in the login flow (probably in the page's .then(...) handler)
  6. Redirect to /dashboard

/me bootstrap

On every page load, @nuxtjs/auth calls GET /me to refresh the user record. The response's data field is set as $auth.user.

This means a stale token will fail at /me, and the FE should redirect to login. Verify this redirect actually happens.

Logout

logout: false in the strategy means @nuxtjs/auth does not call any logout endpoint. To log out, the FE must:

  1. $auth.logout() — clears local state
  2. Manually clear cookies ($cookies.removeAll() or per-cookie)
  3. Redirect to /login

This is purely client-side. The token is not added to a revocation set server-side. A stolen token remains valid until it expires (which, per the BE patterns, is "never explicitly").

Token expiry

Probably none. The designer-api's /webauthenticate handler doesn't appear to encode an exp field. Verify by reading the BE handler.

Multi-tab synchronisation

Cookies cross tabs, so a login in one tab makes the user "logged in" in another tab on the next page load. But logout in one tab does not auto-logout other tabs — the userdetail cookie clear is per-tab.

To sync: add a window.addEventListener('storage', ...) handler.

CSRF

Auth is via header (Bearer token or similar), not cookie. CSRF therefore not exploitable on authenticated endpoints. (The session-cookie issue would apply only if BE handlers ever read req.session.)

Findings

F-1: Token has no expiry (MEDIUM)

A token issued at any point remains valid forever. Combined with no server-side revocation, a leaked token is a long-lived credential.

Fix: BE-side, encode exp in the token. FE-side, refresh / re-login on expiry.

F-2: No server-side logout (MEDIUM)

Logout is purely client-side. Tokens stay valid.

Fix: add a POST /logout endpoint to designer-api; have FE call it before clearing local state.

Storing the user record in a non-httpOnly cookie means XSS can read user info (PII).

Fix: keep only the auth token in storage; let the FE fetch /me fresh each session.

F-4: No auth middleware applied globally (HIGH if true — VERIFY)

nuxt.config.js's router.middleware: 'axios' runs the axios interceptor, but no auth middleware is in the global chain. Pages don't appear to declare middleware: 'auth' in spot-checks. This means unauthenticated visitors may reach protected pages without redirect.

Verify: try visiting /dashboard without a token; does the FE redirect?

If not, add middleware: 'auth' per-page or set router.middleware: ['auth', 'axios'] globally.

F-5: Role-typed UI gating is enforcement-by-hope (MEDIUM)

The Navbar hides nav items based on role_type, but a knowledgeable user can navigate directly via URL (e.g., visit /topics even if the nav item isn't shown). The BE is the only enforcement layer; if BE handlers don't check role_type, the role gating is cosmetic.

Verify: are BE handlers consistent about checking role_type?

Recommendations

ID Recommendation Effort
AC-1 Verify auth-redirect on unauthenticated visit; add middleware: 'auth' globally if missing Small
AC-2 Encode exp in token (BE) + FE refresh-on-expiry Medium (BE+FE)
AC-3 Add POST /logout on BE; FE calls it before clearing state Small
AC-4 Remove userdetail cookie; rely on /me fetch Small
AC-5 Add multi-tab storage listener for logout sync Small
AC-6 Centralise role IDs as const ROLE = {...} (also referenced from admin_console_R) Small
AC-7 Audit BE handlers for consistent role_type checks Medium