I have set up express to use the following paths:
const profile = require("./api/profile")
const events = require("./api/events")
app.use("/api/events", events)
app.use("/api/profile", profile)
Inside the events and profile index.js files I have the following:
const router = require('./../../modules/router.js')
router.get('/', (req, res) => {
})
module.exports = router
My router.js file:
const express = require("express")
const cookieParser = require('cookie-parser')()
const cors = require('cors')({origin: true})
const router = express.Router()
const firebase = require("./firebase.js")
// https://github.com/firebase/functions-samples/tree/master/authorized-https-endpoint
// Must have header 'Authorization: Bearer <Firebase ID Token>'
const validateFirebaseIdToken = (req, res, next) => {
if ((!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) &&
!req.cookies.__session) {
res.status(403).send({ "error": 'Unauthorized'})
return
}
let idToken
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
// Read the ID Token from the Authorization header.
idToken = req.headers.authorization.split('Bearer ')[1]
} else {
// Read the ID Token from cookie.
idToken = req.cookies.__session
}
firebase.admin.auth().verifyIdToken(idToken).then((decodedIdToken) => {
req.user = decodedIdToken
return next()
}).catch(error => {
res.status(403).send({"error": 'Unauthorized'})
})
}
router.use(cors)
router.use(cookieParser)
router.use(validateFirebaseIdToken)
module.exports = router
For some reason, the router mixes up the paths /api/events/ and /api/profile/ whenever I call them. For all other paths it works fine. How can I stop this from happening?
If you are using the same router for both events and profile, it could be the source of your issue.
Have you tested to create one router for each module?
Maybe try something like this for both events and profile:
const router = require('express').Router()
router.get('/', (req, res) => {
})
module.exports = router
Related
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.
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)
...
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?
When I create a router I need to add the following code in each module:
const express = require("express")
const cookieParser = require('cookie-parser')()
const cors = require('cors')({origin: true})
const router = express.Router()
const firebase = require("./firebase.js")
// https://github.com/firebase/functions-samples/tree/master/authorized-https-endpoint
// Must have header 'Authorization: Bearer <Firebase ID Token>'
const validateFirebaseIdToken = (req, res, next) => {
if ((!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) &&
!req.cookies.__session) {
res.status(403).send({ "error": 'Unauthorized'})
return
}
let idToken
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
// Read the ID Token from the Authorization header.
idToken = req.headers.authorization.split('Bearer ')[1]
} else {
// Read the ID Token from cookie.
idToken = req.cookies.__session
}
firebase.admin.auth().verifyIdToken(idToken).then((decodedIdToken) => {
req.user = decodedIdToken
return next()
}).catch(error => {
res.status(403).send({"error": 'Unauthorized'})
})
}
router.use(cors)
router.use(cookieParser)
router.use(validateFirebaseIdToken)
// api functions go here
module.exports = router
Initially I had this in a separate file and was sharing the router across modules, however, sharing caused problems. I don't want to copy and paste this code in each module file... How can I make sure that all routers are created like this with out having to copy and paste?
It's difficult to see exactly which part you want to share, but if you pull the validateFirebaseIdToken function into its own file, then you can import it like anything else. This is important because you can easily use it wherever you want. You may have routes which don't require authentication... in which case you wouldn't use this middleware.
validate-firebase-token.js
const firebase = require("./firebase.js")
const validateFirebaseIdToken = (req, res, next) => {
if ((!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) &&
!req.cookies.__session) {
res.status(403).send({ "error": 'Unauthorized'})
return
}
let idToken
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
// Read the ID Token from the Authorization header.
idToken = req.headers.authorization.split('Bearer ')[1]
} else {
// Read the ID Token from cookie.
idToken = req.cookies.__session
}
firebase.admin.auth().verifyIdToken(idToken).then((decodedIdToken) => {
req.user = decodedIdToken
return next()
}).catch(error => {
res.status(403).send({"error": 'Unauthorized'})
})
}
module.exports = validateFirebaseIdToken
... and now your router code looks like this:
const express = require("express")
const cookieParser = require('cookie-parser')()
const cors = require('cors')({origin: true})
const router = express.Router()
const validateFirebaseIdToken = require('./validate-firebase-token')
router.use(cors)
router.use(cookieParser)
router.use(validateFirebaseIdToken)
// api functions go here
module.exports = router
As stated by someone in the answers to my other question, you can apply middleware on to the app rather than a specific router. That way the middleware applies to all requests!
verify_jwt_token
var jwt = require('jsonwebtoken');
var config = require('./env/config.json')
module.exports = function(req,res,next) {
var token = req.body.token || req.query.token || req.headers['x-access-token'];
if (token) {
console.log("----------------------------------------");
console.log(token);
console.log("----------------------------------------");
console.log(config.jwt_secret);
console.log("----------------------------------------");
// verifies secret and checks exp
jwt.verify(token, config.jwt_secret, function(err, decoded) {
if (err) { //failed verification.
return res.json({"error": true});
}
req.decoded = decoded;
next(); //no error, proceed
});
} else {
// forbidden without token
return res.status(403).send({
"error": true
});
}
}
Routes
var user = require('../controller/user.controller.js');
var token_verify = require('../../config/verify_jwt_token.js')
module.exports = function (app) {
app.route('/register_user').post(user.register);
app.route('/login').get(user.login);
app.route('/auth',token_verify).get(user.auth);
}
I want to call j w t verification function from this route.I don't want to call from the controller. app.route('/auth',token_verify).get(user.auth); Calling from this route
Please help.
Thank you in advance.
This will work-ish:
const express = require('express')
const app = express()
function jwtVerify (req, res, next) {
console.log('verifying token...')
// logic
next()
}
app
.use(jwtVerify)
.route('/example')
.get((req, res) => res.json({ hello: 'World' }))
app.listen(3000)
The issue you will run into is that the middleware will execute for every route you define because the middleware is applied to the app instance. To fix this, you need to make use of the Router. Quick dirty example below:
app.js
const express = require('express')
const app = express()
const securedRoutes = require('./secured-routes')
function jwtVerify (req, res, next) {
console.log('verifying token...')
// logic
next()
}
app.use('/secured', jwtVerify, securedRoutes)
app.listen(3000)
secured-routes.js
const express = require('express')
const router = express.Router()
router.get('/', (req, res) => res.json({ message: 'Hello from secured routes' }))
module.exports = router