JWT gives invalid token - reactjs

Working locally, my jwt token is invalid but in jwt.io it shows verified signature. Not sure what i am missing. I am having invalid signature whenever i tried to make a call to a api whithin the app.
Link.js
const { Router } = require("express");
const Link = require("../models/Link");
const auth = require("../middleware/auth.middleware");
const router = Router();
router.get("/", auth, async (req, res) => {
try {
const links = await Link.find({ owner: req.user.userId });
res.json(links);
} catch (error) {
res.status(500).json({ message: "Something went wrong, try again" });
}
});
auth.middleware.js
const jwt = require("jsonwebtoken");
const config = require("config");
module.exports = (req, res, next) => {
if (req.method === "OPTIONS") {
return next();
}
try {
const token = req.headers.authorization; // Token
if (!token) {
return res.status(401).json({ message: "No Authorization" });
}
const decoded = jwt.verify(token, config.get("secret"));
req.user = decoded;
next();
} catch (error) {
res.status(401).json({ message: "No Authorization" });
}
};
Links.tsx
const LinksPage: React.FC = () => {
const [links, setLinks] = useState([]);
const fetchLinks = useCallback(async () => {
try {
const fetched = await request("http://localhost:5000/api/link/", "GET", null, {
Authorization: Token,
});
setLinks(fetched);
} catch (error) {
alert(error);
}
}, []);
};

Maybe the "req.headers.authorization" was not what you looking for.
Try to console.log(req.headers.authorization) F12 in chrome, firefox.
I suggest you also POSTMAN (free software). It help me a lot for debugging the back end (server side).

I solved the problem. I had to json.parse(token) which stored in the client in order to jwt.verify(token, secret), but instead i was verifying string that contains object of token and userId.

Related

React profile page, how to avoid 'GET http://localhost:3001/users/profile 401 (Unauthorized)' when trying to get JSON data from back end

