Vue-Element-Admin request.js Axios Wrapper: Token Refresh Mechanism and 401 Error Handling

The request.js wrapper in vue-element-admin does not implement automatic silent token refresh; instead, it detects expired tokens via custom backend response codes (50008, 50012, 50014) and forces a user re-login, while treating standard HTTP 401 errors as generic network failures.

The request.js file is the central HTTP client configuration in the PanJiaChen/vue-element-admin repository, providing a pre-configured Axios instance with interceptors for authentication and error handling. Understanding its token lifecycle is essential for developers customizing authentication flows or debugging session expiration issues.

How request.js Injects Authentication Tokens

Request Interceptor: Automatic Header Injection

Before any HTTP request leaves the application, the request interceptor in src/utils/request.js (lines 13-24) checks the Vuex store for an existing session. If store.getters.token returns a value, the interceptor retrieves the actual token string via getToken() from src/utils/auth.js and injects it into the request headers as X-Token.

// src/utils/request.js - Request interceptor
service.interceptors.request.use(
  config => {
    if (store.getters.token) {
      config.headers['X-Token'] = getToken()
    }
    return config
  },
  error => {
    console.log(error)
    return Promise.reject(error)
  }
)

This ensures that authenticated routes receive the necessary credentials without manual header configuration in every API call.

Business Logic Error Handling in Response Interceptors

Custom Response Codes vs HTTP Status Codes

The wrapper differentiates between transport-layer failures (HTTP 4xx/5xx) and application-layer errors (custom JSON codes). In src/utils/request.js (lines 45-68), the response interceptor examines the code field in the response body. While a successful response carries code: 20000, specific values trigger authentication-related actions:

  • 50008: Illegal token (tampered or malformed)
  • 50012: Other clients logged in (session concurrency)
  • 50014: Token expired

When these codes appear, the interceptor opens an Element-UI MessageBox dialog prompting the user to re-login:

// Simplified excerpt from lines 45-68
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
  MessageBox.confirm(
    'You have been logged out, you can cancel to stay on this page, or log in again',
    'Confirm logout',
    {
      confirmButtonText: 'Re-Login',
      cancelButtonText: 'Cancel',
      type: 'warning'
    }
  ).then(() => {
    store.dispatch('user/resetToken').then(() => {
      location.reload()  // Force reload to reset router and state
    })
  })
}

The Absence of Silent Token Refresh

No automatic token refresh mechanism exists in the current implementation. Unlike modern OAuth2 clients that might call a /refresh endpoint upon detecting an expired access token, this wrapper immediately invalidates the session and requires manual user intervention.

The resetToken action in src/store/modules/user.js clears the Vuex state and removes the token from cookies/local storage, but it does not attempt to exchange a refresh token or negotiate a new session silently. This design assumes the backend uses short-lived tokens or session-based authentication where re-authentication is the preferred security posture.

Handling HTTP 401 Unauthorized Errors

When the server returns a genuine HTTP 401 status (or any other non-2xx status) without the custom JSON body expected by the business logic interceptor, the error falls into the second response interceptor handler at lines 74-81:

// src/utils/request.js - Error interceptor
service.interceptors.response.use(
  response => { /* ... success handling ... */ },
  error => {
    console.log('err' + error) // Debug logging
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

Standard 401 errors are treated as generic network failures. The interceptor displays the error message via Element-UI's Message component and propagates the rejected promise to the calling code. No automatic logout, token refresh, or redirection occurs for HTTP-level authentication failures.

Practical Implementation Examples

Making an Authenticated Request

API functions import the configured instance and benefit from automatic token injection:

import request from '@/utils/request'

export function fetchUserProfile() {
  // X-Token header is injected automatically if user is logged in
  return request({
    url: '/user/profile',
    method: 'get'
  })
}

Catching 401 Errors in Components

Since the wrapper rejects the promise for HTTP errors, components can handle authentication failures explicitly:

export default {
  async created() {
    try {
      await this.$store.dispatch('user/getInfo')
    } catch (err) {
      // err.message contains the HTTP status text
      console.warn('Authentication failed:', err)
      this.$router.push('/login')
    }
  }
}

Simulating Token Expiration Handling

When the backend returns code: 50014, the user sees a confirmation dialog. Upon confirmation, the application state resets:

// This flow occurs automatically inside request.js when code === 50014
store.dispatch('user/resetToken').then(() => {
  // Clears localStorage/cookies and Vuex state
  location.reload() // Full page reload clears in-memory router state
})

Summary

  • Automatic injection: The request interceptor adds X-Token headers by reading from store.getters.token via getToken() in src/utils/auth.js.
  • Custom code handling: Responses with codes 50008, 50012, or 50014 trigger a logout dialog and dispatch user/resetToken, forcing re-authentication.
  • No silent refresh: The wrapper does not implement refresh token logic; expired sessions always require user action.
  • HTTP 401 treatment: Standard 401 responses surface as generic errors in the Axios error interceptor without automatic logout or token clearing.
  • State management: Token removal occurs through src/store/modules/user.js, ensuring both Vuex and persistent storage are cleared before reloading.

Frequently Asked Questions

How does request.js detect an expired token?

The wrapper checks the code field in JSON responses for specific values: 50014 (token expired), 50008 (illegal token), or 50012 (other clients logged in). When these appear, it displays a confirmation dialog and dispatches the user/resetToken action to clear the session. It does not inspect JWT expiration dates or attempt proactive validation.

What is the difference between HTTP 401 and the custom 50014 code?

HTTP 401 is handled in the Axios error interceptor (lines 74-81) as a network-level failure, displaying a generic error message and rejecting the promise. The custom 50014 code is handled in the success interceptor (lines 45-68) as a business logic condition, triggering the logout/re-login flow. The application expects the backend to return 200 OK with a JSON body containing these custom codes rather than standard HTTP 401 statuses.

Where is the authentication token stored in vue-element-admin?

The token is retrieved via getToken() from src/utils/auth.js, which abstracts the storage mechanism (typically cookies or localStorage). The request interceptor accesses it through store.getters.token to check for existence, then reads the actual value via getToken() to inject into the X-Token header.

Can I add automatic token refresh to request.js?

Yes, but it requires modifying src/utils/request.js to intercept 401 errors or specific custom codes, call a refresh endpoint, update the token via setToken(), and retry the original request. The current implementation deliberately avoids this pattern for simplicity, instead forcing a full page reload to clear all state when sessions expire.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →