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))
Related
I'm currently building an app using React Native on the frontend and Node.js together with express on the backend.
I have two axios instances, one of which I call the main server and the other the authentication server. With both instances all normal server calls outside of the following file are working expect this one. I'm currently localhosting both servers.
My frontend looks like this:
import axios from 'axios';
import AsyncStorage from '#react-native-async-storage/async-storage';
const BASE_URL = 'URL_PLACEHOLDER'; // <- IP Address of my computer
export const instanceMain = axios.create({
baseURL: `${BASE_URL}:3000`,
timeout: 1000,
});
export const instanceAuth = axios.create({
baseURL: `${BASE_URL}:4000`,
});
// Interception to check if a token refresh is needed
instanceMain.interceptors.request.use(async function (response) {
const accessToken = await AsyncStorage.getItem('accessToken');
const refreshToken = await AsyncStorage.getItem('refreshToken');
await instanceAuth
.post(`/token`, {
accessToken: accessToken,
refreshToken: refreshToken,
})
.then((res) => {
AsyncStorage.setItem('accessToken', res.data.accessToken);
AsyncStorage.setItem('refreshToken', res.data.refreshToken);
})
.catch((error) => {
console.log(error);
});
return response;
});
My backend of the post axios is trying to make looks like this:
app.post('/token', async (req, res) => {
try {
const accessToken = req.body.accessToken;
const refreshToken = req.body.refreshToken;
if (accessToken == null || accessToken == undefined)
return res.sendStatus(401);
// Verifying AccessToken
jwt.verify(accessToken, process.env.ACCESS_TOKEN_SECRET, (error) => {
// If it is expired this should be executed. I think I'm probably handling this
// the wrong way, but this was the only way I could think of.
if (error) {
// tokenDB is an in-storage json database containing all active refresh tokens
tokenDB.read();
tokenDB.data ||= { tokens: [] };
if (refreshToken == null) return res.sendStatus(401);
// Checking if tokenDB does not contain the refresh token, if so, return 403
if (!tokenDB.data.tokens.includes(refreshToken)) {
return res.sendStatus(403);
}
// Verifying RefreshToken
jwt.verify(
refreshToken,
process.env.REFRESH_TOKEN_SECRET,
(error, account) => {
// Deleting iat from account, otherwise the new tokens would be the same
// as before
delete account.iat;
if (error) return res.sendStatus(403);
// Generating new Tokens
const newAccessToken = generateAccessToken(account);
const newRefreshToken = generateRefreshToken(account);
// Removing old refreshToken from tokenDB and push newRefreshToken to it
tokenDB.data.tokens = tokenDB.data.tokens.filter(
(token) => token !== refreshToken
);
tokenDB.data.tokens.push(newRefreshToken);
// Result: new Access and Refresh token
res.json({
accessToken: newAccessToken,
refreshToken: newRefreshToken,
});
tokenDB.write();
}
);
} else {
return res.sendStatus(100);
}
});
} catch (error) {
console.error(error);
}
});
The error message simply says [AxiosError: Network Error]. error.config gives back the following, but I can't do much with it.
{"adapter": [Function xhrAdapter], "baseURL": "IP_PLACEHOLDER:4000", "data": "{\"accessToken\":\"ACCESS_TOKEN_PLACEHOLDER",\"refreshToken\":\"REFRESH_TOKEN_PLACEHOLDER"}", "env": {"Blob": [Function Blob], "FormData": [Function FormData]}, "headers": {"Accept": "application/json, text/plain, */*", "Content-Type": "application/json"}, "maxBodyLength": -1, "maxContentLength": -1, "method": "post", "timeout": 0, "transformRequest": [[Function transformRequest]], "transformResponse": [[Function transformResponse]], "transitional": {"clarifyTimeoutError": false, "forcedJSONParsing": true, "silentJSONParsing": true}, "url": "/token", "validateStatus": [Function validateStatus], "xsrfCookieName": "XSRF-TOKEN", "xsrfHeaderName": "X-XSRF-TOKEN"}
I hope I can find some help here, Thanks.
Use redux and proper async/await function to use. Otherwise, Axios return an error.
enter link description here
recommendation
use better only node.js and express ,axios isn't necesary because express is same
I'm trying to implement the Facebook OAuth in my express/NodeJS app using authorization code flow. I'm using react-facebook-login node module to fetch the authorization code. In my react app, I could get the authorization code successfully. But in server side, I can't request the access token from the Facebook API as I'm getting an error message "redirect_uri is not identical to the one you used in the OAuth dialog request"
Code in my react app,
facebookLogin = async (signedRequest) => {
return fetch('/api/auth/facebook', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ signedRequest }),
}).then((res) => {
if (res.ok) {
return res.json();
} else {
return Promise.reject(res);
}
});
};
responseFacebook = async (response) => {
try {
if (response['signedRequest']) {
const userProfile = await this.facebookLogin(response['signedRequest']);
console.log(userProfile);
} else {
throw new Error(response.error);
}
} catch (err) {
console.log(err);
}
};
render() {
<FacebookLogin
appId={process.env.FACEBOOK_CLIENT_ID}
fields="name,email"
responseType="code"
redirectUri="http://localhost:3000/"
callback={this.responseFacebook}
/>
In my app.js
const facebookOAuth = require('./config/facebookOAuth');
// facebook oauth route
app.post("/api/auth/facebook", async (req, res) => {
try {
const signedRequest = req.body.signedRequest;
const profile = await facebookOAuth.getProfile(signedRequest);
console.log(profile);
res.send({ profile });
} catch (err) {
console.log(err);
res.status(401).send();
}
});
facebookOAuth.js look like this
const fetch = require('node-fetch');
const getData = async (userId, accessToken) => {
const userData = await fetch(`https://graph.facebook.com/${userId}?fields=name,email&access_token=${accessToken}`, {
method: 'GET'
}).then((res) => {
return res.json();
}).then((userData) => {
return userData;
});
return userData;
};
exports.getProfile = async (signedRequest) => {
const decodedSignedRequest = JSON.parse(Buffer.from((signedRequest.split(".")[1]), 'base64').toString());
const profile = await fetch(`https://graph.facebook.com/oauth/access_token?client_id=${process.env.FACEBOOK_CLIENT_ID}&redirect_uri=${encodeURIComponent('http://localhost:3000/')}&client_secret=${process.env.FACEBOOK_CLIENT_SECRET}&code=${decodedSignedRequest.code}`, {
method: 'GET'
}).then((res) => {
return res.json();
}).then((token) => {
console.log(token);
const userData = getData(decodedSignedRequest.user_id, token.access_token);
return userData;
}).catch((err) => {
console.log(err);
return err;
});
return profile;
}
What I'm getting is this error
"error": {
message: 'Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request',
type: 'OAuthException',
code: 100,
error_subcode: 36008,
fbtrace_id: 'A-YAgSqKbzPR94XL8QjIyHn'
}
I think the problem lies in my redirect_uri. Apparently, the redirect uri I obtained from the Facebook auth dialog is different from the one that I'm passing to the facebook API in my server side (http://localhost:3000/).
I believe there's something to do with the origin parameter of the redirect_uri. Initial auth dialog request uri indicates that it's origin parameter value is something like "origin=localhost:3000/f370b6cb4b5a9c". I don't know why react-facebook-login add some sort of trailing value at the end of origin param.
https://web.facebook.com/v2.3/dialog/oauth?app_id=249141440286033&auth_type=&cbt=1620173773354&channel_url=https://staticxx.facebook.com/x/connect/xd_arbiter/?version=46#cb=f39300d6265e5c4&domain=localhost&origin=http%3A%2F%2Flocalhost%3A3000%2Ff370b6cb4b5a9c&relation=opener&client_id=249141440286033&display=popup&domain=localhost&e2e={}&fallback_redirect_uri=http://localhost:3000/&locale=en_US&logger_id=f1b3fba38c5e31c&origin=1&redirect_uri=https://staticxx.facebook.com/x/connect/xd_arbiter/?version=46#cb=f17641be4cce4d4&domain=localhost&origin=http%3A%2F%2Flocalhost%3A3000%2Ff370b6cb4b5a9c&relation=opener&frame=f3960892790a6d4&response_type=token,signed_request,graph_domain&return_scopes=false&scope=public_profile,email&sdk=joey&version=v2.3
I tried finding everywhere about this but no luck. Anyone has clue about this, much appreciated.
Are you using middleware to parse the body? if you aren't code could be undefined here.
const facebookOAuth = require('./config/facebookOAuth');
// facebook oauth route
app.post("/api/auth/facebook", async (req, res) => {
try {
const code = req.body.code;
const profile = await facebookOAuth.getProfile(code);
console.log(profile);
res.send({ profile });
} catch (err) {
console.log(err);
res.status(401).send();
}
});
Passport.authenticate throw Unauthorized error with cookies approach.
Where can be problem, in passport js settins or on frontend side?Interesting that on compyter another developer it works fine
nodeJS
router.post('/check',passport.authenticate("cookie", { session: false }), async (req, res, next) => {
try {
return res.status(200).json({ status: true })
} catch (error) {
res.status(400).json(error);
}
});
frontend
export async function isAuthorized(context, commands) {
let token = getCookie('token')
if (token) {
let settings = {
"method": 'POST',
headers: {
"Content-Type": "application/json",
},
}
try {
let response = await fetch('/api/auth/check', settings) //here unathorized
let result = await response.json()
return result.status
} catch (error) {
throw Error
}
} else {
return false
}
}
the cookie is sent successfully with this
maybe I need use some additional settings for passport or something like this
I'm getting struggle with this code, so I need a third eye on this to find a solution.
I'm developing a ReactJS app with a REST API with Node.JS (Express), and I'm getting this error:
SyntaxError: "JSON.parse: unexpected character at line 1 column 1 of the JSON data"
I'm using Sequelize ORM to work with Models and Database in Node.JS.
I'm also using CORS module for Node.JS.
This implementation works fine.
// Node.js Route for login
const router = require('express').Router();
const User = require('user');
router.post("/login", async (req, res) => {
try {
await User.findOne({
where: {
email: req.body.email,
password: req.body.password,
}
}).then((user) => {
if (!user) {
return res.send({message: "Login error!"});
} else {
const userData = {id: user.id, email: user.email};
res.send({"user": userData});
}
}).catch((err) => {
return res.send(err);
});
} catch (err) {
return res.send(err);
}
});
// ReactJS for login
loginFunction(e, data) {
e.preventDefault();
fetch('http://localhost:4500/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(json => {
this.setState({'user': json['user']});
})
.catch((err) => {
console.log(err);
this.setState({errors: "Login error"})
});
}
On the other hand, this implementation do not work properly and throws the SyntaxError above:
// Node.JS for Posts
const router = require('express').Router();
const Post = require('post');
router.get("/posts", async (req, res) => {
try {
await Post.findAndCountAll()
.then((posts) => {
res.send({"posts": posts});
}).catch((err) => {
return res.send(err);
});
} catch (err) {
return res.send(err);
}
});
// ReactJS for Posts
postsFunction() {
fetch('http://localhost:4500/posts', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(json => {
this.setState({'posts': json.posts.rows});
})
.catch((err) => {
console.log(err);
this.setState({errors: "Posts error."})
});
}
As you can see both implementation have little differences, What am I missing?
PS: When I test the 2nd implementation on Postman, data is retrieving successfully.
try removing headers when using GET method
headers: {
'Content-Type': 'application/json'
}
Try to use res.json instead of res.send in the node js function that cause the error.
I found the issue!
I follow the (#vengleab so) suggestion:
console log response instead of response => response.json()
I'm realize that response returns an object like this:
Response: {
body: ReadableStream
locked: false
<prototype>: object { … }
bodyUsed: false
headers: Headers { }
ok: true
redirected: false
status: 200
statusText: "OK"
type: "basic"
url: "http://localhost:3000/admin/undefined/posts"
}
The URL attribute contain an undefined value, so when I try to console.log the .env variable API_URL that contains the localhost URL used in this line:
fetch('http://localhost:4500/posts', {
That in real function is:
fetch(process.env.API_URL + '/posts', {
The result of the console.log was undefined.
As it is explained in Create React App docs, the environment variables must start with the prefix REACT_APP_.
Finally the line works as:
fetch(process.env.REACT_APP_API_URL + '/posts', {
I found that it was because my front end react url pointed to the same url as my backend server.js running mongodb. Also clearing the cookies on the browser seems to have helped as well.
In my VUE components, I use this async method to fetch data from API:
Components:
methods: {
async fetch() {
// console.log("##### WAIT ####");
const { data } = await staffRepository.getItems(this.teamId)
// console.log("##### END WAIT ####");
this.staffs = data
},
},
As you can see I use a custom repository to have a single axios code, this repository is imported in my previous component.
staffRepository:
export default {
getItems(nationId) {
return Repository.get(`page/${nationId}`)
},
}
And finally the main repository having the axios code:
Repository:
import axios from 'axios/index'
const baseDomain = 'https://my end point'
const baseURL = `${baseDomain}`
...
const headers = {
'X-CSRF-TOKEN': token,
// 'Access-Control-Allow-Origin': '*', // IF you ADD it add 'allowedHeaders' to ai server config/cors.php
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/json',
Authorization: `Bearer ${jwtoken}`,
}
export default axios.create({
baseURL,
withCredentials: withCredentials,
headers: headers,
})
This code works very nice when the jwtoken is a valid and NOT EXIPRED token.
The problem is when the token is expired or not found and my laravel 5.8 API returns the status code 401 (or other).
GET https://api.endpoint 401 (Unauthorized)
A good solution could catch the status code in staffRepository, the one having the get method.
MySolution: (not working)
getItems(nationId) {
return Repository.get(`page/${nationId}`)
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error.response.status) // <-- it works!
})
},
This could be nice because in error case the error in console is 401
But I can't use this solution because I have 2 nested promises: this one and the async fetch() into the component.
How can I fix it still using my repository environment?
I would suggest using the returned promise in your component, to make things more explicit:
methods: {
fetch() {
let data = null
staffRepository
.getItems(this.teamId)
.then(data => {
// do something with data
this.staffs = data
})
.catch(e => {
// do something with error, or tell the user
})
},
},
Edit - this will work perfectly fine, as your method in Repository will return a promise by default if you are using axios.
Try this: API code, where HTTP is an axios instance
export const get = (path: string): Promise<any> => {
return new Promise((resolve, reject) => {
HTTP.get(`${path}`)
.then((response) => {
resolve(response);
})
.catch((error) => {
reject(handleError(error));
});
});
};
// ***** Handle errors *****/
export function handleError(error) {
if (error.response) {
const status = error.response.status;
switch (status) {
case 400:
// do something
break;
case 401:
// do something, maybe log user out
break;
case 403:
break;
case 500:
// server error...
break;
default:
// handle normal errors here
}
}
return error; // Return the error message, or whatever you want to your components/vue files
}
The best practice solution is to use axios's interceptors:
import axios from "axios";
import Cookies from "js-cookie";
export default (options = {}) => {
let client = options.client || axios.create({ baseURL: process.env.baseUrl });
let token = options.token || Cookies.get("token");
let refreshToken = options.refreshToken || Cookies.get("refreshToken");
let refreshRequest = null;
client.interceptors.request.use(
config => {
if (!token) {
return config;
}
const newConfig = {
headers: {},
...config
};
newConfig.headers.Authorization = `Bearer ${token}`;
return newConfig;
},
e => Promise.reject(e)
);
client.interceptors.response.use(
r => r,
async error => {
if (
!refreshToken ||
error.response.status !== 401 ||
error.config.retry
) {
throw error;
}
if (!refreshRequest) {
refreshRequest = client.post("/auth/refresh", {
refreshToken
});
}
const { data } = await refreshRequest;
const { token: _token, refreshToken: _refreshToken } = data.content;
token = _token;
Cookies.set("token", token);
refreshRequest = _refreshToken;
Cookies.set("refreshToken", _refreshToken);
const newRequest = {
...error.config,
retry: true
};
return client(newRequest);
}
);
return client;
};
Take a look at client.interceptors.response.use. Also you should have a refreshToken. We are intercepting 401 response and sending post request to refresh our token, then waiting for a new fresh token and resending our previous request. It's very elegant and tested solution that fits my company needs, and probably will fit your needs too.
To send request use:
import api from './api'
async function me() {
try {
const res = await api().get('/auth/me')
// api().post('/auth/login', body) <--- POST
if (res.status === 200) { alert('success') }
} catch(e) {
// do whatever you want with the error
}
}
Refresh token: The refresh token is used to generate a new access
token. Typically, if the access token has an expiration date, once it
expires, the user would have to authenticate again to obtain an access
token. With refresh token, this step can be skipped and with a request
to the API get a new access token that allows the user to continue
accessing the application resources.