Heroku API not able to access cookies - node.js

I have hosted a JWT auth API on heroku. While accessing the API using axios from my front-end React app, I notice that the protected routes can never be fetched since they are dependent on res.cookies which are never set. While testing the API out on postman, it sets the cookies and everything works fine. Can you help me finding where I am going wrong and how I can overcome this.
Sample cookie that is supposed to be set(working fine with postman)
This cookie is needed, because when I GET /user, with the following code:
exports.checkUser = catchAsync(async (req, res, next) => {
let currentUser;
if (req.cookies.jwt) {
const token = req.cookies.jwt;
const decoded = await promisify(jwt.verify)(token, process.env.JWT_SECRET);
currentUser = await User.findById(decoded.id);
} else {
currentUser = null;
}
res.status(200).send({ currentUser });
});
currentUser should be populated as follows
When I access this API using my react frontend: this being my useAuth hook
import { useState, useContext } from "react";
import { useHistory } from "react-router-dom";
import axios from "axios";
import { UserContext } from "./userContext";
export default function useAuth() {
let history = useHistory();
const { setUser } = useContext(UserContext);
const [error, setError] = useState(null);
const setUserContext = async () => {
return await axios
.get("<my api link>/user")
.then((res) => {
console.log(res);
setUser(res.data.currentUser);
history.push("/home");
})
.catch((err) => {
console.log(err);
setError(err);
});
};
const registerUser = async (data) => {
const { username, email, password, passwordConfirm, name } = data;
return axios
.post("<My api link>/auth/post", {
username,
email,
password,
passwordConfirm,
name,
})
.then(async (res) => {
console.log(res);
await setUserContext();
})
.catch((err) => {
console.log(err);
return setError(err);
});
};
res.data.currentUser is ALWAYS null. Any help is appreciated

The concept of JWT is that the handling of the token is handed over to the client.
Hence, the frontend is supposed to store/save the JWT Token it receives in cookies or localstorage and is supposed to send them via headers in requests where you want to access such protected routes.
Hence it's the frontend duty and that's why Postman handles it automatically for you.
You can use react-cookie to save and retrieve this JWT token in the frontend whenever required and you will have to reform your axios request.
In your specific case, you can do the following for GET request:
axios.get('URL', {
withCredentials: true
});
But I would strongly recommend amending your backend to extract JWT tokens from headers instead of cookies, which would make your request, something similar to:
let JWTToken = 'xxyyzz'; // Get this from cookie or localstorage, hardcoded for demonstration.
axios
.get("URL", { headers: {"Authorization" : `Bearer ${JWTToken}`} })
.then(res => {
this.profile = res.data;
console.log('Fetched Data is', res.data);
})
.catch(error => console.log(error))
Do not forget to enable CORS on your backend!

Related

How to use JWT token stored in cookie on a NodeJs API using Fetch API Get Request in ReactJs to get user data

Hi I have managed to store the JWT token generated on Login to a cookie in the browser. How can I use the token to display a list of users from a protected route in Nodejs APi.
I have accessed the token from the cookie using following code
Cookies.get("access")
the Fetch API Code is
const url = "http://localhost:4000/users/";
const getUsers = () => {
fetch(
url,
{ method: "GET" },
{
headers: {
Authorization: "Bearer" + Cookies.get("access"),
"x-auth-token": Cookies.get("access"),
},
}
)
.then((data) => data.json())
.then((prdt) => console.log(prdt));
};
useEffect(getUsers, []);
Node API Code is as follows:
//http://localhost:4000/users
router.get("/", auth, async function (request, response) {
try {
const result = await getAllUsers();
response.send(result);
} catch {
response.status(400).send({
message: error,
});
}
});
the Nodejs Auth Code is as follows:
export const auth = (request, response, next) => {
try {
const token = request.header("x-auth-token");
console.log("token", token);
jwt.verify(token, process.env.SECRET_KEY);
next();
} catch (err) {
response.status(401).send({
error: err.message,
});
}
};
I am not sure where I am going wrong
the Nodejs Part works as tried in Postman The issue is originating on the react side fetch API where I am not sure how to send the keyname "x-auth-token" with jwt token to retrieve the data.

Expressjs: google-auth-libary id-token verification "token used too late number > number..."

I have tried to add google auth login for my reactjs/expressjs web app. On the frontend I am using the react-google-login package:
import React from "react"
import { GoogleLogin } from "react-google-login"
import { useHistory } from "react-router-dom"
const axios = require('axios')
export default function LoginButton() {
let history = useHistory()
const { REACT_APP_GOOGLE_CLIENT_ID, REACT_APP_API_URL } = process.env
const onSuccess = async (response) => {
console.log(response)
const data = { token: response.tokenId }
const res = await axios.post(REACT_APP_API_URL + "/auth/google", data, {
'Content-Type' : 'text/json'
})
.then((res) => {
history.push('/home')
})
.catch((err) => {
console.log("[LOGIN FAILED]")
})
}
const onFailure = (response) => {
console.log(response)
}
return(
<GoogleLogin
clientId={REACT_APP_GOOGLE_CLIENT_ID}
buttonText="Log in with Google"
onSuccess={onSuccess}
onFailure={onFailure}
cookiePolicy={'single_host_origin'}
/>
)
}
From what it seems I am getting the correct data from this button. I am sending the token to the expressjs api. This is where I am having issues. When using google-auth-library to verify the token I am getting the error: "Error: Token used too late, number_1 > number_2". As far as I know the idToken has expired, but isn't it weird considering I sent it as soon as possible from the frontend. Backend code:
const { OAuth2Client } = require('google-auth-library')
require('dotenv').config()
const client = new OAuth2Client(process.env.CLIENT_ID)
const postGoogleLogin = async (req, res) => {
const { token } = req.body
try{
const ticket = await client.verifyIdToken({
idToken: token,
audience: process.env.CLIENT_ID
})
const { name, email, picture } = ticket.getPayload()
res.status(201).json({
name: name,
email: email,
picture: picture
})
}
catch(e){
console.log(e)
}
}
I have tried to verify the token using the endpoint https://oauth2.googleapis.com/tokeninfo?id_token=XYZ123 which says the token is valid, but as far as I know this endpoint should not be used in production
The issue appears to be from the library itself. As long as you're using the same client id on both React and ExpressJs, verifyIdToken should return a success response as long as the token is still valid.
Also, you can make use of https://oauth2.googleapis.com/tokeninfo?id_token=XYZ123 in your production code.
Internally, the library call the same endpoint to verify your token.

Why am I getting a 431 status error when passing in a JWT token in Authorization section of the header?

So I am creating a full stack React app with Express and Node. I have implemented auth0 for authentication and authorization but I am getting the following error, does anyone have any suggestions on how to solve it? Thanks!
"Failed to load resource: the server responded with a status of 431 (Request Header Fields Too Large)"
The following is the class component where I am calling my api/tasks route and passing in the jwt access token from Auth0.
import React, { Component } from 'react';
import axios from "axios";
import {withAuth0} from "#auth0/auth0-react";
import config from "../auth_config.json";
const {apiOrigin = "http://localhost:3001"} = config
componentDidMount() {
const getProtectedTasks = async () => {
const {getAccessTokenSilently} = this.props.auth0;
const accessToken = await getAccessTokenSilently();
try {
const token = accessToken;
const self = this;
const response = await axios.get(`${apiOrigin}/api/tasks`, {
headers: {
Authorization: `Bearer ${token}`
}
})
self.setState({
allTasks: response.data
})
} catch {
}
}
getProtectedTasks()
}
The following is my code that handles the get api/tasks endpoint
const db = require("../models");
const jwt = require("express-jwt");
const jwksRsa = require("jwks-rsa");
module.exports = function (router) {
const authorizeAccessToken = jwt({
secret: jwksRsa.expressJwtSecret({
cache:true,
rateLimit:true,
jwksRequestsPerMinute:5,
jwksUri: `https://task-motivator.us.auth0.com/.well-known/jwks.json`
}),
audience: "https://quickstarts/api",
issuer: "https://task-motivator.us.auth0.com",
algorithms: ["RS256"]
});
router.get("/api/tasks", authorizeAccessToken, (req, res) => {
db.Task.findAll({}).then(data => {
res.json(data);
});
});

How to get data from the backend that needs authorization using React

I am creating a website using the MERN stack however I don't know how to get data to the frontend that needs authorization from the backend and I tried to console log the problem and it shows me the HTML of my login page even though I am logged in. Any will be appreciated thank you so much.
My backend code:
router.get("/questions", ensureAuthenticated, (req, res) => {
math = Math.floor(Math.random() * 3) + 1;
Security.findOne({
user: req.user.id
}, (err, user) => {
if (err) {
console.log(err);
}
if (math === 1) {
res.send({
question: user.firstQuestion
});
} else if (math === 2) {
res.send({
question: user.secondQuestion
});
} else {
res.send({
question: user.thirdQuestion
});
}
});
});
My Frontend code:
class QuestionForm extends Component {
constructor(props) {
super(props);
this.state = {
data: ''
}
}
componentDidMount() {
axios.get("http://localhost:5000/users/questions")
.then((res) => {
this.setState({
data: res.data
});
}).catch((err) => console.log(err));
}
render() {
return <h1 > {
this.state.data
} < /h1>
}
}
a lot of changes should be made.
you never want to use the port in your Axios request
add to you package.json an proxy attribute
"proxy": "http://localhost:5000"
then you can change your axios get to
axios.get("/users/questions")
best practice when using autorization is to add to axios interceptors
follow this thread :
How can you use axios interceptors?
and also here is an example for using authorization with JWT token
const tokenHandler = axios.create();
tokenHandler.interceptors.request.use(config => {
const token = localStorage.getItem("token");
if (token) {
config.headers["Authorization"] = token;
}
return config;
});
export default tokenHandler;
let's say you create a token on the login page and store it inside your local storage.
now you can import the token handler and your request should look something like this :
import {tokenHandler} from '<TOKEN HANDLER PATH>'
..
..
tokenHandler.get("/users/questions")
.then((res)=>{
this.setState({data:res.data});
}).catch((err)=>console.log(err));

Send response from server side axios request to React/Redux app

I'm a little new to creating a backend in Node/Express, but I am trying use axios to make HTTP requests. I've set up express routes that will make the necessary request and I know from using Postman that GET request I'm testing does return a response. Where I'm stuck is how to return that data and send it to my React/Redux app to use.
-Server Side-
//Express Route
app.get('/api/recipes', recipeController.getRecipes)
//Controller Function that makes axios request
const axios = require('axios')
const Promise = require('bluebird')
module.exports = {
getRecipes(req, res) {
const url = "https://gw.hellofresh.com/api/recipes/search?country=us&limit=9"
const token = "IUzI1NiIsInR5c"
axios
.get(url, {
"headers": {"Authorization": "Bearer " + token}
})
.then((response) => {
console.log(response)
})
.catch((err) => {
console.log(err)
})
}
}
-Client Side-
I dispatch the following action and make a call using the endpoint I created. However, at this point, I'd get an error status even though on the server side I was able to get a response. I tried playing around using Promises as I read that axios GET requests returns promises, but couldn't wrap my head around on how to implement it.
export const getRecipes = () => {
return (dispatch) => {
axios
.get('/api/recipes')
.then((resp) => {
console.log(resp)
})
.catch((err) => {
console.log(err)
})
}
}
You need to call res.send in the route, to send the data to the client:
module.exports = {
getRecipes(req, res) {
const url = "https://gw.hellofresh.com/api/recipes/search?country=us&limit=9"
const token = "IUzI1NiIsInR5c"
axios
.get(url, {
"headers": {"Authorization": "Bearer " + token}
})
.then(response => {
console.log(response)
res.send(response) // <= send data to the client
})
.catch(err => {
console.log(err)
res.send({ err }) // <= send error
})
}
}

Resources