Axios request interceptor not working on browser refresh - reactjs

I am using axios interceptor in my react app to pass the token for each request.
I initially call the setupAxiosInterceptors method after I login (See code below). This works perfectly fine until I refresh the browser.
const registerSucessfulLoginForJwt = (username, token) => {
sessionStorage.setItem(USER_NAME_SESSION_ATTRIBUTE_NAME, username)
setupAxiosInterceptors(createJwtAuth(token)) //Calling the axios interceptor at the time of login
}
See below the setupAxiosInterceptors method
const setupAxiosInterceptors = (token) => {
Axios.interceptors.request.use((config) => {
if(isUserLoggedIn()) {
config.headers.authorization = token
sessionStorage.setItem('authorization', token)
}
return config
})
}
Any thought on how to fix this so it works at all time?

I was able to find a solution to my problem. I create an ApiSetup.js file where I create a custom axios instance which could use for all requests.
const request = axios.create({
baseURL: API_PATH_BASE
})
request.interceptors.request.use(config => {
const currentUser = AuthenticationService.getLoggedInUser() //You can get the user directly from the cookie or session storage...
if(currentUser.userName) {
config.headers.Authorization = currentUser.userToken
}
return config
}, err => {
console.log(err)
return Promise.reject(err)
})
export default request

Related

Logic recommendation for React refreshing token

Can anyone suggest me a good logic to automatically refresh my accessToken?
At the moment, I have an OpenAPI generated class, where the accessToken is a promise in all the requests. In this promise, I check if the token is expired and I fetch the new one, based on a refresh token.
I also have an AuthContext, that I use to manage my authtentication, user details, etc. (saving to localstorage, etc)
The problem is that I need somehow to access my AuthContext and to give it the new token or to logout if the token could not be refreshed, in my API class.
I do receive an error on the following code, but this is expected:
Invalid hook call. Hooks can only be called inside of the body of a function component.
My access token as promise (inside the API class)
accessToken: new Promise<string>(async (resolve, reject) => {
const tokenExpiresAt = Date.parse(model.tokenExpiration);
const {saveAuth, logout} = useAuth() // Here is the problem
// if token is expired, refresh it
if (tokenExpiresAt < Date.now()) {
this.Auth.refresh({accessToken: model.token, refreshToken: model.refreshToken})
.then((response) => {
// save new auth
saveAuth(response.data)
resolve(response.data.token);
})
.catch((error) => {
// error refreshing token, logout
logout()
reject("Token expired");
});
}
resolve(model.token);
})
I faced a similar problem a while ago, yes you cannot call a hook inside a hook so another approach to solve this issue is to intercept the axios response interceptor.
axiosApiInstance.interceptors.response.use((response) => {
return response
}, async function (error) {
const originalRequest = error.config;
if (error.response.status === 403 && !originalRequest._retry) {
originalRequest._retry = true;
const refreshToken = localStorage.getItem('refreshToken');
const access_token = await refreshAccessToken(refreshToken);
axios.defaults.headers.common['Authorization'] = 'Bearer ' + access_token;
return axiosApiInstance(originalRequest);
}
return Promise.reject(error);
});
And you can implement your own refreshAccessToken function according to your usecase.

Add default axios header after login nextjs

I'm using Next.js for my app, and currently have an API route that sets a JWT as a cookie. Throughout the app, I'm using Axios to fetch all of my data from external APIs, and after a user logs in I need to set that cookie as a default request header on every API call to make sure that a user has been authenticated. The basic flow is like this:
The login form sends a post request to my API route at /api/auth/login, passing the username and password and returning the JWT, and setting it as a cookie. Once the idToken cookie has been set I need to add that as an authentication header to every API request within my Axios instance, seen as adapter here. How can I go about getting this done?
My handle login function:
const handleLogin = async (values: ValuesProps) => {
const response = await axios.post('/api/auth/login', values);
if (response.status !== 200) {
throw new Error(response.statusText);
}
};
Which speaks to api/auth/login:
import { NextApiRequest, NextApiResponse } from 'next';
import { setCookie, parseCookies } from 'nookies';
import { adapter } from 'utils/api/config';
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method !== 'POST') {
res.status(500).json('Only POST requests allowed at this route.');
} else {
const { data } = await adapter.post(AUTH.login, JSON.stringify(req.body));
const cookies = parseCookies();
setCookie({ res }, 'idToken', data.token, {
maxAge: 30 * 24 * 60 * 60,
path: '/',
});
api.defaults.headers.Authorization = `Bearer ${cookies['idToken']}`
res.status(200).json(data);
}
};
export default handler;
As you see here I tried adding adapter.defaults.headers.Authorization as a global default, but I'm not seeing it in my request headers. What's the best way to go about setting this globally?
You could use axios.create. It is a factory that creates new instances of axios. So you write a function
import axios from "axios";
export const axiosInstance = async () =>{
// you need to be careful in next.js for adding cookies.
// You could be on the server or on client. this code will work for client assuming that you will be using on client side
// I belive you are using `parser` to get cookies. get the token
const yourToken="whatever"
const axiosClient = axios.create({
baseURL: 'baseUrlHere',
timeout: 1000,
headers: {
'Accept': 'application/vnd.GitHub.v3+json',
// this is how u set in your code
'Authorization': `Bearer ${cookies['idToken']}`
}
});
return axiosClient
}
Then import this in anywhere you want to use:
const {data}=await axiosInstance().post("/auth")
Technically this should work
You can set default header to all axios request by command:
const token = getCookie('token')
axios.defaults.headers.common["idToken"] = token

Token is Invalid in Reactjs application

