Jwt authorization/ passport - node.js

Hello I have a question about JWT, auth:
I created my auth class:
const passport = require('passport');
const Strategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
module.exports = (app) => {
const jwtConfig = app.config.jwt;
const Users = app.datasource.models.tb_users;
const options = {};
options.secretOrKey = jwtConfig.secret;
options.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
const strategy = new Strategy(options,(payload, done) => {
Users
.findOne({where: payload.id})
.then(user => {
if(user){
return done(null,{
id: user.id,
login: user.login
});
}
return done(null,false);
})
.catch(error => done(error,null));
});
passport.use(strategy);
return {
initialize: () => passport.initialize(),
authenticate: () => passport.authenticate('jwt', jwtConfig.session)
};
}
this is my app.js:
const express = require('express');
const bodyParser = require('body-parser');
const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
const authRouter = require('./routes/auth');
const authorization = require('./auth');
const config = require('./config/config');
const datasource = require('./config/datasource');
const Email = require('./utils/email');
const app = express();
const port = 3000;
app.set('port',port);
app.config = config;
app.email = new Email(app.config);
app.datasource = datasource(app);
console.log(app.config);
app.use(bodyParser.json({
limit: '5mb'
}));
const auth = authorization(app);
app.use(auth.initialize());
app.auth = auth;
indexRouter(app);
usersRouter(app);
authRouter(app);
module.exports = app;
And then I have my token login and validation method
app.route('/login')
.post(async (req,res)=>{
try {
const response = await usersControllers.signin(req.body);
const login = response.login;
console.log(login);
if(login.id && login.isValid){
const payload = {id: login.id};
res.json({
token: jwt.sign({data:payload}, app.config.jwt.secret,{expiresIn: '1h'})
});
}else{
console.log('entrou here');
res.sendStatus(HttpStatus.UNAUTHORIZED);
}
} catch (error) {
console.log('entrou here');
console.error(error.message);
res.sendStatus(HttpStatus.UNAUTHORIZED);
}
})
and in my method of searching all users I call this authorization:
app.route('/users')
.all(app.auth.authenticate())
.get((req,res)=>{
usersController
.getAll()
.then(data => {
res.json(data);
})
.catch(error=>{
console.log(error);
res.status(400);
});
})
here: .all(app.auth.authenticate())
At this point I start to get confused
in insomnia he if I go in the login route and then in the get all users route he gives unauthorized even though being authorized by the login route
I would like to know what I could do so I could use my get route with my token
Is this front end work?
Basically I would like to know how I would do to access other routes with my token other than insomnia
And what I could improve on my code is missing something to achieve this?

Not exactly clear what is being asked and you are referencing "insomnia" which is not represented in the code you have shared. You have asked about the token work on the frontend and will share some thoughts.
You need to save the token on the client when it is returned from POST to /login. As you have specified the that passport should attempt to extract the token from the request headers: options.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken(); you will need to send the token within your headers when you are attempting to access secured routes. This is added as key/value to "Authorization" in the format "Bearer " + token.
For example using chaihttp test runner to post data to a secured route:
chai.request(server)
.post(`/books`)
.set('Authorization', `Bearer ${token.token}`) // set the auth header with the token
.send(data)
...

Related

Post request stuck pending when returning a cookie

Im learning to use JWT. I setup a very simple react app and an express server that uses a json file as database to try it but when i return a cookie via res.cookie the request is stuck on pending according to my browsers network tab.
This is how my server looks
const express = require("express");
const cors = require("cors");
const cookieParser = require("cookie-parser");
const jwt = require("jsonwebtoken");
const { JsonDB, Config } = require("node-json-db");
require("dotenv").config();
const server = express();
server.use(express.json());
server.use(cors());
server.use(cookieParser());
const db = new JsonDB(new Config("db", true, false));
server.post("/login", async (req, res) => {
const { username, password } = req.body;
const data = await db.getData("/users");
const user = data.find((user) => {
return user.username === username && user.password === password;
});
if (!user) {
return res.status(403).json({
error: "invalid login",
});
}
const token = jwt.sign(user, process.env.MY_SECRET, { expiresIn: "1h" });
return res.cookie("token", token, { httpOnly: true });
});
server.get("/logout", (req, res) => {
console.log("cleared token");
return res.clearCookie("token");
});
server.listen(3000, () => {
console.log("server listening on port 3000");
});
And this is my request in my react app
const handleSubmit = async () => {
const username = usernameRef.current.value;
const password = passwordRef.current.value;
const response = await axios.post("http://localhost:3000/login", {
username: username,
password: password,
});
console.log(response);
};
I've tried changing the order of the middlewares around and adding next() at the end but didn't work. Sending data via res.send works just fine. I've worked with express about a year ago but never ran into this.
res.cookie only set cookie. It doesn't response to client. After res.cookie, use res.send, res.json or any method to response.