For this application, I am using React & Express. I have React running on PORT 3000, and Express running on PORT 3001. On the Express side, I have authentication working that uses JWT.
First, here is my auth.js service file:
const jwt = require('jsonwebtoken');
const models = require('../models');
const bcrypt = require('bcryptjs');
var authService = {
signUser: function (user) {
const token = jwt.sign({
Username: user.Username,
UserId: user.UserId
},
'secretkey',
{
expiresIn: '1h'
}
);
return token;
},
verifyUser: function (token) {
try {
let decoded = jwt.verify(token, 'secretkey');
return models.users.findByPk(decoded.UserId);
} catch (err) {
console.log(err);
return null;
}
},
hashPassword: function (plainTextPassword) {
let salt = bcrypt.genSaltSync(10);
let hash = bcrypt.hashSync(plainTextPassword, salt);
return hash;
},
comparePasswords: function (plainTextPassword, hashedPassword) {
return bcrypt.compareSync(plainTextPassword, hashedPassword);
}
}
module.exports = authService;
When a user makes a POST request to the signup route, it works:
router.post('/signup', function (req, res, next) {
models.users.findOrCreate({
where: {
Username: req.body.username
},
defaults: {
FirstName: req.body.firstName,
LastName: req.body.lastName,
Email: req.body.email,
Password: authService.hashPassword(req.body.password)
}
})
.spread(function (result, created) {
if (created) {
res.redirect("http://localhost:3000/login");
} else {
res.send('This user already exist')
}
});
});
Signup works in both Postman and React.
When a user makes a POST request to the login route, it works:
router.post('/login', function (req, res, next) {
models.users.findOne({
where: {
Username: req.body.username
}
}).then(user => {
if (!user) {
console.log('User not found')
return res.status(401).json({
message: "Login Failed"
});
} else {
let passwordMatch = authService.comparePasswords(req.body.password, user.Password);
if (passwordMatch) {
let token = authService.signUser(user);
res.cookie('jwt', token);
res.redirect('http://localhost:3001/users/profile');
} else {
console.log('Wrong Password');
}
}
});
});
Login works in both Postman and React.
When a user makes a GET request to the profile route, it semi-works:
router.get('/profile', function (req, res, next) {
let token = req.cookies.jwt;
if (token) {
authService.verifyUser(token).then(user => {
if (user) {
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(user));
} else {
res.status(401);
res.send('Invalid authentication token');
}
});
} else {
res.status(401);
res.send('Invalid authentication token');
}
});
This works only in Postman, I can see the data that I want using Postman. In React, it will not get the profile route that I request. This is where the error comes in: Console Error
On the React side, this is profile GET component:
import React from 'react';
import axios from 'axios';
class UserProfile extends React.Component {
constructor(props) {
super(props);
this.state = {
profileData: []
}
};
fetchProfileData = () => {
var encodedURI = window.encodeURI(this.props.uri);
return axios.get(encodedURI).then(response => {
this.setState(() => {
return {
profileData: response.data
};
});
});
};
componentDidMount() {
this.fetchProfileData();
}
render() {
console.log(this.state.profileData);
if (this.state.profileData.length === 0) {
return <div>Failed to fetch data from server</div>
}
const profile = this.state.profileData.map(user => (
<div key={user.UserId}>Hello world</div>
));
return <div>{profile}</div>
}
}
export default UserProfile;
Then when I go to render this component, I just:
<UserProfile uri="http://localhost:3001/users/profile" />
Which then will render 'Failed to fetch data from server', then the console will log the '401 (Unauthorized)' error. I just can't get it to render in React.
And if anyone wants my Express app.js file for some extra information:
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var models = require('./models');
var cors = require('cors');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hbs');
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://localhost:3000');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(cors());
app.use('/', indexRouter);
app.use('/users', usersRouter);
// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404));
});
// error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
models.sequelize.sync().then(function () {
console.log("DB Synced Up");
});
module.exports = app;
Thank you in advanced. I have been struggling to figure this out.
I have tried toying with my UserProfile component. And I've tried toying with my /profile route in Express. The only 2 errors I've gotten is the 401 (Unauthorized) and something about the Headers. I know that my JWT key gets passed onto reacts side, because when I do 'localhost:3000/profile' (react side), I can see that I have the cookie stored. I'm not sure on how to approach authorization on React side. At this point, I am very clueless on what to do. This is the first time I've tried setting up authentication with React. I have always used Express and the .hbs files to render my profile pages. But I've been told that you shouldn't render a profile page in the back-end. So, here I am trying to do it with React.
I have rendered things from the back-end to the front-end, but that's without the use of JWT. I strongly believe that it has something to do with the JWT cookie. I just don't know how to authenticate it in React. Thanks again in advanced.
I fixed it by adding this into my React project:
I added this into my fetchProfileData()
{ withCredentials: true }
fetchProfileData = () => {
var encodedURI = window.encodeURI(this.props.uri);
return axios.get(encodedURI, { withCredentials: true }).then(response => {
this.setState(() => {
return {
profileData: response.data
};
});
});
};
Then in Express, I toyed with my Profile route. Put the data into an array, and sent it on its way:
router.get('/profile', function (req, res, next) {
var userData = [];
let token = req.cookies.jwt;
if (token) {
authService.verifyUser(token).then(user => {
userData.push(user);
res.send(userData);
});
} else {
res.status(401);
res.send('Invalid authentication token');
}
});

How can I solve: ERR_CONNECTION_REFUSED in React Js for my sign in form

