Security & Authentication - FactoriaF5-Asturias/project-p5-digital-academy-team1-restaurant-frontend GitHub Wiki

Authentication

The backend issues a JWT stored in httpOnly cookies. The frontend never reads or stores the token: the auth store only keeps the user data and role returned by /auth/me.

sequenceDiagram
    participant U as User
    participant F as Frontend (Vue)
    participant B as Backend (Spring Boot)
    U->>F: Submits login form
    F->>B: POST /api/v1/auth/login
    B-->>F: 200 + Set-Cookie (httpOnly JWT) + XSRF-TOKEN cookie
    F->>B: GET /api/v1/auth/me
    B-->>F: User data + roles
    F->>F: auth store saves user and role (no token)
    U->>F: Navigates to a protected view
    F->>F: Router guard checks role
    F->>B: Request with cookies + X-XSRF-TOKEN header
    B-->>F: 200 / 401 / 403

Why cookies instead of localStorage

  • An httpOnly cookie cannot be read by JavaScript, so an XSS vulnerability cannot steal the session.
  • The browser sends the cookies automatically on each request (withCredentials: true).
  • Cookies are Secure and SameSite=None, which is why both apps run over HTTPS in development.

XSRF protection

Because the session travels in cookies, the backend also requires an XSRF token. Axios copies the XSRF-TOKEN cookie into the X-XSRF-TOKEN header automatically:

// src/services/api.js
const api = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  withCredentials: true,
  withXSRFToken: true,
  xsrfCookieName: 'XSRF-TOKEN',
  xsrfHeaderName: 'X-XSRF-TOKEN',
})

Automatic session renewal

A response interceptor in api.js handles expired tokens without the user noticing:

flowchart TD
    A[Response with error] --> B{Status}
    B -->|403| F[onForbidden: go to Acceso denegado]
    B -->|other| R[Reject the error]
    B -->|401| C{Request was login?}
    C -->|Yes| R
    C -->|No| D{Already refreshing?}
    D -->|Yes| Q[Queue the request until the refresh ends]
    D -->|No| E[GET /auth/refresh]
    E -->|OK| G[Retry original + queued requests]
    E -->|Fails| H[onUnauthorized: clear session, go to Login]
  • Credentials errors on login are not retried.
  • Each request is retried only once (_retry flag) to avoid infinite loops.
  • api.js does not import the router or the stores: the app registers what to do on 401 / 403 with setSessionHandlers (src/router/sessionRedirects.js), keeping the HTTP layer decoupled.

Authorization (Route Guards)

Each route declares its allowed roles in meta.roles, and a global beforeEach guard applies these rules (src/router/guards.js):

  • Unknown URL → guests go to Login, logged-in users to the Carta.
  • Not logged in and the route needs a role → Login.
  • Logged in on a guest-only route (login, register) → Carta.
  • Logged in with the wrong role → Acceso denegado.

Route guards improve the user experience, but real security is enforced by the backend: every protected endpoint validates the session and role on the server.


Data Stored in the Browser

Data Storage Sensitive
Cart (products and quantities) localStorage No
Device identifier (random UUID used for table detection) localStorage No
Session (JWT) httpOnly cookie Yes — not accessible from JS

Form Validation

  • Client-side validation gives immediate feedback (required fields, email format, postal code, product fields in the admin form — utils/productValidation.js).
  • Actions stay disabled until the data is valid (e.g. "Confirm order" without payment method).
  • Server-side validation errors are shown to the user without losing the data entered.