Usually I make applications with Django and React, using JWT for authentication. I am testing nextJS. I can't manage the JWT authentication because I can't access the localstorage to get my 2 tokens (access refresh).
With react I use axios.js:
import axios from 'axios';
import { baseURL } from './const/urls';
const axiosInstance = axios.create({
baseURL: baseURL,
timeout: 5000,
headers: {
Authorization: localStorage.getItem('access_token')
? 'JWT ' localStorage.getItem('access_token')
: null,
'Content-Type': 'application/json',
accept: 'application/json',
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
Expires: '0'
}
});
axiosInstance.interceptors.response.use(
(response) => response,
(error) => {
const originalRequest = error.config;
// Prevent infinite loops
if (
error.response.status === 401 &&
originalRequest.url === '/token/refresh/'
) {
window.location.href = '/login/';
return Promise.reject(error);
}
if (
error.response.data.code === 'token_not_valid' ||
error.response.status === 401 ||
error.response.statusText === 'Unauthorized'
) {
const refreshToken = localStorage.getItem('refresh_token');
if (refreshToken) {
const tokenParts = JSON.parse(atob(refreshToken.split('.')[1]));
// exp date in token is expressed in seconds, while now() returns milliseconds:
const now = Math.ceil(Date.now() / 1000);
if (tokenParts.exp > now) {
return axiosInstance
.post('/token/refresh/', { refresh: refreshToken })
.then((response) => {
localStorage.setItem('access_token', response.data.access);
localStorage.setItem('refresh_token', response.data.refresh);
axiosInstance.defaults.headers['Authorization'] =
'JWT ' response.data.access;
originalRequest.headers['Authorization'] =
'JWT ' response.data.access;
return axiosInstance(originalRequest);
})
.catch((err) => {
console.log(err);
});
} else {
console.log('Refresh token is expired', tokenParts.exp, now);
window.location.href = '/Login/';
}
} else {
console.log('Refresh token not available.');
window.location.href = '/Login/';
}
}
// specific error handling done elsewhere
return Promise.reject(error);
}
);
export default axiosInstance;
But It doesn't work with nextJS. localStorage or window.localStorage undifined. I understand that it doesn't work because it tries to execute the code on the server side. How to force it to use the client side code only? I saw that I could useEffect but I don't understand how to use it in my case.
CodePudding user response:
Use (typeof window === 'undefined')
condition to check if you can access window which you will be able to on the client side but won't be on the server side. I use it often so it works. For instance you could do something like this depending on the condition check:
// save to storage
export const saveToStorage = (key, value) => {
if(typeof window !== 'undefined') {
return window.localStorage.setItem(key, value);
}
}
// get from storage
export const getFromStorage = (key) => {
if (typeof window !== 'undefined') {
return window.localStorage.getItem(key);
}
}
CodePudding user response:
NextJS can run on nodeJS (server) where the window is not available, you need to make sure it will only run on the client-side (might be by checking if window is not undefined) but you need to be aware that your requests won't work on the server-side.
I recommend saving your token on cookies if you need SSR.
if (typeof window !== "undefined") {
// Client-side-only code
}