I humbly need your help to resolve the ERR_CONNECTION_REFUSED issue whenever I try to sign in an already signed up user using my chat app that am building in react Js and stream. It will be good if you jus show me what I might have missed by editing my below code. Thank you in advance!
//This is my code//
const { connect } = require('getstream');
const bcrypt = require('bcrypt');
const StreamChat = require('stream-chat').StreamChat;
const crypto = require('crypto');
require('dotenv').config();
const api_key = process.env.STREAM_API_KEY;
const api_secret = process.env.STREAM_API_SECRET;
const app_id = process.env.STREAM_APP_ID;
const signup = async (req, res) => {
try {
const { fullName, username, password, phoneNumber } = req.body;
const userId = crypto.randomBytes(16).toString('hex');
const serverClient = connect(api_key, api_secret, app_id);
const hashedPassword = await bcrypt.hash(password, 10);
const token = serverClient.createUserToken(userId);
res.status(200).json({ token, fullName, username, userId, hashedPassword, phoneNumber });
} catch (error) {
console.log(error);
res.status(500).json({ message: error });
}
};
const login = async (req, res) => {
try {
const { username, password } = req.body;
const serverClient = connect(api_key, api_secret, app_id);
const client = StreamChat.getInstance(api_key, api_secret);
const { users } = await client.queryUsers({ name: username });
if(!users.length) return res.status(400).json({ message: 'User not found' });
const success = await bcrypt.compare(password, users[0].hashedPassword);
const token = serverClient.createUserToken(users[0].id);
if(success) {
res.status(200).json({ token, fullName: users[0].fullName, username, userId: users[0].id});
} else {
res.status(500).json({ message: 'Incorrect password' });
}
} catch (error) {ads
console.log(error);
res.status(500).json({ message: error });
}
};
module.exports = { signup, login }

Jsonwebtoken verify next function not working in middleware

I was working with jsonwebtoken. Then I go to verify jsonwebtoken I see some tutorial. But when I am using my next () function I am not getting my expected data
I would have benefited from your help. Thanks
// This is for verifying the jsonwebtoken
function verifyJwt(req, res, next) {
const authHeaders = req.headers.authorization;
if (!authHeaders) {
return res.status(401).send({ message: "Unauthorized access" });
}
const token = authHeaders.split(" ")[1];
// verify a token symmetric
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, function (err, decoded) {
if (err) {
return res.status(403).send({ message: "Forbidden Access" });
}
req.decoded = decoded;
next();
console.log(decoded);
});
console.log(token);
}
//This one is for giving access if the email matched then it will give me the needed data
app.get("/booking", verifyJwt, async (req, res) => {
const patientEmail = req.query.patientEmail;
// Left over
const decodedEmail = req.decoded.patientEmail;
if (patientEmail === decodedEmail) {
const query = { patientEmail: patientEmail };
const services = await bookingCollection.find(query).toArray();
// const authorization = req.headers.authorization;
console.log(authorization);
return res.send(services);
} else {
return res.status(403).send({ message: "Forbidden Access" });
}
});
This next() function not working any solve ?
Please try the below code
try {
const decoded = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET);
req.decoded = decoded;
console.log(decoded);
next();
} catch (ex) {
return res.status(403).send({ message: 'Forbidden Access' });
}
appointments.map is not a function. Please console appointment and see what it returns.
Problem Solved
Thanks Everyone

Why is a user able to login and have a json web token is issued even though user has entered incorrect password?

