I am using a JWT Token auth system, and when I login I get the token like this:
axios.post('/login', data)
.then(response => {
localStorage.setItem('token', response.data.token);
});
This works well and the token is saved in localStorage. However, the token is not included in the later requests. The Authorization header is Bearer null.
This is how I set up my global axios object.
window.axios = axios.create({
baseURL: '/api/',
timeout: 10000,
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': document.head.querySelector('meta[name="csrf-token"]').content,
'Authorization': 'Bearer ' + localStorage.getItem('token')
}
});
If I refresh the site, the token is set, and is used properly.
Edit:
I got it to work by removing the Authorization header from the create() method and instead using window.axios.defaults.headers.common['Authorization']. But now the same problem appears with Laravel Echo. I create the instance like this:
window.Echo = new Echo({
broadcaster: 'pusher',
key: 'xxx',
cluster: 'eu',
encrypted: true,
namespace: 'xxx',
auth: {
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('token')
}
}
});
And I update the header like this:
window.setAuthToken = (token) => {
window.axios.defaults.headers.Authorization = 'Bearer ' + token;
window.Echo.options.auth.headers.Authorization = 'Bearer ' + token;
localStorage.setItem('token', token);
}
The axios header is successfully updated, but not Echo.
Use axios interceptors for this purpose. It will run for every request call.
Better to keep axios methods in a separate file and make call to it than using it directly in all components. This way we can replace axios with another library if we want with minimal effort. Here's what I'm doing in my project.
import axios from "axios";
import AuthService from "./auth";
import config from '../config'
const instance = axios.create({
baseURL: config.apiServer.url,
timeout: config.apiServer.timeout
});
instance.interceptors.request.use(
config => {
const token = AuthService.getToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
error => Promise.reject(error)
);
const ApiService = {
get(url) {
return instance.get(url)
.then(res => res)
.catch(reason => Promise.reject(reason));
},
post(url, data) {
return instance.post(url, data)
.then(res => res)
.catch(reason => Promise.reject(reason));
},
awaitAll() {
return axios.all(Array.from(arguments))
.then(axios.spread((...responses) => responses))
.catch(reasons => Promise.reject(reasons));
}
};
export default ApiService;
Now to use it in a component:
ApiService.get(YOUR_GET_URL)
.then(res => {
Console.log(res);
))
.catch(reason => {
console.log(reason);
})
The problem is that your are using localStorage.getItem('token') at page load. When you are setting it in localStorage, you have to update it in axios header.
window.axios = axios.create({
baseURL: '/api/',
timeout: 10000,
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': document.head.querySelector('meta[name="csrf-token"]').content,
'Authorization': 'Bearer ' + localStorage.getItem('token')
}
});
axios.post('/login', data)
.then(response => {
localStorage.setItem('token', response.data.token);
window.axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('token');
});
I faced the same problem before and I found out that the file that contains my axios config was being loaded at the time of storing the token, so it was accessing it before it is stored.
The solution is, in axios config:
const axiosInstance = axios.create({
baseURL: `${API_BASE_URL}`,
headers: {
Accepted: 'appication/json',
'Content-Type': 'application/json',
},
});
axiosInstance.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.authorization = token;
}
return config;
},
(error) => Promise.reject(error),
);
export default axiosInstance;
After that, use this instance where you need to make a request.
Related
On my front-end I have this Axios code:
import axios from "axios";
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
const apiUrl = process.server ? `${process.env.FRONT}api/` : '/api/';
const api = axios.create({
baseURL: apiUrl,
headers: {
'Content-Type': 'application/json'
}
})
api.interceptors.request.use(function (config) {
const token = localStorage.getItem('token')
if (token) {
config.headers.common['Authorization'] = 'Bearer ' + token
}
return config;
}, function (error) {
return Promise.reject(error);
});
export const login = async (payload) => {
const { data } = await api.post(`login`, payload)
return data
}
What this code does is just set some headers with token. Then, this request goes to front-end server:
router.post(`/login`, async (req, res) => {
try {
const data = await api.post('/login', req.body)
res.json(data.data)
} catch (e) {
res.status(e.response.status).json(e.response.data)
}
})
If you do console.log(req.headers) here, it's gonna be okay, headers will look like this:
req.headers {
accept: 'application/json, text/plain, */*',
'content-type': 'application/json',
host: 'localhost:8010',
connection: 'keep-alive',
'content-length': '898',
authorization: 'Bearer ...token...',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36',
...
}
And here is the problem, after that, when request goes to back end, it's captured by middleware auth:
router.post('/login', auth, accountController.login)
This is how this middleware looks like:
import * as jwtService from './../services/jwtService';
import { Request, Response } from 'express';
import { CommonResponse } from "../responses/response";
export default async (req: Request, res: Response, next: any) => {
try {
if (req.headers.authorization) {
const user = await jwtService.getUser(req.headers.authorization.split(' ')[1])
if (user) next();
else return CommonResponse.common.unauthorized({ res })
} else {
return CommonResponse.common.unauthorized({ res })
}
} catch (e) {
return CommonResponse.common.unauthorized({res});
}
}
And here is the problem, if you do console.log(req.headers) here you'll see, this:
{
accept: 'application/json, text/plain, */*',
'content-type': 'application/json',
'user-agent': 'axios/0.26.0',
'content-length': '898',
host: 'localhost:3000',
connection: 'close'
}
What happened to my headers?
By the way, if on front end server you do this:
const data = await api.post('/login', req.body, {headers: req.headers})
This is going to work, but still, what happened? Is this how interceptors should work?
Change your code of interceptors as follows:
api.interceptors.request.use(function (config) {
const token = localStorage.getItem('token')
if (token) {
config.headers['Authorization'] = 'Bearer ' + token
}
return config;
}, function (error) {
return Promise.reject(error);
});
And in middleware you should use Authorization instead of authorization like:
import * as jwtService from './../services/jwtService';
import { Request, Response } from 'express';
import { CommonResponse } from "../responses/response";
export default async (req: Request, res: Response, next: any) => {
try {
if (req.headers.Authorization) { // correct this line
const user = await jwtService.getUser(req.headers.Authorization.split(' ')[1]) // correct this line
if (user) next();
else return CommonResponse.common.unauthorized({ res })
} else {
return CommonResponse.common.unauthorized({ res })
}
} catch (e) {
return CommonResponse.common.unauthorized({res});
}
}
And make sure that you have token in localStorage too and provide feedback if still facing issue.
There seems to be no issue with code.
Try this demo and it works fine.
https://github.com/indolent-developer/axiosDemo
Most probably you are having issue with localstorage. Generally I like to avoid if without else. Can you can some console logs and see it is working fine.
Maybe the reason behind it is that you're assigning to request.headers.common.
It works for me when I assign to req.headers
api.interceptors.request.use(
(req) => { // I'm using req instead of config just for clarity
req.headers['Authorization'] = `token ${token}`
return req;
},
(err) => {
return Promise.reject(err);
}
);
In your server side, change req.headers.authorization to req.headers.Authorization
import * as jwtService from './../services/jwtService';
import { Request, Response } from 'express';
import { CommonResponse } from "../responses/response";
export default async (req: Request, res: Response, next: any) => {
try {
if (req.headers.Authorization) {
const user = await jwtService.getUser(req.headers.authorization.split(' ')[1])
if (user) next();
else return CommonResponse.common.unauthorized({ res })
} else {
return CommonResponse.common.unauthorized({ res })
}
} catch (e) {
return CommonResponse.common.unauthorized({res});
}
}
This is my global Axios
import axios from 'axios';
import { storage } from 'containers/login/utils/local-storage';
const token = storage.getToken();
const axiosInstance = axios.create({
baseURL: process.env.REACT_APP_API_URL,
});
axiosInstance.interceptors.response.use((response) => {
response.config.headers = {
Authorization: `Bearer ${token}`,
};
return response;
}, (error) => Promise.reject(error));
axiosInstance.interceptors.request.use((request) => {
request.headers = {
Authorization: `Bearer ${token}`,
};
return request;
}, (error) => Promise.reject(error));
export default axiosInstance;
In this request I need to add new header: invoiceLimit = `${-invoiceLimit}`
export const updateInvoiceLimit = async (
invoiceLimit: string,
)
: Promise<ReturnDataType> => {
let result: ReturnDataType = {} as ReturnDataType;
try {
axios.defaults.headers.common.invoiceLimit = `${-invoiceLimit}`;
result = await axios.put(`${CREDITS_URL.CREDITS}/invoice/limit`);
return result;
} catch (error) {
SnackBarUtils.error(`${(error as Error).message}. ${result.data.message}`);
}
return result;
};
When I use this: axios.defaults.headers.common.invoiceLimit = `${-invoiceLimit}`;
header adds to the Axios defaults, but then when I call axios.put so this custom header goes away and left only global header from interceptors.
I know it's not best practice, but its customer API and I want not to make another instance of Axios but use one global instance.
I think the problem is here:
axiosInstance.interceptors.request.use((request) => {
request.headers = {
Authorization: `Bearer ${token}`,
};
return request;
}, (error) => Promise.reject(error));
You are overriding all request headers with just this one:
{ Authorization: `Bearer ${token}` }
So try spreading headers before adding the new one like this:
axiosInstance.interceptors.request.use((request) => {
request.headers = {
...request.headers,
Authorization: `Bearer ${token}`,
};
return request;
}, (error) => Promise.reject(error));
Did this solve the problem?
I thought you could just reuse the initial axiosInstance and the headers will be merged when you do:
await axios.put(`${CREDITS_URL.CREDITS}/invoice/limit`,{}, {
headers: {
invoiceLimit: `${-invoiceLimit}`
}
})
I have managed to make this run: How to modify axios instance after exported it in ReactJS?
And it looks like this:
import axios from 'axios';
import constants from '../constants.js';
import Cookies from 'js-cookie';
const API = axios.create({
baseURL: `${constants.urlBackend}`,
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
});
API.interceptors.request.use(
config => {
var accesstoken = Cookies.get('accesstoken');
if (accesstoken) {
config.headers.Authorization = `Bearer ${accesstoken}`;
} else {
delete API.defaults.headers.common.Authorization;
}
return config;
},
error => Promise.reject(error)
);
export default API;
And this is an example usage
getUserList() {
API.get('/userlist')
.then(response => {
this.setState({
userList: response.data
}, () => {
console.log(this.state.userList)
});
})
}
But now im confused because I dont understand how to use this with a post so I can pass some data to it, similar to this
axios({
method: 'post',
url: constants.urlBackend + "/register",
data: qs.stringify({ email, password }),
headers: {
'content-type': 'application/x-www-form-urlencoded;charset=utf-8'
}
})
But using the above object.
API.post('/user/update/'+this.state.rowId).then(response => {
//some sort of body {email,password}
})
Have you tried
API.post(
'/user/update/' + this.state.rowId, {
email,
password
}).then(response => {})
I want to use axios in api test. To initialize client I need first to set auth token, which I expect to get with axios as well.How to get it form async code?
const a = require('axios');
getToken = () => {
var axios =a.create({
baseURL: 'http://local.v2.app.com/api/auth/v1.0',
headers: {'Content-Type': 'application/json'},
});
return axios.post('/credentials/login', {
username: '1#test.com',
password: 'Test#1234'
})
.then(function (response) {
return response.data.data.token;
})
.catch(function (error) {
console.log(error);
});
}
const client=a.create({
baseURL: 'http://local.v2.app.com/api/auth/v1.0',
headers: {
'Content-Type': 'application/json',
'Authorization': getToken()
},
});
module.exports = client;
First, getToken should be async (or just return promise):
async getToken() {
var axios = a.create({
baseURL: 'http://local.v2.app.com/api/auth/v1.0',
headers: {'Content-Type': 'application/json'},
});
try {
const response = await axios.post('/credentials/login', {
username: '1#test.com',
password: 'Test#1234'
})
return response.data.data.token;
} catch (error) {
console.error(error);
}
}
Then you can mock request:
const client = a.create({
baseURL: 'http://local.v2.app.com/api/auth/v1.0',
headers: {
'Content-Type': 'application/json'
}
})
const request = client.request
const addToken = (token) => (data, headers) => {
headers.Authorization = token
return data
}
client.request = (config = {}) => new Promise(async (resolve, reject) => {
token = await getToken()
if (!config.transformRequest) config.transformRequest = []
config.transformRequest.push(addToken(token))
request(config).then(resolve, reject)
})
module.exports = client
Yes, bit messy, feel free to refactor!
axios has request function which makes requests, others like get, post are aliases. This code patches request to get token first and then continue request.
transformRequest is took from axious readme on github, there is a comment "You may modify the headers object" so it is legal.
P.S good question, don't know why -1
This is the system flow:
I log in a save the user data in localstorage.
I got a component
that read the localStorage to get a user data, like JWT.
The problem is when I do a new login, the localStorage don't update with the new data and so I can't make requisitions that use the localStorage new user data.
I found that if I update manually the page, the localStorage it's udpdated.
In the reactjs I'm using react-router-dom to navigate with the pages.
How can I get new login data to update localStorage?
//login
api.post('users/login', {email, password})//make log in
.then(res => {
localStorage.removeItem('user_info');// I remove the ond data from localStorage
if(res){//If api do the post
let data = JSON.stringify(res.data.data)
localStorage.setItem('user_info', data);//Save new data in localStorage
history.push('/');//Go the homepage
}
}).catch(error => {
this.setState({ noAuth: true })
console.log('error');
});
The component that got the user data in localStorage:
import axios from "axios";
import { isAuthenticated } from '../../components/Util/Auth';
export const token = 'Z31XC52XC4';
// var user_info = JSON.parse(localStorage.getItem('user_info'));
// var auth_token = user_info.auth_token;
// console.log(isAuthenticated);
if (isAuthenticated()) {
var user_info = JSON.parse(localStorage.getItem('user_info'));
var auth_token = user_info.auth_token;
}
const api = axios.create({
// baseURL: 'https://url-from/api/', //PRODUÇÃO
baseURL: 'https://url-from/api/', //DESENVOLVIMENTO
headers: {
'Accept': 'application/json, text/plain, */*',
'Access-Origin': 'D',
'Authorization': `Bearer ${auth_token}`,
'Company-Token': `${token}`
}
});
// console.log(user_info);
api.interceptors.request.use(
config => {
console.log(config.headers);
console.log(user_info);
const newConf = {
...config,
headers: {
...config.headers,
'Authorization': `Bearer ${auth_token}`,
}
}
// return newConf;
},
error => Promise.reject(error)
)
export default api;
When you create an Axios instance you are passing in a "static" value.
'Authorization': `Bearer ${auth_token}`,
if this value doesn't exist, it becomes
'Authorization': `Bearer undefined`,
To fix it, you need to add an interceptor to axios to update the value of that token on EVERY request, not just on instance creation.
api.interceptors.request.use(
config => {
const user_info = JSON.parse(localStorage.getItem('user_info'));
const newConf = {
...config,
headers: {
...config.headers,
'Authorization': `Bearer ${user_info.auth_token}`,
}
}
},
error => Promise.reject(error)
)
I can do this using the tip of my friend JCQuintas
import axios from "axios";
import { isAuthenticated } from '../../components/Util/Auth';
export const token = 'Z31XC52XC4';
const api = axios.create({
// baseURL: 'https://url-from/api/', //PRODUÇÃO
baseURL: 'https://url-from/api/', //DESENVOLVIMENTO
headers: {
'Accept': 'application/json, text/plain, */*',
'Access-Origin': 'D',
// 'Authorization': `Bearer ${auth_token}`,
'Company-Token': `${token}`
}
});
api.interceptors.request.use(function (config) {
console.log(config);
if (isAuthenticated()) {
var user_info = JSON.parse(localStorage.getItem('user_info'));
var auth_token = user_info.auth_token;
}
config.headers.Authorization = `Bearer ${auth_token}`;
return config;
});
export default api;