Hi there,
this code snippet can help you implement JWT (JSON Web Token) authentication in the Vue.js 3 application.
The code creates an axiosBaseInstance instance of the Axios Javascript library to make HTTP requests to the API RESTfull backend server. (In my projects, I’ve recently been using the fantastic Django Ninja Rest framework with the Django Ninja JWT plugin.)
The axiosBaseInstance provides interceptors to change the outgoing HTTP request configuration by inserting the valid access token into Content-Type header and handling 401 errors in incoming HTTP server responses.
The axiosBaseInstance object is configured with a few options like the baseURL, timeout, maxContentLength, maxBodyLength, and headers.
The Content-Type header is set to application/json in the headers object and says in what form the data in the request body should be sent.
The interceptors are used to add middleware functions to the Axios request/response pipeline.
The request interceptor modifies the request configuration before it is sent to the server.
This is where is added the Authorization header to the request using the access token retrieved from the auth store (in my case the Pinia store for Vue.js).
The response interceptor is added to handle error responses from the server. In case of a 401 unauthorized error, it tries to get the new valid access token by calling the refreshAccessToken method of the auth store (from the Pinia store).
If the token is successfully refreshed, the Authorization header of the axiosBaseInstance object is updated by the new the access token and retry the original request.
That’s all for now.
If you have any questions or notes, just give me a shout in comments.
Hanz
The Pinia auth store example code
import Axios from 'axios' import {ref} from "vue" import {defineStore} from 'pinia' // Pinia store import {router} from "../router" // we want an extra dedicated Axios instance without interceptors to bypass // the catching 401 error in the main Axios instance const axiosInstanceAuth = Axios.create({ baseURL: URL_API_SERVER, timeout: 20 * 1000, // 20 sec headers: { 'Content-Type': 'application/json', } }) export const useAuthStore = defineStore('auth', () => { let accessToken = ref(null) let refreshToken = ref(null) let loggedUser = ref(null) let isLogged = ref(false) const _cleanCredentials = () => { isLogged.value = false loggedUser.value = null accessToken.value = false refreshToken.value = false } async function refreshAccessToken() { try { const response = await axiosInstanceAuth.post(URL_REFRESH_TOKEN, { refresh: refreshToken.value }) accessToken.value = response.data.access isLogged.value = true } catch (e) { _cleanCredentials() await router.push({name:'login_view'}) } } async function loginUser(username, password) { try { const res = await axiosInstanceAuth.post(URL_ACCESS_TOKEN, { username: username, password: password }) accessToken.value = res.data.access refreshToken.value = res.data.refresh loggedUser.value = res.data.username isLogged.value = true return true } catch (e) { _cleanCredentials() return false } } async function logoutUser() { _cleanCredentials() await router.push({name:'logour_view'}) } return {accessToken, refreshToken, loggedUser, isLogged, refreshAccessToken, loginUser, logoutUser} })
Example code of the Axios instance with request/response interceptors
import Axios from 'axios' import {useAuthStore} from '../stores/auth' // Pinia store // the main Axios instance with interceptors const axiosBaseInstance = Axios.create({ baseURL: URL_API_SERVER, timeout: 20 * 1000, // 20 sec maxContentLength: 50 * 1024 ^ 3, // in Bytes, set 50MB maxBodyLength: 50 * 1024 ^ 3, // in Bytes, set 50MB headers: { 'Content-Type': 'application/json', } }) axiosBaseInstance.interceptors.request.use( config => { // from Pinia store const store = useAuthStore() // set the Authorization header with the current access token config.headers.Authorization = `Bearer ${store.accessToken}` return config; }, error => { return Promise.resolve(error) }); axiosBaseInstance.interceptors.response.use( response => response, async error => { const originalRequest = error.config // check if the error is due to an expired access token if (error.response.status === 401 && !originalRequest._retry) { originalRequest._retry = true // from Pinia store const store = useAuthStore() await store.refreshAccessToken() // update the Authorization header with the new access token axiosBaseInstance.defaults.headers.common['Authorization'] = `Bearer ${store.accessToken}` // retry the original request return axiosBaseInstance(originalRequest) } return Promise.reject(error) } )