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});
}
}
Related
I am following the stripe docs about validating webhooks and despite I do everything as they do i keep getting 400 error. BTW in the docs here https://stripe.com/docs/webhooks/signatures they don't return from the catch blok but here https://stripe.com/docs/webhooks/quickstart they do, so I assyme that the correct option is to return from it? And back to my main problem I have no idea what am I missing here this is my code:
import { NextApiHandler } from "next";
import { Stripe } from "stripe";
import { apolloClient } from "../../graphql/apolloClient";
import {
UpdateOrderDocument,
UpdateOrderMutation,
UpdateOrderMutationVariables,
} from "../../generated/graphql";
import type { StripeWebhookEvents } from "../../stripeEvents";
const stripeWebhookHandler: NextApiHandler = (req, res) => {
const webhookSignature = req.headers["stripe-signature"];
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
const stripeSecret = process.env.STRIPE_SECRET_KEY;
if (!stripeSecret || !webhookSignature || !webhookSecret) {
return res.status(500).json({ error: "Stripe credential not provided" });
}
const stripe = new Stripe(stripeSecret, { apiVersion: "2020-08-27" });
let event;
try {
event = stripe.webhooks.constructEvent(
req.body,
webhookSignature,
webhookSecret
) as StripeWebhookEvents;
} catch (err: unknown) {
return res
.status(400)
.send(`Webhook Error: ${err instanceof Error && err.message}`);
}
switch (event.type) {
case "charge.succeeded":
apolloClient.mutate<UpdateOrderMutation, UpdateOrderMutationVariables>({
mutation: UpdateOrderDocument,
variables: {
id: {
id: event.data.object.metadata.cartId,
},
data: {
stripeCheckoutId: event.data.object.id,
email: event.data.object.receipt_email,
},
},
});
break;
}
res.status(204).end();
};
export default stripeWebhookHandler;
and I thought that maybe next has maybe different shape of req.headers or req.body and I am not sure abouut req body, headers seem to be in tact however. At least the signature seems to be extracted correctly. Dhose are test data of course:
{
webhookSignature: 't=1658224240,v1=a3f574b3e6c3a02eb86308e5e43f3d0a96664098ee5dd58859fc94e96693fc50,v0=ef29de87716d9d318d6ad960f028fd5960618c853ff686bd44e261aaa2368f3b',
webhookSecret: 'whsec_0d8a54d09bf221f7c5c77ca7a3fca4b988ccd9e49d8a31d7c91f854025503fe4',
stripeSecret: 'sk_test_51Kuvp4KsMpogemXo9vUcgihi1vK4dlof76OL4EcYhmVgN8r81tl7r0rSsqWgOtXxYnZPJlo6S2KA0gFWZmyBQIbS00ABzicwum',
headers: {
host: 'localhost:3000',
'user-agent': 'Stripe/1.0 (+https://stripe.com/docs/webhooks)',
'content-length': '2818',
accept: '*/*; q=0.5, application/xml',
'cache-control': 'no-cache',
'content-type': 'application/json; charset=utf-8',
'stripe-signature': 't=1658224240,v1=a3f574b3e6c3a02eb86308e5e43f3d0a96664098ee5dd58859fc94e96693fc50,v0=ef29de87716d9d318d6ad960f028fd5960618c853ff686bd44e261aaa2368f3b',
'accept-encoding': 'gzip'
}
is there something I do incorrectly here? Thanks a lot
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 set up JWT to be set in localstorage whenever someone logins or registers. And it works, I can see the token in localstorage. But when I set the token in the headers with axios, node.js in the backend can`t find the token. Like it does not exists. I have checked it in the front end, I get logs of the token in the headers. And also when I request from postman it works. Here is the code.
setAuthToken function = {
const instance = axios.create({
baseURL: "https://localhost:5000",
});
if (token) {
instance.defaults.headers.common["x-auth-token"] = `${token}`;
console.log(instance.defaults.headers.common["x-auth-token"]);
} else {
delete instance.defaults.headers.common["x-auth-token"];
}
}
const loadUser = async () => {
if (localStorage.token) setAuthToken(localStorage.token);
console.log(localStorage.token);
try {
const res = await axios.get("/api/users");
console.log(res);
dispatch({ type: USER_LOADED, payload: res.data });
} catch (err) {
console.log(err.response.data.msg);
dispatch({ type: AUTH_ERROR });
}
The request comes to the await axios statement and goes to catch so error is in the request.
Here is the backend code
// Get current user
router.get("/", auth, async (req, res) => {
try {
const user = await User.findById(req.user.id);
res.status(200).json({ user });
} catch (err) {
console.log(err);
res.status(500).json({ msg: `Server Error` });
}
});
auth middleware function here = {
const token = req.headers["x-auth-token"];
console.log(token, "token in auth.js");
console.log(req.headers, "req.header");
if (!token) {
return res.status(401).json({ msg: `Access denied.` });
}
try {
const decoded = jwt.verify(token, config.get("jwtSecret"));
req.user = decoded.user;
next();
} catch (err) {
res.status(401).json({ msg: `Token is not valid` });
}
}
I`m new to backend develoment and axios. Can someone help me please. Thank you
Here are the console.logs
Logs
Logs
Little update, it looks like there is a problem with proxy, I am using my own backend api, and also movie data base api. So maybe thats why I cant set headers? Here are new logs:
config: Object { url: "/api/users", method: "get", timeout: 0, … }
data: "Proxy error: Could not proxy request /api/users from localhost:3000 to http://localhost:5000/ (ECONNREFUSED)."
headers: Object { connection: "keep-alive", date: "Wed, 05 May 2021 13:18:05 GMT", "keep-alive": "timeout=5", … }
request: XMLHttpRequest { readyState: 4, timeout: 0, withCredentials: false, … }
status: 500
statusText: "Internal Server Error
I think the issue is because you are setting you are setting up your instance wrongly
set up your instance in a new file config.js -
import Axios from 'axios';
const baseURL = "http://localhost:5000";
const axiosInstance = Axios.create({
baseURL: baseURL,
});
axiosInstance.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);
}
);
export default axiosInstance;
now when making any api request instead of using axios use axiosInstance eg-
axiosInstance.get('/something').then(res => console.log(res)).catch(err => console.log(err))
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
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.