Possible to pass a parameter into Express' app.use()

I have my Node/Express server running. I have the main server.js file where most of the code is for the server. Now I want to separate out the routes into a separate file. I have done this before using app.use(routes). But the problem is, I want to pass a string in for one of the routes to use.
Here is my server.js code:
// other imports
import routes from './routes.js';
const app = express();
...
const port = Number.parseInt(process.env.PORT, 10) || 3001;
const serverType = process.env.NODE_ENV === 'production' ? 'Production' : 'Test';
const statusMsg = `${serverType} Node server for external facing web server on ${port}`;
// i want `routes` to have access to `statusMsg`
app.use(routes);
Then in routes.js:
import express from 'express';
const router = express.Router();
router.get('/', (req, res) => res.status(200).send(statusMsg);
export default router;
I use serverType and port elsewhere in server.js, else I would just move all that code to routes.js.
Update
Adding in updated routes.js as I understand it with suggestion from jonrsharpe.
import express from 'express';
const router = express.Router();
const createRoutes = (statusMsg) => {
router.get('/', (req, res) => res.status(200).send(statusMsg);
};
export default createRoutes;
You can separate the server logic, routes logic, and business logic (usually inside a separate file called a controller).
inside the server file try blow code:
const express = require('express');
const app = express();
const bodyParser = require('body-parser')
require('dotenv').config();
const connectDB = require('./config/config')
const cookieParser = require('cookie-parser')
const authRoutes = require('./routes/authRoutes')
const categoryRoutes = require('./routes/categoryRoutes')
const cors = require('cors');
connectDB();
app.use(bodyParser.json());
app.use(cookieParser());
app.use(cors());
//Route Mounting
app.use('/', authRoutes);
app.use('/', categoryRoutes);
app.listen(process.env.PORT, ()=>{
console.log(`Server is running on PORT: ${process.env.PORT}`)
})
Then create a separate file authRoute.js and do the following code
const express = require('express')
const { registerUser, loginUser, getAllUsers, logoutUser} = require('../controllers/authController')
const router = express.Router()
const {isAuthenticatedUser, isAuthorizedRoles} = require('../middleware/auth')
router.route('/user/new').post(registerUser);
router.route('/user/login').post(loginUser);
router.route('/users').get(getAllUsers);
router.route('/account/logout').get(logoutUser);
module.exports = router;
lastly to write the business logic create file authController.js and place the following code.
const User = require('../model/userSchema');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
require('dotenv').config();
exports.registerUser = async (req, re, next)=>{
let password = req.body.password;
password = await bcrypt.hash(password, 10);
const newUser = req.body;
newUser.password = password;
try {
const user = await User.create(newUser);
if(user){
res.json({
success:true,
user
});
}
} catch (error) {
console.log(error);
}
}
exports.loginUser = async function(req, res){
const email = req.body.email;
const password = req.body.password;
if(!email || !password){
return res.json({
success:false,
message:'Please provide the email & Password'
})
}
const user = await User.findOne({email:email});
if(!user){
return res.json({
success:false,
message:'user with this email not found in database'
})
}
const isPasswordMatch = await bcrypt.compare(password, user.password);
if(!isPasswordMatch){
return res.json({
success:false,
message:'Your password is wrong...'
})
}
const token = jwt.sign({ _id: user._id }, process.env.JWT_SECRET, { expiresIn: process.env.JWT_EXPIRY_TIME });
res.cookie('token', token, {httpOnly:true, expires:new Date(Date.now() + 60*60*1000 )}).json({
success:true,
message:'You are logged in! Enjoy',
})
}
exports.getAllUsers = async (req, res, next)=>{
res.json({
success:true,
data:[
{
"id":1,
"name":"Yasir",
"qual":"MCS",
"age":32
},
{
"id":2,
"name":"Tabish",
"qual":"BS",
"age":21
},
{
"id":3,
"name":"Ahmed",
"qual":"BSCS",
"age":32
},
{
"id":4,
"name":"Hassan",
"qual":"MCS",
"age":33
}
]
})
}
exports.logoutUser = async (req, res, next)=>{
res.cookie('token', null, {expires:new Date(Date.now())}).json({
success:true,
message:'You are loggedOut',
})
}
This is the way you can have separation of concerns.

403 forbidden expressjs backend

GET http://localhost:5000/booksIdea/show 403 (Forbidden)
i check the token in the website https://jwt.io/ i got invalid signature so i guess why the problem came from but i ignore how to fix it
i searched abt this error and this is what i found : Receiving a 403 response is the server telling you, “I’m sorry. I know who you are–I believe who you say you are–but you just don’t have permission to access this resource. Maybe if you ask the system administrator nicely, you’ll get permission. But please don’t bother me again until your predicament changes.”
API GET function on front end:
import axios from 'axios'
export const ShowBooks = () => {
let token = localStorage.getItem("usertoken")
return axios.get("http://localhost:5000/booksIdea/show", {
headers: {
Authorization: `Bearer ${token}`, //here remove + in template litereal
},
})
.then(res => {
console.log("Success")
})
.catch(error => {
console.log(error)
})
}
backend app.js
const express = require('express')
var cookieParser = require('cookie-parser')
const app = express()
var cors = require('cors')
var bodyParser = require('body-parser')
const port = 5000
const routes = require("./routes");
const con = require('./db')
var cors = require('cors')
app.use(cors())
// database connect
con.connect(function(err) {
if (err) throw err;
console.log("Connected!");
});
//cookie
app.use(cookieParser())
//routes
// support parsing of application/json type post data
app.use(bodyParser.json());
//support parsing of application/x-www-form-urlencoded post data
app.use(bodyParser.urlencoded({ extended: true }));
app.use("/", routes);
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
here is routes
var express = require('express')
var router = express.Router()
var Controller = require('./controller')
var authController = require('./authController')
var BooksIdeaController = require('./BooksIdeaController')
router.post('/register',Controller.register);
router.post('/login',authController.login);
router.post('/booksIdea/:id',authController.verify,BooksIdeaController.addComment)
router.post('/booksIdea/addbook',authController.verify,BooksIdeaController.addBookIdea)
router.get('/booksIdea/show',authController.verify,BooksIdeaController.showBookIdea)
router.put('/booksIdea/edit/:id',authController.verify,BooksIdeaController.UpdateBookIdea)
router.delete('/booksIdea/delete/:id',authController.verify,BooksIdeaController.DeleteBookIdea)
module.exports = router;
authController
const con = require('./db');
var bcrypt = require('bcrypt');
let jwt = require('jsonwebtoken');
const express = require('express')
var cookieParser = require('cookie-parser')
const app = express()
module.exports.login=function(req,res){
var username=req.body.name;
var password=req.body.password;
con.query('SELECT * FROM users WHERE username = ?',[username], function (error, results, fields) {
if (error) {
res.json({
status:false,
message:'there are some error with query'
})
}else{
if(results.length >0){
bcrypt.compare(password, results[0].password, function (err, result) {
if (result == true) {
jwt.sign({user:results},'configSecret',(err,token)=>{
res.json({
token:token
})
});
// res.json({
// status:true,
// message:'successfully authenticated'
// })
} else {
res.json({
status:false,
message:"username and password does not match"
});
}
});
}
else{
res.json({
status:false,
message:"username does not exits"
});
}
}
});
}
module.exports.home=function(req,res){
res.send('hello');
}
//////
// if(password==results[0].password){
// }else{
//
// }
module.exports.verify = function verifyToken(req, res, next) {
// Get auth header value
const bearerHeader = req.headers['authorization'];
// Check if bearer is undefined
if(typeof bearerHeader !== 'undefined') {
// Split at the space
const bearer = bearerHeader.split(' ');
// Get token from array
const bearerToken = bearer[1];
// Set the token
req.token = bearerToken;
// Next middleware
next();
} else {
// Forbidden
res.sendStatus(403);
}
}
How can I fix this error? thank you in advance for your help
Check your localstorage localStorage.getItem("usertoken")
Your token can be:
missing or undefined
incorrect token - probably a typo

Route declared properly but still getting a Could not get any response error

I have making an API using express and node.
Here is my app.js
const express = require('express');
const bodyParser = require('body-parser');
const dotenv = require('dotenv');
// setup dotenv to read environment variables
dotenv.config()
// Load Environment Varibles
const env = require('./utils/env');
// INIT MONGODB CONNECTION
require('./mongoose');
// create a new express application
const app = express();
// setup bodyparser middleware to read request body in requests
// we're only reading JSON inputs
app.use(bodyParser.json());
// Listen to API routes
const apiRoutes = require('./routes')
app.use('/api', apiRoutes);
// Start listening to requests
app.listen(env.PORT, () => {
console.log(`Server started on PORT ${env.PORT}`);
});
And here is the API routes that are being imported
const express = require('express');
const apiController = require('./apiController');
const apiValidator = require('./apiValidator');
const router = express.Router();
router.post('/login', apiValidator.loginUserValidator, apiController.loginUserController);
router.get('/rand', (req, res) => {
res.send('Some randon text');
});
module.exports = router;
Here is the middleware
const {
failureResponse
} = require('./../utils/response');
const errorcodes = require('./../utils/errorcodes');
const loginUserValidator = (req, res, next) => {
const user = req.body;
if (!user.username) {
return res.status(400).json(failureResponse(errorcodes.ERROR_INVALID_BODY_PARAMETER, "Invalid username"));
}
if (!user.password) {
return res.status(400).json(failureResponse(errorcodes.ERROR_INVALID_BODY_PARAMETER, "Invalid password"));
}
if (user.authTokens) {
delete user.authTokens;
}
next();
};
module.exports = {
loginUserValidator
};
Here is the controller
const User = require('./../models/user');
const {
successResponse,
failureResponse
} = require('./../utils/response');
const errorcodes = require('./../utils/errorcodes');
const loginUserController = async (req, res) => {
try {
const user = req.body;
// find if the user already exists
const existingUser = await User.findOne({
username: user.username
});
if (existingUser) {
// user exists. generate token and login user
console.log('Existing user login');
const token = existingUser.generateAuthToken();
return res.status(200).json(successResponse(token));
} else {
console.log('New user login');
const savedUser = await new User(user).save();
const token = savedUser.generateAuthToken();
return res.status(200).json(successResponse(token));
}
} catch (e) {
console.log(e);
return res.status(400).json(failureResponse(errorcodes.ERROR_SERVER_ERROR, "Unable to login user"));
}
};
module.exports = {
loginUserController
};
Here the issue is when I try to hit the login route from Postman, I am getting an error which says Could not get any response.
But when I hit the rand route, the output is correct.
So the issue isn't the arrangement of the code.
Why am I not able to use the login route here?

Mongo/express cannot load route (Unexpected '<' in postman)

Been trying to get user user object by id from parameters, using User.findById and cannot access this route.
Postman response: Cannot GET /users/get/ with status 404 (not found) which is wierd.
Postman JSON response:
Unexpected '<'
Been using express-promise-router but also tried with default express.Router();
Here is my code:
routes/users.js
const express = require('express');
const router = require('express-promise-router')();
const passport = require('passport');
const router1 = express.Router();
require('../passport');
const { validateBody, schemas } = require('../helpers/routeHelpers');
const UsersController = require('../controllers/users');
const passportSignIn = passport.authenticate('local', { session: false });
const passportJWT = passport.authenticate('jwt', { session: false });
router.route('/signup')
.post(validateBody(schemas.authSchema), UsersController.signUp);
router.route('/signin')
.post(validateBody(schemas.authSchema), passportSignIn, UsersController.signIn);
router.route('/get/:id')
.get(UsersController.getUser);
router.route('/secret')
.get(passportJWT, UsersController.secret);
controllers/users
module.exports = router;
const JWT = require('jsonwebtoken');
const User = require('../models/user');
const { JWT_SECRET } = require('../configuration');
const signToken = (user) => {
return JWT.sign({
iss: 'CodeWorkr',
sub: user.id,
iat: new Date().getTime(), // current time
exp: new Date().setDate(new Date().getDate() + 1) // current time + 1 day ahead
}, JWT_SECRET);
};
module.exports = {
signUp: async (req, res) => {
const { email, password } = req.value.body;
// Check if there is a user with the same email
const foundUser = await User.findOne({ 'local.email': email });
if (foundUser) {
return res.status(403).json({ error: 'Email is already in use' });
}
// Create a new user
const newUser = new User({
method: 'local',
local: {
email: email,
password: password
}
});
await newUser.save();
// Generate the token
const token = signToken(newUser);
// Respond with token
return res.status(200).json({ token });
},
signIn: async (req, res) => {
// Generate token
const token = signToken(req.user);
res.status(200).json({ token });
},
getUser: async (req, res) => {
User.findById(req.params.id)
.then((user) => {
res.status(200).json({ user });
console.log('test');
});
},
secret: async (req, res) => {
console.log('I managed to get here!');
res.json({ secret: 'resource' });
}
};
server.js
const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const cors = require('cors');
const mongoose = require('mongoose');
const passport = require('passport');
const db = require('./configuration/config').mongoURI;
const dbTest = require('./configuration/config').mongoURITest;
mongoose.Promise = global.Promise;
if (process.env.NODE_ENV === 'test') {
mongoose.connect(dbTest, { useMongoClient: true });
} else {
mongoose.connect(db, { useMongoClient: true });
}
const app = express();
app.use(cors());
app.use(passport.initialize());
app.use(passport.session());
// Middlewares moved morgan into if for clear tests
if (!process.env.NODE_ENV === 'test') {
app.use(morgan('dev'));
}
app.use(bodyParser.json());
// Routes
app.use('/users', require('./routes/users'));
// Start the server
const port = process.env.PORT || 3001;
app.listen(port);
console.log(`Server listening at ${port}`);
Other post routes works fine,

Resources