0Pricing
Vue Academy · Lesson

Secure Authentication Patterns

Token storage (memory vs localStorage vs httpOnly cookie), silent refresh, logout on tab close.

Secure Authentication Patterns is a free Vue Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Vue Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Tokens and Where They Live

SPAs authenticate with tokens, but where you store them decides your XSS and CSRF exposure. The modern pattern splits a short-lived access token from a long-lived refresh token, each stored differently.

The localStorage Problem

Storing tokens in localStorage is convenient but dangerous: any XSS-injected script can read it and exfiltrate the token. Avoid putting credentials there.

// AVOID
localStorage.setItem("token", accessToken);

Access Token in Memory

Keep the access token in a JavaScript variable or a reactive ref. It vanishes on reload, so a successful XSS has a much smaller window and nothing persists on disk.

import { ref } from "vue";

export const accessToken = ref(null);

Refresh Token in an httpOnly Cookie

The refresh token lives in a HttpOnly, Secure, SameSite cookie. JavaScript cannot read it, so XSS cannot steal it, and the browser sends it only to your auth endpoint.

Set-Cookie: refresh=...; HttpOnly; Secure; SameSite=Strict; Path=/auth

Attaching the Access Token

An Axios request interceptor adds the in-memory access token to the Authorization header on every call.

api.interceptors.request.use((config) => {
  if (accessToken.value) {
    config.headers.Authorization = "Bearer " + accessToken.value;
  }
  return config;
});

Detecting Expiry: the 401

When the access token expires, the server responds with 401 Unauthorized. A response interceptor catches this to trigger a refresh.

api.interceptors.response.use(
  (res) => res,
  async (error) => {
    if (error.response?.status === 401) {
      // attempt refresh
    }
    return Promise.reject(error);
  }
);

Refreshing the Token

On a 401, call the refresh endpoint. The browser sends the httpOnly refresh cookie automatically (withCredentials), and the server returns a fresh access token.

const { data } = await axios.post("/auth/refresh", null, {
  withCredentials: true
});
accessToken.value = data.accessToken;

Retrying the Original Request

After refreshing, replay the original failed request with the new token so the user never notices.

async (error) => {
  const original = error.config;
  if (error.response?.status === 401 && !original._retry) {
    original._retry = true;
    const { data } = await axios.post("/auth/refresh", null, { withCredentials: true });
    accessToken.value = data.accessToken;
    original.headers.Authorization = "Bearer " + data.accessToken;
    return api(original);
  }
  return Promise.reject(error);
}

Avoiding Refresh Stampedes

If many requests 401 at once, queue them behind a single in-flight refresh so you do not fire the refresh endpoint repeatedly.

let refreshing = null;
function refresh() {
  if (!refreshing) {
    refreshing = axios.post("/auth/refresh", null, { withCredentials: true })
      .finally(() => { refreshing = null; });
  }
  return refreshing;
}

Logging Out

On logout, clear the in-memory token and call an endpoint that clears the refresh cookie server-side.

async function logout() {
  accessToken.value = null;
  await axios.post("/auth/logout", null, { withCredentials: true });
}

Combine with CSRF Defense

Because the refresh cookie is sent automatically, protect that endpoint with SameSite and CSRF tokens just like other cookie-authenticated routes.

Quick Check

Where should the access token and refresh token each be stored?

Recap

Store the access token in an in-memory ref (never localStorage) and the refresh token in an HttpOnly cookie. A request interceptor attaches the bearer token; a response interceptor catches 401, calls the refresh endpoint, updates the token, and retries the original request - deduplicating concurrent refreshes and pairing with CSRF defenses.

Frequently asked questions

Is the “Secure Authentication Patterns” lesson free?

Yes — the full text of “Secure Authentication Patterns” is free to read here on the web, and the Vue Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Vue Academy course, upgrade to CoddyKit PRO.

What will I learn in “Secure Authentication Patterns”?

Token storage (memory vs localStorage vs httpOnly cookie), silent refresh, logout on tab close. You practise Vue Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Vue Academy?

No prior experience is required. Vue Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Secure Authentication Patterns” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Vue Academy lesson?

Yes. Every Vue Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. XSS Prevention in Vue
  2. Content Security Policy (CSP) with Vue
  3. CSRF Protection in Vue SPAs
  4. Secure Authentication Patterns
← Back to Vue Academy