So I am trying to avoid requiring more than once and so I am designing all of my modules to take arguments from the main app.js file. I have a module for users to login that looks like this:
module.exports = (express, router, jwt, user, config) => {
function jwtSignUser (user) {
const ONE_WEEK = 60 * 60 * 24 * 7
return jwt.sign(user, config.authentication,jwtSecret, {
expiresIn: ONE_WEEK
})
}
router.post('/login', function(req, res, next) {
const result = user.findOne(
{
email: req.body.email
},
function (err, user) {
if (err) {
res.status(400).send(err)
}
console.log(user)
res.send({
user: user,
token: jwtSignUser(user)
})
}
)
})
return router
}
But when I try to run it I get an error TypeError: jwtSignUser is not a function
How can I use this function but still have the module take in the arguments necessary for the function (like jwt)? Is there a better way I can structure this? I am a newbie to Javascript
I don't know if this is the "best" way to do this. But I did some re-factoring and got it to work by doing the following:
var jwtSignUser = (jwt, config, user) => {
const ONE_WEEK = 60 * 60 * 24 * 7
return jwt.sign(user, config.authentication.jwtSecret, {
expiresIn: ONE_WEEK
})
}
var main = (express, router, jwt, user, config) => {
router.post('/login', function(req, res, next) {
const result = user.findOne(
{
email: req.body.email
},
function (err, user) {
if (err) {
res.status(400).send(err)
}
console.log(user)
res.send({
user: user,
token: jwtSignUser(jwt, config, user.toObject())
})
}
)
})
return router
}
module.exports = main
Update
This is not the true reason for why this code now works. There is a part where I have config.authentication,jwtSecret which should be config.authentication.jwtSecret. It now seems to work regardless of where I stick the jstSignUser function
Related
I use a sqlite3 database for a node.js express. If I return the request as in the tutorial in router.js it works and I get all the items. Now I have created a service to get the sql from the route (controller). But unfortunately I don't get anything back. I had already tried it with async await in the service. That didn't help either.
my code:
// router.js
const dbService = require("../services/dbService/");
router.get("/users", (req, res, next) => {
try {
res.status(200).send({
data: dbService.getAllUsers();
})
return;
} catch(err) {
next(err);
}
});
// dbService.js
const db = require("../db/database.js");
module.exports = {
getAllUsers() {
const sql = "select * from users";
db.all(sql,[], (err, rows) => {
return {"data": rows};
});
}
}
For simple reasons, I have not included error handling in the code. Why can't I get database values from the service? What do I have to do?
Thanks in advance! Mike
You're running afoul of asynchronous JS. db.all returns results to the callback.
A refactor to use callbacks would look something like:
// router.js
const dbService = require("../services/dbService/");
router.get("/users", (req, res, next) => {
dbService.getAllUsers((err, result) => {
if (err) next(err);
res.json({
data: result;
});
});
});
// dbService.js
const db = require("../db/database.js");
module.exports = {
getAllUsers(cb) {
const sql = "select * from users";
db.all(sql,[], (err, rows) => {
cb(err, rows);
});
}
}
And promises woudl look like:
// router.js
const dbService = require("../services/dbService/");
router.get("/users", async (req, res, next) => {
try {
const result = await dbService.getAllUsers();
res.json({
data: result;
});
} catch (err) {
next(err);
}
});
// dbService.js
const db = require("../db/database.js");
module.exports = {
getAllUsers(cb) {
const sql = "select * from users";
return new Promise((resolve, reject) =>
db.all(sql,[], (err, rows) => {
if (err) reject(err);
resolve(rows);
})
);
}
}
How to store and get req.user from JsonwebToken
I am developing a booking application using node the only thing left to do now is to get the user information who booked the product and display it in the admin portal
.then((user) => {
const maxAge = 3 * 60 * 60;
const token = jwt.sign(
{ id: user._id, username, role: user.role },
jwtSecret,
{
expiresIn: maxAge, // 3hrs
}
);
res.cookie("jwt", token, {
httpOnly: true,
maxAge: maxAge * 1000,
});
now i wanna access the user id from any router i have
Pass the token to your backend and deserialize it to get the data you need.
app.use("/your_route", async function (req, res, next)
{
console.log( req.headers.cookie);
var token = ""
//HERE GET YOUR JWT and put it in variable token
//jwtSecret is your secret jwt
var tokenData = jwt.verify(token, jwtSecret)
console.log(tokenData);
}
exports.userId = (req,res,next)=> {
const token=req.cookies.jwt;
jwt.verify(token, jwtSecret, (err, decodedToken) => {
req.userId= decodedToken.id;
})
next();
}
and this is the file where I want the userid
router.get("/",userId, async (req, res) => {
try {
const id = req.userId;
console.log(id);
} catch (e) {
console.log(e);
}
});
I have been building this project from a tutorial. The signup functionality works fine but the login feature doesn't work. Whenever I try logging in a registered user using postman the error I get is
Error: Unknown authentication strategy "local"
In the other posts on stack overflow, I didn't find a solution to this error. Passport, passport-local and passport-jwt are all installed so that shouldn't be the issue. I would really appreciate any sort of help.
passport.js
require('dotenv').config();
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const JWTStrategy = require('passport-jwt').Strategy;
const User = require('./models/User');
// Environment variables
const STRATEGY_KEY = process.env.STRATEGY_KEY;
const cookieExtractor = req => {
let token = null;
// Retrieve the token from cookies
if (req && req.cookies) {
token = req.cookies['access_token'];
}
return token;
};
const jwtOptions = {
jwtFromRequest: cookieExtractor,
secretOrKey: STRATEGY_KEY,
};
// Authorization for protected routes
passport.use(
new JWTStrategy(jwtOptions, (payload, done) => {
User.findById({ _id: payload.sub }, (err, user) => {
// Check for error
if (err) return done(err, false);
// Check if user exists
if (user) return done(null, user);
return done(null, false);
});
})
);
// Local strategy using username and password
passport.use(
new LocalStrategy((username, password, done) => {
User.findOne({ username }, (err, user) => {
// Error while fetching the user from database
if (err) return done(err);
// No such user exists
if (!user) return done(null, false);
// Check if entered password matches
user.comparePassword(password, done);
});
})
);
routes.js
require('dotenv').config();
const express = require('express');
const passport = require('passport');
const router = express.Router();
const STRATEGY_KEY = process.env.STRATEGY_KEY;
const signToken = userID => {
return jwt.sign(
{
iss: STRATEGY_KEY,
sub: userID,
},
STRATEGY_KEY,
{
expiresIn: '1h',
}
);
};
router.post(
'/signin',
passport.authenticate('local', { session: false }),
(req, res) => {
if (req.isAuthenticated()) {
const { _id, username, email } = req.user;
const token = signToken(_id);
res.cookie('access_token', token, {
httpOnly: true,
sameSite: true,
});
res.status(200).json({
isAuthenticated: true,
user: {
username,
email,
},
});
}
}
);
module.exports = router;
So after many hours of debugging, the solution I found to this problem was that I didn't import passport.js file in routes.js file, which I was not expecting since that import stays there ideal not doing anything, not being part of any code(exceot the import) but I was wrong. The passport configuration we make in that file is imported under the hood even though it doesn't take part in any further lines of that file.
So, I'm creating an Auth module for my backend that works with both nodejs/express & vue. I've got the node/express side down but im having trouble also using vue within the module. Maybe I'm just not doing it right? I'm not really sure how to even do it at the moment. Here is my app.js:
const bcrypt = require('bcryptjs');
const db = require('./db/index');
const jwt = require('./jwt')
/**
* #user User object passed from register form. Requires a username/password
* #return True / False based on if user was registered or not
*/
exports.register = function(user) {
return new Promise(function(resolve, reject){
bcrypt.hash(user.password, 10)
.then((hash) => {
user.password = hash;
db.createUser(user).then((res) => {
res ? resolve(res) : reject('Error Occured')
}).catch((err) => {console.log(err)})
})
.catch((err)=> {console.log(err)})
})
}
/**
* #username Username from form
* #password Password from form
* #return JWT Token applied to username or null
*/
exports.login = function(username, password) {
return new Promise(function(resolve, reject){
db.getUserByName(username).then((user) => {
bcrypt.compare(password, user.password, function(err, res) {
if(res) {
delete user.password
jwt.assignToken(user).then((token) => {
resolve(token)
}).catch((err)=> { reject(err) })
} else {
reject(err)
}
});
}).catch((err)=>{ reject(err) })
})
}
and this would be my vue.js within the same folder
const app = require('./app')
exports.install = function (Vue, options){
Vue.prototype.$login = function(){
app.login()
}
Vue.prototype.$register = function(){
app.register()
}
}
Obviously its just basic as i havn't started any work on it yet but it would use functions from app.js. Whats the most efficient way to incorporate this so I can use one module for both my vue/nuxt frontend and my express backend?
I have an overloaded fetch function on an Angular service 'PostSvc'
if a user is provided as an argument the idea is to only return that users posts. Otherwise fetch all posts from the database....
The user is an 'object' passed from the post.ctrl.js with the following keys
{
username: $scope.currentUser,
body: $scope.postBody,
user_id: $scope.currentUser._id
}
this is giving me the _id field mongodb/mongoose generate as expected
console.log($scope.currentUser._id)
Here's the Mongoose Post model:
var db = require('../db')
var Post = db.model('Post', {
username : { type: String, required: true },
body : { type: String, required: true },
user_id: { type: String, required: true },
date : { type: String, required: true, default: Date.now },
})
module.exports = Post
Here's a snippet from the server.js file assigning the router:
app.use("/api/posts", require("./controllers/api/posts"))
Here's the Express './controllers/api/post.js' Router:
var Post = require('../../models/post')
var router = require('express').Router()
var websockets = require('../../websockets')
// this route works just fine
// returns All posts from the db
//
router.get("/", function(req, res, next) {
// find all posts from db
//
Post.find()
.sort("-date")
.exec(function(err, posts) {
if (err) { return next(err) }
res.json(posts)
next()
})
})
the probelem....
router.get("/:user_id", function(req, res, next) {
var query = {}
// these messages aren't being logged to console
// so there's no way the route is being used
console.log("from the get 'api/posts' method" )
console.dir(req.params.user_id)
if (req.params.user_id) {
query = { user_id: req.params.user_id } // sent from posts.svc.js
}
// this query is not executing properly
// I have no access to the `req.params.user_id`
//
Post.find({ user_id: req.params.user_id })
.sort("-date")
.exec(function(err, posts) {
if (err) { return next(err) }
res.json(posts)
next()
})
})
router.post("/", function(req, res, next) {
var post = new Post({ body: req.body.body })
if (!req.auth) { return res.sendStatus(401) }
post.username = req.auth.username
post.user_id = req.body.user_id
post.save(function(err, post) {
if (err) { return next(err) }
websockets.broadcast("new_post", post)
res.status(201).json(post)
})
})
module.exports = router
And last but not least here is the Angular Service that sends the initial 'GET' request to the 'server.js' -> './controllers/api/posts.js' and awaits the response:
angular.module("app").service('PostsSvc', function($http) {
this.fetch = function(user) {
var credentials = {}
if (user) {
credentials = user
// checkpoint
//
console.dir("from the fetch function " + credentials._id)
// only return authencated users posts
return $http.get("/api/posts", {
params: {
user_id: credentials._id // to the posts.js router
}
})
// return all posts otherwise
} else { return $http.get("/api/posts") }
}
this.create = function(post) {
return $http.post("/api/posts", post)
}
})
I have being in this same trouble with the router.get in express. The body is empty. I finally turned to use instead a router.post() which worked perfectly for me, as long as you also call the api with a post request whit angular.