I try to get a list from the backend in Reactjs component with JWT token but I get an error message {"status":"Token is Invalid"}, please guide me.
My backend API is working fine and my token is saved in the localstore after login.
my frontend used API code
import {API} from "../config";
/**
* to get about pages
* get a single about page
* update a about page
* delete about page
*/
export const getAboutContent = (token) =>{
return fetch(`${API}/about/?token=${token}`, {
method: "GET",
})
.then(response =>{
return response.json();
})
.catch(err =>{
console.log(err);
});
};
about/index.js
const [allAboutContent, setAllAboutContent] = useState([]);
const loadAllAboutContent = () => {
getAboutContent().then(data => {
if(data.error){
console.log(data.error)
} else{
setAllAboutContent(data.data)
}
});
};
useEffect(() =>{
loadAllAboutContent();
}, [])
Please help.
You are invoking getAboutContent in about/index.js file without JWT and hence it not defined. Just update your code to read JWT from localStorage like below
const loadAllAboutContent = () => {
// Read token from local storage
const token = localStorage.getItem('jwt');
// Pass to getAboutContent
getAboutContent(token).then(data => {
if(data.error){
console.log(data.error)
} else{
setAllAboutContent(data.data)
}
});
};
Also, I see you have stored your token as {token: ''}. Maybe, you can directly save it. Otherwise you have to read it like this JSON.parse(localStorage.getItem('jwt')).token

how to post headers with axios in react

Hi i have this problem:
i made and API with an auth JWT runs perfect and my front is in react so i need only one component its a simple app, so in my App.jsx i have my axios post to get the token and then i pass my token through the component, then i receive the props in the component and i saw the token but when i pass to the header like i test in postman, so nothing happens, and if i try the front and in the API i disable the JWT in my route works perfectly so i let my code next
const Cards = (props) => {
//console.log(props.auth.token);
//axios connection
const apiCall = async () =>{
let config = {
headers: {
'Authorization': `Bearer ${props.auth.token}`
}
}
console.log(config);
try {
const res = await clientAxios.post('/api/games', config,
{
console: 'nintendo',
game: 'super mario',
duration: '60hs'
},)
console.log(props.auth.token);
} catch (error) {
console.log(error.status);
}
}
apiCall();
Instead of sending the token with each request, set the token in axios' default headers like so:
window.axios.defaults.headers.common['Authorization'] = `Bearer ${jwtToken}`;

React-Adal returns 401 with POST request but 200 with GET request

I'm trying to send a POST request to an API that is hosted in Azure and is authenticated through Azure Active Directory. I'm using React with React-Adal to send my requests. I configured react-adal using the GitHub repo and this tutorial to guide me.
adalConfig.js
import { AuthenticationContext, adalFetch, withAdalLogin, adalGetToken } from 'react-adal';
export const adalConfig = {
tenant: 'ad5842d4-1111-1111-1111-111111111111',
clientId: '1f89aa20-1111-1111-1111-111111111111', //ClientID of the ReactClient application
endpoints: {
demoApi: 'e7926712-1111-1111-1111-111111111111', //ClientID of the DemoApi
microsoftGraphApi: 'https://graph.microsoft.com'
},
postLogoutRedirectUri: window.location.origin,
redirectUri: 'https://localhost:44394/',
cacheLocation: 'sessionStorage'
};
export const authContext = new AuthenticationContext(adalConfig);
export const adalDemoApiFetch = (fetch, url, options) =>
adalFetch(authContext, adalConfig.endpoints.demoApi, fetch, url, options);
export const adalTokenFetch = () =>
adalGetToken(authContext, adalConfig.endpoints.demoApi);
export const withAdalLoginApi = withAdalLogin(authContext, adalConfig.endpoints);
When I use the adalDemoApiFetch with a GET request it works fine and returns 200 with the list of schedules.
const url = `https://localhost:44322/api/Schedules/GetAllSchedules`;
const response = await adalDemoApiFetch(axios.get, url);
console.log(response);
const schedules = response.data;
When I use the same adalDemoApiFetch with a POST to add a new schedule to the list it returns a 401.
const url = `https://localhost:44322/api/Schedules/AddSchedule`;
const azureADID = authContext.getCachedUser();
const token = authContext.acquireToken("e7926712-1111-1111-1111-111111111111");
console.log(token);
const options = {
beginningDateTime: this.state.begDateTime.toJSON(),
endindDateTime: this.state.endDateTime.toJSON(),
userID: this.state.userID,
azureADID: azureADID.profile.oid
};
const response = await adalDemoApiFetch(axios.post, url, options);
console.log(response);
I also tried copying out the token and using it in Postman to make the call and it still returns 401. When I use the token that is returned from the function below it works just fine.
export const adalTokenFetch = () =>
adalGetToken(authContext, adalConfig.endpoints.demoApi);
I use axios to call it in the code below and it works just fine.
const url = `https://localhost:44322/api/Schedules/AddSchedule`;
const azureADID = authContext.getCachedUser();
const token = await adalTokenFetch();
console.log(token);
const options = {
beginningDateTime: this.state.begDateTime.toJSON(),
endindDateTime: this.state.endDateTime.toJSON(),
userID: this.state.userID,
azureADID: azureADID.profile.oid
};
const response = await axios.post(url,
{
data: options
},
{
headers: {
"Authorization": `Bearer ${token}`
}
}
);
console.log(response);
What am I doing wrong? Why would it work with a GET request and not with the POST request? Am I missing something?
I tried working with Axios too. Seems that using axios it is unable to authorize the bearer token. Try using the Adalfetch that fixed the problem for me.
You need to tell adalApiFetch you are using POST method instead of GET. It defaults to GET, that is why that works ootb. Use the options object.

Resources