1.below are the AuthRoutes.js , User.js , RequireAuth.js files with the code.
2.Ive tried changing the email to an invalid un-captured one , and it does report the error "Invalid email or password" which is correct, however when the user enters the incorrect password for that user ,
3. a JSON web token is issued instead of posting the error invalid Email or password as indicated in the code.
const express = require('express');
const mongoose = require('mongoose')
const jwt = require('jsonwebtoken');
const User = mongoose.model('User');
const router = express.Router();
router.post('/signup', async (req,res)=>{
const {name,email,password} = req.body;
try{
const user = new User({name,email,password});
await user.save();
const token = jwt.sign({ userId: user._id},'MY_SECRET_KEY');
res.send({token});
} catch (err) {
return res.status(422).send(err.message);
}
});
router.post('/signin', async (req, res) =>{
const { email, password} = req.body;
if (!email || !password) {
return res.status(422).send({error:'Must provide email and password '})
}
const user = await User.findOne ({ email});
if(!user) {
return res.status(422).send ({error:'Invalid email or password'});
}
try{
await user.comparePassword(password);
const token = jwt.sign ({userId: user._id},'MY_SECRET_KEY');
res.send ({ token });
} catch (err){
return res.status(422).send({error:'Invalid password or email'})
}
});
module.exports = router;
const jwt = require ('jsonwebtoken');
const mongoose = require ('mongoose');
const User = mongoose.model('User');
module.exports = (req,res,next) =>{
const {authorization} = req.headers.body;
//authorization === 'Bearer dfkndlkfgdkgmdlkgmdlkgdkfgmdlfkg(token)'
if (!authorization) {
return res.status (401).send ({error:"You must be logged in"});
}
const token = authorization.replace('Bearer ','');
jwt.verify(token, 'MY_SECRET_KEY', async (err, payload)=>{
if (err){
return res.status(401).send({error:'You must be logged in'});
}
const {userId} = payload;
const user = await User.findById(userId);
req.user = user;
next();
});
};
const mongoose = require('mongoose');
const bcrypt = require ('bcrypt');
const userSchema = new mongoose.Schema({
name:{
type:String,
required: true,
},
email:{
type: String,
unique: true,
required: true,
},
password:{
type:String,
required: true
}
});
userSchema.pre('save', function (next){
const user = this;
if(!user.isModified('password')){
return next();
}
bcrypt.genSalt(10, (err,salt) =>{
if (err){
return next (err);
}
bcrypt.hash(user.password, salt, (err, hash) => {
if (err) {
return next (err);
}
user.password = hash;
next ();
});
});
});
userSchema.methods.comparePassword = function(candidatePassword){
return new Promise ((resolve,reject) => {
bcrypt.compare (candidatePassword, this.password, (err,isMatch) => {
if (err){
return reject (err);
} else {
return resolve(true);
}
});
});
};
mongoose.model('User', userSchema);
Its because your comparePassword is returning a result that isn't checked for / caught.
Change your promise to:
return new Promise ((resolve,reject) => {
bcrypt.compare(candidatePassword, this.password, (err, res) => {
if (err){
reject(err);
} else {
resolve(res);
}
});
});
And then your check to:
const result = await user.comparePassword(password);
if (!result) return res.status(422).send({error:'Incorrect email and/or password'});

set current user in context using apollo server

i have this middleware and need to get current user to set it in context of apollo server
app.use(async (req, res, next)=>{
const token = req.headers['authorization'];
if(token !== "null"){
try {
const currentUser = await jwt.verify(token, process.env.SECRET)
req.currentUser = currentUser;
} catch (error) {
console.log(error);
}
}
next()
})
and need to set current user in context
const SERVER = new ApolloServer({
schema,
context:{
currentUser //need to set this current user
}
})
SERVER.applymiddleware({app})
the context api in Apollo server offers handling request as follows
const initGraphQLserver = () => {
const graphQLConfig = {
context: ({ req, res }) => ({
user: req.user,
}),
rootValue: {},
schema,
};
const apolloServer = new ApolloServer(graphQLConfig);
return apolloServer;
};
This will assume that you will have proper middleware that will parse the cookie or header. This is dependent on your authentication mechanism as you need to have set user for the request in some middleware before, if you would like to use JWT, you can use for example this middleware
const auth = (req, res, next) => {
if (typeof req.headers.authorization !== 'string') {
return next();
}
const header = req.headers.authorization;
const token = header.replace('Bearer ', '');
try {
const jwtData = jwt.verify(token, JWT_SECRET);
if (jwtData && jwtData.user) {
req.user = jwtData.user;
} else {
console.log('Token was not authorized');
}
} catch (err) {
console.log('Invalid token');
}
return next();
};
This middleware will inject user if the jwt token is correct, then in your server file you will need to have the following order of middlewares
const app = express();
app.use(auth);
initGraphQLserver().applyMiddleware({ app });
Now you should have user in your schema context. I hope that it is clear, the code is not completed, but should be easy to finalize everything.

Resources