Item in local storage is undefined - reactjs

Here the login method will return the token so I'm setting the token to local storage in login method and in auth.header function I'm justing getting the token and I'm using that token in getAllTweet. This getAllTweets methods accept token as parameter.
In login method console.log(responce.data) here it's printing the token but in getAllTweets it displaying error as undefined.
login(loginId, password) {
return axios
.post(API_URL + "login", {
loginId,
password
})
.then(response => {
console.log(response.data);
if (response.data) {
localStorage.setItem("token", JSON.stringify(response.data));
}
console.log(response.data);
return response.data;
});
export default function authHeader() {
const token = JSON.parse(localStorage.getItem('token'));
// if (user && user.token) {
// console.log(user);
// console.log(user.token);
return token;
// return { Authorization: 'Bearer ' + user.accessToken }; // for Spring Boot back-end
//return { 'x-access-token': user.accessToken }; // for Node.js Express back-end
// } else {
// return {};
// }
}
getAllTweet(authHeader) {
return axios.get(API_URL + 'all',{
headers: { Authorization: `Bearer ${authHeader} ` },
});
}

do it like this. it will work as expected.
function authHeader() {
const token = JSON.parse(localStorage.getItem('token'));
return token;
}
getAllTweet() {
return axios.get(API_URL + 'all',{
headers: { Authorization: `Bearer ${authHeader()} ` },
});

async function getAuthHeader() {
const token = JSON.parse(localStorage.getItem('token')).then(token => {
return { Authorization: `Bearer ${token} `}
});
}
// using
const header = await getAuthHeader()

Related

Axios Interceptor is not working in React JS

I am using the below code as an interceptor in my React JS app for getting token back but unfortunately, it is not working. Refresh token returns new idToken and updates local storage data correctly. The same code I'm using some other application which works fine. One main difference is that I currently use React 18 and the previous 16. I struggled to identify the problem but failed. Your help will be appreciable.
axios.interceptors.response.use(
(response) => {
return response;
},
(error) => {
if (error.response.status === 401) {
// console.log(error.response.data.code)
let usersData = JSON.parse(localStorage.getItem("userData"));
const refreshToken = usersData.refreshToken;
return axios
.post(
`${api_base_url}/auth/authentication/refresh_token`,
JSON.stringify({
refresh_token: refreshToken,
})
)
.then((response) => {
usersData["accessToken"] = response.data.data.accessToken;
usersData["idToken"] = response.data.data.idToken;
setSessionStorage("userData", usersData);
error.response.config.headers[
"Authorization"
] = `Bearer ${response.data.data.idToken}`;
return axios(error.response.config);
})
.catch((error) => {
if (error.response.data.code !== "TOKEN_EXPIRED") {
return;
}
localStorage.clear();
window.location = "/login";
});
}
return Promise.reject(error);
}
);
function getIRequestProp(severType, isMultipart, isSocial) {
const serverUrl = severType ? social_api_base_url : api_base_url;
let userData = JSON.parse(localStorage.getItem('userData'));
let idToken;
idToken = userData !== null ? userData['idToken'] : '';
let content_type;
if (isSocial) {
content_type = 'application/x-www-form-urlencoded'
} else {
content_type = isMultipart ? 'multipart/form-data' : 'application/json'
}
return {
serverUrl: serverUrl,
requestHeader: {
'Content-Type': content_type,
'Accept-Language': DEFAULT_LANGUAGE,
Authorization: `Bearer ${idToken}`
}
};
}
async function post(url, body, isSocialServer, isMultipart) {
const {serverUrl, requestHeader} = getIRequestProp(isSocialServer, isMultipart);
return axios.post(serverUrl + url, body, {
headers: requestHeader
});
}
So, I call API like this:
AxiosServices.post(ApiUrlServices.SOCIALS_UPDATE_LINKS(UserInfo.userId), payload, false)
.then(response => {})
What i figured out that return axios(error.response.config); is not sending authorization token in API request headers and trying request infinitely. But consoling error.response.config shows token sets in the config correctly.
Adding an additional modification of axios request, I solved my problem.
axios.interceptors.request.use(request => {
// Edit request config
let usersData = JSON.parse(localStorage.getItem('userData'));
request.headers['Authorization'] = `${usersData.idToken}`;
return request;
}, error => {
return Promise.reject(error);
});

React-Admin Simple Refresh JWT Token

I have an react-admin with an auth provider like the below.
I want to refresh my token, but I don't know how to do it.
I tried to follow this blog post, but my auth is a little different and I can't make it work (the error "httpClient(...).then" is not a function and others make me leave it).
I can make it with a more simple solution, does not need to be in memory. I tried to call my refresh endpoint to get my refresh token, but my call go without the current token.
My endpoint to refresh the token is:
/auth/jwt/refresh
I need to call it like this:
curl -X 'GET' \
'http://localhost:8000/auth/jwt/refresh' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
And My response body would be: (and I need to save it to my localstorage or the in memory way)
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZTUwZDdhZDctOWE5Ni00NzQyLTgxNWEtZTNmZmJmNGRiMTVjIiwiYXVkIjpbImZhc3RhcGktdXNlcnM6YXV0aCJdLCJleHAiOjE2Mzk4NDE1MDF9.-o2yk56sCj_MZx_VA6PxH7gZ-KKSMmopbDNDiapHmn0",
"token_type": "bearer"
}
My inMemoryJWTManager file:
const inMemoryJWTManager = () => {
let inMemoryJWT = null;
let isRefreshing = null;
let logoutEventName = 'ra-logout';
let refreshEndpoint = '/auth/jwt/refresh';
let refreshTimeOutId;
const setLogoutEventName = name => logoutEventName = name;
const setRefreshTokenEndpoint = endpoint => refreshEndpoint = endpoint;
// This countdown feature is used to renew the JWT before it's no longer valid
// in a way that is transparent to the user.
const refreshToken = (delay) => {
refreshTimeOutId = window.setTimeout(
getRefreshedToken,
delay * 1000 - 5000
); // Validity period of the token in seconds, minus 5 seconds
};
const abordRefreshToken = () => {
if (refreshTimeOutId) {
window.clearTimeout(refreshTimeOutId);
}
};
const waitForTokenRefresh = () => {
if (!isRefreshing) {
return Promise.resolve();
}
return isRefreshing.then(() => {
isRefreshing = null;
return true;
});
}
// The method make a call to the refresh-token endpoint
// If there is a valid cookie, the endpoint will set a fresh jwt in memory.
const getRefreshedToken = () => {
const request = new Request(refreshEndpoint, {
method: 'GET',
headers: new Headers({ 'Content-Type': 'application/json' }),
credentials: 'include',
});
isRefreshing = fetch(request)
.then((response) => {
if (response.status !== 200) {
ereaseToken();
global.console.log(
'Token renewal failure'
);
return { token: null };
}
return response.json();
})
.then(({ token, tokenExpiry }) => {
if (token) {
setToken(token, tokenExpiry);
return true;
}
ereaseToken();
return false;
});
return isRefreshing;
};
const getToken = () => inMemoryJWT;
const setToken = (token, delay) => {
inMemoryJWT = token;
refreshToken(delay);
return true;
};
const ereaseToken = () => {
inMemoryJWT = null;
abordRefreshToken();
window.localStorage.setItem(logoutEventName, Date.now());
return true;
}
// This listener will allow to disconnect a session of ra started in another tab
window.addEventListener('storage', (event) => {
if (event.key === logoutEventName) {
inMemoryJWT = null;
}
});
return {
ereaseToken,
getRefreshedToken,
getToken,
setLogoutEventName,
setRefreshTokenEndpoint,
setToken,
waitForTokenRefresh,
}
};
export default inMemoryJWTManager();
This is my auth provider: (updated, using inMemoryJWTManager)
import inMemoryJWTManager from './inMemoryJWT'
const apiUrl = 'http://localhost:8000'
const authProvider = {
login: ({username, password}) => {
const oAuthParams = {
username,
password
}
const body = Object.keys(oAuthParams).map((key) => {
return encodeURIComponent(key) + '=' + encodeURIComponent(oAuthParams[key]);
}).join('&');
const request = new Request(`${apiUrl}/auth/jwt/login`, {
method: 'POST',
body: body,
headers: new Headers({'Content-Type': 'application/x-www-form-urlencoded'}),
});
return fetch(request)
.then(response => {
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
return response.json();
})
.then(( {access_token} ) => {
inMemoryJWTManager.setToken(access_token);
});
},
checkError: (error) => {
const status = error.status;
if (status === 401 || status === 403) {
inMemoryJWTManager.ereaseToken();
return Promise.reject({redirectTo: '/login'});
}
// other error code (404, 500, etc): no need to log out
return Promise.resolve();
},
checkAuth: () => inMemoryJWTManager.getToken()
? Promise.resolve()
: Promise.reject({ message: 'Login necessário', redirectTo: 'login' }),
logout: () => {
inMemoryJWTManager.ereaseToken();
return Promise.resolve();
},
getPermissions: () => {
return inMemoryJWTManager.getToken() ? Promise.resolve() : Promise.reject();
},
};
export default authProvider;
My updated httpClient code using inMemoryJWTManager: (and I'm using: const dataProvider = jsonServerProvider(apiUrl, httpClient); with modifications to it, but I think it is irrelevant)
const httpClient = (url) => {
const options = {
headers: new Headers({ Accept: 'application/json' }),
};
const token = inMemoryJWTManager.getToken();
console.log(token)
if (token) {
options.headers.set('Authorization', `Bearer ${token}`);
return fetchUtils.fetchJson(url, options);
} else {
inMemoryJWTManager.setRefreshTokenEndpoint(`${apiUrl}/auth/jwt/refresh`);
return inMemoryJWTManager.getRefreshedToken().then((gotFreshToken) => {
if (gotFreshToken) {
options.headers.set('Authorization', `Bearer ${inMemoryJWTManager.getToken()}`);
};
return fetchUtils.fetchJson(url, options);
});
}
};
My problem is that, when I call my refresh token endpoint, my request go without the {'Authorization': Bearer... and it is not renewed and I got logged out. The other endpoints are fine, they go with the token.
You must check token expire before each requests, if token expired you must get new from /auth/jwt/refresh, then you can send current request. All this information is in the article post. Example:
const httpClient = (url) => {
const options = {
headers: new Headers({ Accept: 'application/json' }),
};
const token = inMemoryJWT.getToken();
if (token) {
options.headers.set('Authorization', `Bearer ${token}`);
return fetchUtils.fetchJson(url, options);
} else {
inMemoryJWT.setRefreshTokenEndpoint('http://localhost:8001/refresh-token');
return inMemoryJWT.getRefreshedToken().then((gotFreshToken) => {
if (gotFreshToken) {
options.headers.set('Authorization', `Bearer ${inMemoryJWT.getToken()}`);
};
return fetchUtils.fetchJson(url, options);
});
}
};

axios response interceptor not working properly

Im trying to use axios interceptor with refresh token but when my token has expired. My error code within in interceptors.response is not executing. What can i do to fix this issue, i am also getting a status code of 200 which does not make much sense to me.
Also here is my code
proctedInstance.interceptors.request.use(
async config => {
const accesstoken = localStorage.getItem('accesstoken');
config.headers = {
'Authorization': `Bearer ${accesstoken}`,
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
}
return config;
},
error => {
Promise.reject(error);
}
)
proctedInstance.interceptors.response.use((response) => {
console.log(response);
return response
},
function (error) {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
return axios.post('http://localhost:4000/refresh_token')
.then(res => {
if (res.status === 200) {
localStorage.setItem('accesstoken', res.data.accesstoken)
console.log('my token res.data.accesstoken', res.data.accesstoken);
axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('accesstoken');
return axios(originalRequest);
}
})
}
return Promise.reject(error);
})
export const onProtected = async () => {
const results = await (await proctedInstance.post('/protected')).data
if(results.data === 'This is protected data.'){
return true;
} else
return false;
}
any help is appreciated
Try letting it like this:
proctedInstance.interceptors.response.use(
function (response) {
return response;
},
function (error) {
const access_token = localStorage.getItem("accesstoken");
if (error.response.status === 401 && access_token) {
//Your logic to refresh token and reattempt request
} else {
console.error(error);
}
return Promise.reject(error);
}
);

React-admin JWT authentication refresh token problem

I want to implement my own authProvider for react-admin but I'm stuck.
I use a Django-Rest-Framework backend and a JWT token authentication system.
I want to refresh the JWT token if it's almost expired before every request. According to the documentation the authProvider's checkAuth function gets called before every API call, which is true. My problem is that with my code it doesn't wait for the promise to finish and it uses the old access token which results in a 401 and I get redirected to the login page. Any guidance what am I missing?
import jwt from 'jsonwebtoken';
export default {
login: async ({ username, password }) => {
const request = new Request('http://localhost:8000/api/token/', {
method: 'POST',
body: JSON.stringify({ username, password }),
headers: new Headers({ 'Content-Type': 'application/json' }),
});
const response = await fetch(request);
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
const { refresh, access } = await response.json();
localStorage.setItem("refreshToken", refresh);
localStorage.setItem("accessToken", access);
},
logout: params => {
console.log("logout");
localStorage.setItem('accessToken', "");
localStorage.setItem('refreshToken', "");
return Promise.resolve();
},
checkAuth: (params) => {
const accessToken = localStorage.getItem('accessToken');
const refreshToken = localStorage.getItem('refreshToken');
if (accessToken && refreshToken) {
console.log(accessToken);
const { exp } = jwt.decode(accessToken);
if (exp > (new Date().getTime() / 1000) - 10) {
return Promise.resolve();
} else {
const request = new Request('http://localhost:8000/api/token/refresh/', {
method: 'POST',
body: JSON.stringify({ "refresh": refreshToken }),
headers: new Headers({ 'Content-Type': 'application/json' }),
});
const response = fetch(request)
.then(response => {
if (response.status !== 200) {
throw new Error(response.statusText);
}
return response.json();
})
.then(({ token }) => {
localStorage.setItem('accessToken', token);
return Promise.resolve();
});
return response;
}
}
return Promise.reject();
},
checkError: error => {
if (error.status === 401 || error.status === 403) {
return Promise.reject();
}
return Promise.resolve();
},
getPermissions: params => Promise.resolve(),
}
Can you try something like that
checkAuth: async (params) =>
And
const request = new Request(...);
let data;
const response = await fetch(request);
if (response.ok) data = await response.json()
else throw new Error(response.statusText);
if (data && data.token) {
localStorage.setItem('accessToken', data.token);
console.log(data.token);
return Promise.resolve();
} else return Promise.reject();

React - Login success redirect and auth headers

I have a login function that should set the user's credentials in sessionStorage upon successful login and then redirect to a new route, calling another api with the sessionStorage auth token added to the headers. For some reason the initial api call after successful login is failing because the auth token isn't added to the request headers. However, if I reload the page after the redirect the request header is added, resulting in a successful response. What is the correct way to save credentials to sessionStorage and configure all headers for subsequent requests.
components/Login.jsx
login(event) {
event.preventDefault();
this.props.dispatch(loginUser(this.state.creds));
}
actions/loginActions.js
export function loginUser(user) {
return function(dispatch) {
return LoginApi.login(user).then(creds => {
dispatch(loginUserSuccess(creds));
}).catch(error => {
throw(error);
});
};
}
export function loginUserSuccess(creds) {
sessionStorage.setItem('credentials', JSON.stringify(creds.data));
hashHistory.push('/packages');
return {
type: types.LOGIN_USER_SUCCESS,
state: creds.data
}
}
api/config.js
import axios from 'axios';
sessionStorage.credentials ? axios.defaults.headers.common['Authorization'] = 'Bearer ' + JSON.parse(sessionStorage.credentials).authToken : undefined;
api/packageApi.js
import './config';
import axios from 'axios';
class PackageApi {
static getAllPackages() {
return axios.get('/get/my/packages')
.then(function (response) {
console.log(response);
return response;
})
.catch(function (error) {
console.log(error);
});
}
}
Edit
// same behavior
sessionStorage.getItem('credentials') ? axios.defaults.headers.common['Authorization'] = 'Bearer ' + JSON.parse(sessionStorage.credentials).authToken : undefined;
// Fixed - api/config.js
import axios from 'axios';
const axiosInstance = axios.create();
axiosInstance.interceptors.request.use(
config => {
sessionStorage.getItem('credentials') ? config.headers['Authorization'] = 'Bearer ' + JSON.parse(sessionStorage.credentials).authToken : undefined;
return config;
},
error => Promise.reject(error)
);
export default axiosInstance;
// Fixed - api/packageApi.js
import axiosInstance from './config';
class PackageApi {
static getAllPackages() {
return axiosInstance.get('/get/my/packages')
.then(function (response) {
console.log(response);
return response;
})
.catch(function (error) {
console.log(error);
});
}
}
export default PackageApi;
Your config file is parsed and evaluated as soon as the browser loads it. At that time there is no token in your sessionStorage. What you need to do is to write a function which is only defined at parse time but then you call it at runtime to get the token:
class PackageApi {
static getAllPackages() {
const token = window.sessionStorage.getItem('credentials')
return axios.get('/get/my/packages', {
headers: {'Authorization': `Bearer ${ token }`}
})
.then(function (response) {
console.log(response);
return response;
})
.catch(function (error) {
console.log(error);
});
}
}
Here I did not define a function because sessionStorage.getItem is already a function but it doesn't stop you to write stuff like this:
const getFromStorage = key => {
const value = window.sessionStorage.getItem(key);
return JSON.parse(value);
}
axios.get('/get/my/packages', {
headers: {'Authorization': `Bearer ${ getFromStorage('credentials') }`}
})

Resources