so I have tested my API route /getUser using Postman which is receiving data successfully being received in json format. However the Jwt token which is stored in localstorage and past in the headers is not being verified as within my browser I receive 'Access denied. No JWT provided' which is sent with a 401 status.
Nodejs API is below this includes my authentication route and /getUserwhich users a middleware file found below also which verifies the token
const express = require('express');
const users = express.Router();
const cors = require('cors');
const moment = require('moment');
const jwt = require('jsonwebtoken');
// var exjwt = require('express-jwt');
const auth = require('../middleware/auth');
const bcrypt = require('bcrypt');
const Sequelize = require('sequelize');
const bodyParser = require('body-parser');
const User = require('../models/User');
const config = require('config');
// const secret = 'dassdfdd';
users.use(
bodyParser.urlencoded({
extended: false
})
);
users.use(bodyParser.json());
users.use(cors());
users.post('/authenticate', (req, res) => {
User.findOne({
where: {
email: req.body.email
}
}).then(user => {
if (user) {
if (bcrypt.compareSync(req.body.password, user.password)) {
const payload = {
id: user.id,
name: user.first_name
};
var token = jwt.sign(payload, config.get('secret'), {
expiresIn: 1440 // expires in 24 hours
});
// res.cookie('auth', token);
res.cookie('jwt', token, { httpOnly: true, secure: true });
// return the information including token as JSON
// // res.setHeader('token', token);
// res.setHeader('Authorization', 'Bearer ' + token);
res.send({
message: 'authentication done ',
token: token,
user: user.toJSON()
});
console.log(token);
console.log('Successful Login');
console.log(user.first_name);
} else {
res.json({ message: 'please check your password !' });
console.log('incorrect password');
}
} else {
res.json({ message: 'user not found !' });
console.log('user cannot be found');
}
});
});
users.get('/protected', (req, res) => {
res.send('protected');
});
users.get('/getUser', auth, function(req, res) {
// const currentUser = req.;
// const id = parseInt(req.params.id);
const users = User.findOne({
where: { id: req.user.id }
});
// }
// });
// if (!users) {
// return res.status(404).send('Cannot find your team players');
// }
console;
res.status(200).json(users);
});
module.exports = users;
Login Component
import React, { Component } from 'react';
import axios from 'axios';
import { withRouter } from 'react-router-dom';
class Login extends Component {
constructor() {
super();
this.state = {
email: '',
password: '',
errors: {}
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
onSubmit(e) {
e.preventDefault();
// const user = {
// email: this.state.email,
// password: this.state.password
// };
axios
.post('http://localhost:5000/api/authenticate', {
email: this.state.email,
password: this.state.password
})
.then(res => {
localStorage.setItem('token', res.data.token);
this.props.history.push('/Profile');
});
}
auth.js this is my middleware file
const jwt = require('jsonwebtoken');
const config = require('config');
module.exports = function(req, res, next) {
const token = req.header('Authorization');
if (!token) {
return res.status(401).send('Access denied. No JWT provided.');
}
try {
const decoded = jwt.verify(token, config.get('secret'));
res.set('Authorization', token);
req.user = decoded;
next();
} catch (ex) {
res.status(400).send('Invalid JWT.');
}
};
Profile Component( this is the page , i want the users data to appear)
import React, { Component } from 'react';
import { getJwt } from '../helpers/jwt';
import axios from 'axios';
import { withRouter } from 'react-router-dom';
class Profile extends Component {
constructor(props) {
super(props);
this.state = {
// users: []
};
}
componentDidMount() {
const jwt = getJwt();
if (!jwt) {
this.props.history.push('/Login');
}
axios
.get('http://localhost:5000/api/getUser', {
headers: { Authorization: `Bearer ${jwt}` }
})
.then(res => {
this.profile = res.data;
console.log('profile is:', res.data);
})
.catch(error => console.log(error));
}
Inside your auth.js middleware file, you have const token = req.header('Authorization');. This includes the Bearer prefix which is not part of the JWT itself, and will need to be removed before the token can be parsed by the JWT library.
The Bearer prefix identifies the token type as a Bearer token under the OAuth 2.0 Authorization Framework. If you wish to support other token types the prefix will be different to identify the respective type and framework.
Related
Hello i am trying to use my token in my application after user is logged in but am getting an undefined response in my console. Below are my codes. How can i correct my code to be able to access token inside application and use to do other features of the application?
my controller
import User from "../models/user";
import Stripe from "stripe";
const stripe = Stripe(process.env.STRIPE_SECRET);
export const createConnectAccount = async (req, res) => {
console.log(req.user);
try {
const user = await User.findById(req.user._id).exec();
console.log("USER ==> ", user);
if (!user.stripe_account_id) {
const account = await stripe.accounts.create({
type: "express",
});
console.log("ACCOUNT ===>", account);
user.stripe_account_id = account.id;
user.save();
}
} catch (error) {
res.status(500).json();
}
};
my middleware
var { expressjwt: jwt } = require("express-jwt");
// req.user
export const requireSignin = jwt({
//secret, expiryDate
secret: process.env.JWT_SECRET,
algorithms: ["HS256"],
});
my routes
import express from "express";
const router = express.Router();
import { requireSignin } from "../middlewares";
import { createConnectAccount } from "../controllers/stripe";
router.post("/create-connect-account", requireSignin, createConnectAccount);
module.exports = router;
my auth controller
import User from "../models/user";
import jwt from "jsonwebtoken";
export const register = async (req, res) => {
console.log(req.body);
const { name, email, password } = req.body;
if (!name) return res.status(400).send("Name is required");
if (!password || password.length < 6)
return res
.status(400)
.send("Password is required and should be minimum 6 characters long");
let userExist = await User.findOne({ email }).exec();
if (userExist) return res.status(400).send("Email is taken");
const user = new User(req.body);
try {
await user.save();
console.log("User saved successfully", user);
return res.json({ ok: true });
} catch (err) {
console.log("CREATE USER FAILED", err);
return res.status(400).send("Error.Try again");
}
};
export const login = async (req, res) => {
// console.log(req.body);
const { email, password } = req.body;
try {
//check if user with credentials
let user = await User.findOne({ email }).exec();
// console.log("USER EXISTS", user);
if (!user) res.status(400).send("User with email not found");
//compare password
user.comparePassword(password, (err, match) => {
console.log("COMPARE PASSWORD IN LOGIN ERR", err);
if (!match || err) return res.status(400).send("Wrong password");
//("GENERATE A TOKEN THEN SEND AS RESPONSE TO CLIENT");
let token = jwt.sign({ _id: user._id }, process.env.JWT_SECRET, {
expiresIn: "7d",
});
res.json({
token,
user: {
_id: user._id,
name: user.name,
email: user.email,
createdAt: user.createdAt,
updatedAt: user.updatedAt,
},
});
});
} catch (err) {
console.log("LOGIN ERROR", err);
res.status(400).send("Signin failed");
}
};
my terminal output
POST /api/login 200 1142.309 ms - 349
undefined
POST /api/create-connect-account 500 9.092 ms - -
Headers
import axios from "axios";
export const createConnectAccount = async (token) => {
await axios.post(
`${process.env.REACT_APP_API}/create-connect-account`,
{},
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
};
I'm sorry to tell you your code has other errors in it.
My guess is that your res is not well written in auth controller, login function :
res.status(201).json({
token :token,
user: user
})
Also when reading your token trying to authenticate : it will be easier to use the same package than the one that sign it.
const jwt = require("jsonwebtoken");
exports. requireSignin = () => {
return async (req, res, next) => {
try {
const token = req?.headers?.authorization?.split(" ")[1];
const decodedToken = jwt.verify(token, process.env.JWT_SECRET);
const userId = decodedToken._id;
const user = await User.findOne({ _id: userId });
if (user) {
req.auth = {
user: user,
};
} else {
throw new Error("user not found");
}
next();
} catch (error) {
console.log(error.message);
res.status(401).json({ error: "failed to authenticate" });
}
};
};
But your code is pretty hard to read :
To make it easier to read and clearer for you, try and use joy or yup
Joi : https://www.npmjs.com/package/joi
Yup : https://www.npmjs.com/package/yup
With those you will be able to create middlewares to avoid wrong entries in your body : for example
if (!name) return res.status(400).send("Name is required");
is processed automatically with those packages
Also, you shouldn't use 'import' and 'require' in the same project, choose either one of them
I hope this will help
I am developing login and register services with Nodejs Express.
Every request in postman I get same error:
Funny thing is that I get response in postman (register, login i even receive my JWT token but every time after request I can't do anything without restarting the service in my terminal)
My index.ts
import express from "express";
const https = require("https");
import cors from "cors";
import mongoose from "mongoose";
const app = express();
//import routes
const usersRoute = require("./routes/users");
//Middleware
app.use(cors());
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
//route midddlewares
app.use("/api/users", usersRoute);
//connect to db
mongoose
.connect("mongodb://localhost:27017/loginregister")
.then(() => {
console.log("connected to database");
})
.catch(() => {
console.log("connection failed!");
});
const PORT = 3000;
app.listen(PORT, () => console.log(`Server up and running on port ${PORT}`));
And my users.ts
import express from "express";
const router = express.Router();
const User = require("../models/User");
const {
registerValidation,
loginValidation,
} = require("../middleware/validation");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const secretKey = "f43g34gergeerg";
const verifyToken = require("../middleware/verifyToken");
//REGISTER user
router.post("/register", async (req, res) => {
//VALIDATE DATA from Joi, before register
const { error } = registerValidation(req.body);
if (error) return res.status(400).send(error.details[0].message);
//check if user is alredy in database
const emailExist = await User.findOne({ email: req.body.email });
if (emailExist) return res.status(400).send("Email already exists");
//encrypt password z bcryptjs modulom
const salt = await bcrypt.genSalt(10);
const hashPassword = await bcrypt.hash(req.body.password, salt);
//create new user
const user = new User({
email: req.body.email,
password: hashPassword,
});
try {
//save new user
const savedUser = await user.save();
//res.json(savedUser);
res.json({ user: user._id });
} catch (err) {
res.json({ message: err });
}
});
//LOGIN
router.post("/login", async (req, res) => {
const { error } = loginValidation(req.body);
if (error) return res.status(400).send(error.details[0].message);
//check if email exists
const user = await User.findOne({ email: req.body.email });
if (!user) return res.status(400).send("Email doesn't exist");
//password is correct
const validPass = await bcrypt.compare(req.body.password, user.password);
if (!validPass) return res.status(400).send("Invalid password");
//create and send a json web token
const token = jwt.sign({ _id: user._id }, secretKey, { expiresIn: "1h" });
res.header("auth-token", token).send(token);
res.send("Logged in!");
});
module.exports = router;
File: verifyToken.ts
const jwt = require("jsonwebtoken");
const secretKey = "f43g34gergeerg";
module.exports = (req: any, res: any, next: any) => {
const token = req.header("auth-token");
if (!token) return res.status(401).send("Access denied");
try {
const verified = jwt.verify(token, secretKey);
req.user = verified;
next();
} catch (err) {
res.status(400).send("Invalid token");
}
};
My frontend (Angular) code:
login.component.ts
export class LoginComponent implements OnInit {
email: string = '';
password: string = '';
constructor(public authService: AuthService, private router: Router) {}
ngOnInit(): void {}
onLogin(form: NgForm) {
if (form.invalid) {
return;
}
this.authService.login(form.value.email, form.value.password);
console.log(form.value);
}
}
auth.service.ts file
export class AuthService {
constructor(private http: HttpClient) {}
login(email: string, password: string) {
const user: User = { email: email, password: password };
this.http
.post('http://localhost:3000/api/users/login', user)
.subscribe((response: any) => {
console.log(response);
});
}
}
And error in web is:
SOLVED:
First error in the terminal (Cannot set headers after they are sent to the client) was solved with the accepted answer from Shivam Sood) and for solving the second error in the error tab in my browser was solved by defining responseType: 'text' in my http.post() request in my auth.service.ts file
Error is coming from this line in your login route
res.header("auth-token", token).send(token);
res.send("Logged in!");
You are sending response twice res.send()
You will have to remove res.send("Logged in!"); in order to fix the issue.
UPDATE
I suspect the issue with angular is that, by default angular HTTP expects JSON data, but the data you are sending from backend is text. That is why parsing is failing.
You can update res.send to
res.header("auth-token", token).json({token});
I am working on a user registration project on ReactJS and got stuck on a problem when I try to activate the user account. The backend server is giving me the error 401: Failed to load resource: the server responded with a status of 401 (Unauthorized). What might be the problem here, I was not able to find out any solution related to my code, can anyone help ??
Here is my code for Activation.jsx:
import React, { useState, useEffect } from 'react';
import authSvg from '../assests/welcome.svg';
import axios from 'axios';
import jwt_decode from 'jwt-decode';
import { isAuth } from '../helpers/auth';
import { Navigate , useParams} from 'react-router-dom';
const Activate = ({ match }) => {
const [formData, setFormData] = useState({
name: '',
token: '',
show: true
});
match = useParams();
useEffect(() => {
let token = match.token;
console.log(token);
let { name } = jwt_decode(token);
if (token) {
setFormData({ ...formData, name, token });
}
console.log(token, name);
}, [match.params]);
const { name, token, show } = formData;
const handleSubmit = e => {
e.preventDefault();
axios
.post(`${process.env.REACT_APP_API_URL}/activation`, {
token
})
.then(res => {
setFormData({
...formData,
show: false
});
console.log(res.data.message);
})
.catch(err => {
console.log(err);
});
};
Here is the code from activation controller :
const User = require('../models/auth.model');
const expressJwt = require('express-jwt');
const _ = require('lodash');
const { OAuth2Client } = require('google-auth-library');
const fetch = require('node-fetch');
const { validationResult } = require('express-validator');
const jwt_decode = require('jwt-decode')
const { errorHandler } = require('../helpers/dbErrorHandling');
const nodemailer = require('nodemailer')
exports.activationController = (req, res) => {
const { token } = req.body;
if (token) {
jwt_decode.verify(token, process.env.JWT_ACCOUNT_ACTIVATION, (err, decoded) => {
if (err) {
console.log('Activation error');
return res.status(401).json({
errors: 'Expired link. Signup again'
});
} else {
const { name, email, password } = jwt_decode(token);
console.log(email);
const user = new User({
name,
email,
password
});
user.save((err, user) => {
if (err) {
console.log('Save error', errorHandler(err));
return res.status(401).json({
errors: errorHandler(err)
});
} else {
return res.json({
success: true,
message: user,
message: 'Signup success'
});
}
});
const apiKey = user._id;
user.apiKey = apiKey
user.save((err) => {
console.log(err);
})
}
});
} else {
return res.json({
message: 'error happening please try again'
});
}
};
As you have said in the comments , I think you have apiKey and path as an object properties in your user model which are not filled , and also the problem is you are using jwt_decode for verify and sign purposes which is wrong , the purpose for jwt_decode here is for decoding the token and nothing else , so use jsonwebtoken library for verfication and sign purposes .
Also remove the api key generation code , as what are you doing you are calling user.save() method 2 times in a row so the first one hasn't even finished saving before the 2nd one gets called .
I have a basic MERN login/signup app with a dummy protected route to be visible only to logged in users. I have set up everything related to login and sign up.
I am sending the jwt token from server in a cookie. The aprt up to this works fine. I can sign up and I can login.
But when I make a GET call to /protected route, passport.jwt doesn't seem to work. Nothing happens in there.
Here is my passport.js config:
import {Strategy} from 'passport-jwt';
// import {ExtractJwt} from 'passport-jwt';
import passport from 'passport';
import mongoose from 'mongoose';
import {UserSchema} from '../models/userModel';
import {config} from './config';
const User = mongoose.model('User', UserSchema);
const cookieExtractor = (req) => {
let token = null;
if(req && req.cookies) {
token = req.cookies['jwt'];
}
return token;
}
export const passportConfig = (passport) => {
const opts = {};
opts.jwtFromRequest = cookieExtractor;
opts.secretOrKey = config.key;
passport.use(
new Strategy(opts, (jwt_payload, done) => {
User.findById(jwt_payload.id)
.then((user) => {
if(user) {
return done(null, user);
}
return done(null, false);
})
.catch((err) => {
console.log('Error in finding user by id in jwt.');
});
})
);
};
Here is the userRoutes.js:
import { addNewUser, loginUser, verifyLogin } from '../controllers/userController';
export const routes = (app) => {
app.route('/users/register')
.post(addNewUser);
app.route('/users/login')
.post(loginUser);
app.route('/users/protected')
.get(verifyLogin);
}
Finally, here are the verifyLogin and loginUser controllers in my userController.js:
export const loginUser = (req, res) => {
console.log(req.body);
if(mongoSanitize.has(req.body)) {
return res.send(400).json({error: "Characters $ and . are prohibited. Use different values."});
}
const {errors, isValid} = validateLoginInput(req.body);
if(!isValid) {
return res.status(400).json(errors);
}
const userName = req.body.userName;
const password = req.body.password;
User.findOne({userName: userName})
.then((user) => {
if(!user) {
return res.status(404).json({userNotFound: "Username not found."});
}
else {
bcrypt.compare(password, user.password)
.then((isMatch) => {
if(isMatch) {
const payload = {
id: user.id,
name: user.fullName
};
const token = jwt.sign(payload, config.key, {expiresIn: 60});
res.cookie('jwt', token, {httpOnly: true, secure: false});
res.status(200).json({success: true, token: token});
}
else {
return res.status(400).json({incorrectPassword: "Password incorrect."});
}
})
}
});
};
export const verifyLogin = () => {
console.log('protected'); //'protected' is printed
passport.authenticate('jwt', {session: false}),
(req, res) => {
console.log('here'); //'here' is not printed
const { user } = req;
res.send(200).send({user});
};
};
The controller loginUser works just fine, I can see the token in response and also I have a cookie with the jwt key:value pair.
But the 3rd controller verifyLogin doesn't do anything after passport.authenticate call.
Any ideas where I might be doing something wrong ?
Okay so I was able to fix. Anyone else having the same problem, the issue was that I wasn't using passport.authenticate as a middleware, instead I was calling it as a function with a callback on successful authentication. Here are the change that I made:
userRoutes.js:
export const routes = (app) => {
app.route('/register')
.post(addNewUser);
app.route('/login')
.post(loginUser);
app.route('/protected')
.get(passport.authenticate('jwt', {session:false}), verifyLogin);
}
And then verifyLogin just sends the logged in user information back:
export const verifyLogin = (req, res) => {
console.log('here');
const { user } = req;
res.status(200).json({message:'Secure route', user: user});
};
Everything else is pretty much the same.
I had the same problem until I changed the time to 1 hour. It seems like the value you wrote is in milliseconds, so instead of 60 you should write 60000.
I need to pass a JWT with GET, POST, PUT, DELETE methods from my UI to my API.
For this I tried to create a front-end using ReactJS. I get:
POST http://localhost:4000/book/ 401 (Unauthorized)
when I try to send a request.
I know I have to pass my token, which I did via postman when I send data to mongo DB.
I'm new to MERN stack, so I am still trying to understand.
This is the book controller
import mongoose from 'mongoose';
import { book_schema } from '../model/book_model';
const Book = new mongoose.model('Book', book_schema);
export const add_new_book = (req, res) => {
let new_book = new Book(req.body);
new_book.save((err, book) => {
if (err) {
res.send(err);
}
res.json(book);
});
};
These are the book routes
import {add_new_book} from '../controller/book_controller';
import {authorization} from '../controller/user_controller';
const book_routes = (app) => {
//GET
app.route('/book')
//POST
.post(authorization,add_new_book);
}
This is the user controller
import mongoose from 'mongoose';
import bycrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { user_schema } from '../model/user_model';
const User = mongoose.model('User', user_schema);
export const register = (req, res) => {
const new_user = new User(req.body);
new_user.hash_password = bycrypt.hashSync(req.body.password, 10);
new_user.save((err, user) => {
if (err) {
return res.status(400).send({
message: err
});
} else {
user.hash_password = undefined;
return res.json(user);
}
});
};
export const authenticate = (req, res) => {
User.findOne({
user_name: req.body.user_name
}, (err, user) => {
if (err) throw err;
if (!user) {
res.status(401).json({ message: 'Authentication Failed ! - No User.' })
} else if (user) {
if (!user.comparePassword(req.body.password, user.hash_password)) {
res.status(401).json({ message: 'Authentication Failed ! - Wrong Password.' })
} else {
var token = res.json({
token: jwt.sign({ user_name: user.user_name }, 'RESTFULAPIs', { expiresIn: '24h' })
});
//$window.sessionStorage.accessToken = response.body.access_token;
return token;
}
}
});
};
export const authorization = (req, res, next) => {
if (req.user) {
next();
} else {
return res.status(401).json({ message: 'Unauthorized User !' })
}
};
export const de_authenticate = (req, res) => {
};
This is the server
import express from 'express';
import cors from 'cors';
import bodyParser from 'body-parser';
import mongoose from 'mongoose';
import jsonwebtoken from 'jsonwebtoken';
import db_config from './config_db.js';
const app = express();
const PORT = 4000;
//import routes here
import book_routes from './api/route/book_route';
import user_routes from './api/route/user_route';
import item_routes from './api/route/item_route';
//mongo DB connection
mongoose.Promise = global.Promise;
mongoose.connect(db_config.DB, { useNewUrlParser: true }).then(
() => { console.log('Database is connected') },
err => { console.log('Can not connect to the database' + err) }
);
app.use(cors());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
//JWT setup
app.use((req, res, next) => {
if (req.headers && req.headers.authorization && req.headers.authorization.split(' ')[0] === 'JWT') {
jsonwebtoken.verify(req.headers.authorization.split(''), [1], 'RESTFULAPIs', (err, decode) => {
if (err) req.user = undefined;
req.user = decode;
//console.log(req.user );
//console.log(decode);
next();
});
} else {
req.user = undefined;
next();
}
});
//to app
book_routes(app);
user_routes(app);
item_routes(app);
app.get('/', (req, res) =>
res.send(`Node Server and Express Server Running on Port : ${PORT}`)
);
app.listen(PORT, function () {
console.log(`Server is running on Port: ${PORT}`);
});
I developed front end using ReactJS and import axios for access API via URL. In insert_book.js file my form submission function is look like this,
onSubmit(e) {
e.preventDefault();
const book_obj = {
book_no:this.unique_id_generator(),
isbn_no: this.state.isbn_no,
author: this.state.author,
published_year: this.state.published_year,
publisher: this.state.publisher,
category:this.state.category
};
axios.post('http://localhost:4000/book/', book_obj)
.then(res => console.log(res.data));
this.setState({
isbn_no: '',
author:'',
published_year: '',
publisher: '',
category:''
})
}
Lastly I would like to provide user routes code,
import {register,
authenticate,
authorization
} from '../controller/user_controller';
const user_routes = (app) => {
//SIGNUP
app.route('/auth/signup')
//POST A USER
.post(register);
//SIGNIN
app.route('/auth/signin')
//AUTHENTICATE USER
.post(authenticate);
}
export default user_routes;
Here are the areas where I am stuck:
How could I store these tokens in sessions to send with future requests?
How to attach and send token value with GET, POST, PUT, DELETE methods?
so to store the token in local storage:
const user: User = getUserFromBackend()
// assuming user is an object
localStorage.setItem('token', user.token);
// get token
const token = localStorage.getItem('token');
to make a request with token header:
const token = localStorage.getItem('token');
fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify(data), // body data type must match "Content-Type" header
})
.then(response => response.json())
Although late to answer. I'd suggest storing JWT in cookie if possible. Then you just read the cookie and pass JWT in headers for the request where you need authentication. I prefer to call the header key x-api-token and set its value to the JWT string.
Moreover, you just need to authenticate the user once from your backend by decrypting this JWT and verifying. Once JWT is verified, you can create a session for authenticated user. This session expires in half hour or whatever you've configured in your App.
This way you don't need to send JWT again as the session is valid. I'm not saying that sending JWT is not good and sometimes required in applications where you don't create a session for authenticated user. This happens when performing operations requiring one time authentications like payment processing.
Implementing this logic shouldn't be a problem using node's JWT-decode package (https://www.npmjs.com/package/jwt-decode).
Hope this helps.