In the user.js file, I created the token here with this code
if (user && bcrypt.compareSync(req.body.password, user.passwordHash)) {
const token = jwt.sign(
{
userId: user.id,
isAdmin: user.isAdmin,
},
process.env.SECRET,
{
expiresIn: "50d", // >> on day
}
);
And the token work and everything is ok, But I want to use the token somewhere else, for example, here in cupon.js file
router.post("/cupon", async (req, res) => {
...
const token = req.header("authorization").substring(7);
...
I used this
const token = req.header("authorization").substring(7);
to get the token from the header, Is there a better way to get the token?
You can create a separate middleware for authentication and authorisation. It will help you to reuse it any where or in multiple routes, you can use the same auth middleware and if every thing goes good in auth, you can call next else send the response with 401 status code.
In Auth.js
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
try {
const token = req.headers.authorization.split(' ')[1];
const decodedToken = jwt.verify(token, 'RANDOM_TOKEN_SECRET');
const userId = decodedToken.userId;
if (req.body.userId && req.body.userId !== userId) {
throw 'Invalid user ID';
} else {
next();
}
} catch {
res.status(401).json({
error: new Error('Invalid request!')
});
}
};
Import this Auth.js and pass it to your routes which needs it. This way you can unit test your auth layer and can re-use it anywhere in the code. The below code is for sample purpose:
const express = require('express');
const router = express.Router();
const auth = require('../middleware/auth');
const stuffCtrl = require('../controllers/stuff');
router.get('/', auth, stuffCtrl.getAllStuff);
router.post('/', auth, stuffCtrl.createThing);
router.get('/:id', auth, stuffCtrl.getOneThing);
router.put('/:id', auth, stuffCtrl.modifyThing);
router.delete('/:id', auth, stuffCtrl.deleteThing);
module.exports = router;
For more details, you can check this link and follow along.
Related
I'm working on a project and i can't seem to able to make jwt work, everytime i try to use authorization in my header and provide a valid token, my server crashes and it gives me the error in the title.
Here's my middleware for jwt :
const jwt = require("jsonwebtoken");
const config = process.env;
const verifyToken = (req, res, next) => {
const token = req.headers["authorization"];
// check if there's no token in the request
if (!token) {
return res.status(403).send("A token is required for authentication");
}
// Verify the JWT and decode the user ID
try {
const decoded = jwt.verify(token, config.TOKEN_KEY);
// check if the token has expired
if (decoded.exp < Date.now() / 1000) {
return res.status(401).send({ message: 'Token has expired.' });
}
req.user = decoded;
} catch (err) {
res.status(400).send({ message: 'Invalid token.' });
}
return next();
}
module.exports = verifyToken;
An example of one of the routes i try to access using an authenticated user's token :
const express = require("express");
const router = express.Router();
const categoryController = require('../controllers/categoryController');
const verifyToken = require('../middleware/auth');
router.get("/get_category", verifyToken,categoryController.get_category);
router.post("/create_category", categoryController.create_category);
router.post("/update_category", categoryController.update_category);
router.get("/delete_category/:_id", categoryController.delete_category);
module.exports = router;
Question 1: What's the difference between the first approach and the second one
Question 2: what are the use case for both of them?
jwtMW:
const jwtMW = exjwt({
secret: "keyboard cat 4 ever",
algorithms: ["HS256"],
credentialsRequired: true,
});
approach one
router.post("/authRequest", jwtMW, async (req, res) => {
let toeken = req.headers.authorization;
// use the decoded infomation for further verification
});
approach two
router.post("/authRequest2", async (req, res) => {
const reqToken = req.headers.authorization.split(" ")[1];
const secret = "keyboard cat 4 ever";
var decoded = jwt.verify(reqToken, secret);
// use the decoded infomation for further verification
});
Thanks in advance.
first approach is incorrect, because after path in route you can using middleware, but jwtMW is not middleware, if you want use a middleware try like this:
check-auth.js
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
try {
const token = req.headers.authorization.split(' ')[1]; // Authorization: 'Bearer TOKEN'
if (!token) {
throw new Error('Authentication failed!');
}
const decodedToken = jwt.verify(token, 'supersecret_dont_share');
req.userData = { userId: decodedToken.userId };
next();// it's important line
} catch (err) {
throw new Error('Authentication failed!');
}
};
after that require middleware in route file
const checkAuth = require('../middleware/check-auth');//it's a exmple
router.post('/authRequest', checkAuth , async (req, res) => {
// do somethings
});
in the second approach you don't use a middleware
I try to set Token then get token from header but it always shows "undefined" when i console.log(req.headers['authorization'])
Here is some code
const express = require('express');
const app = express();
const jwt = require('jsonwebtoken');
const accessTokenSecret = 'youraccesstokensecret';
app.listen(8080, () => {
console.log('Authentication service started on port 8080');
});
I set token
app.get("/createJWT",function(req,res){
const accessToken = jwt.sign({ username: "myName"}, accessTokenSecret);
res.json({
accessToken
});
})
the middleware (I show req.headers['authorization'] is undefined)
const authenticateJWT = (req, res, next) => {
const authHeader = req.headers['authorization'];
console.log(authHeader)
if (authHeader) {
const token = authHeader.split(' ')[1];
jwt.verify(token, accessTokenSecret, (err, user) => {
if (err) {
return res.sendStatus(403);
}
req.user = user;
next();
});
} else {
res.sendStatus(401);
}
};
And finally, I access a route to test, req.headers['authorization'] does not have values.
app.get("/check",authenticateJWT,function(req, res){
res.send("ok")
})
please help me, thanks
So when you are calling the API form the frontend or say you are checking the API with postman are you setting up the header while requesting /check? (I am not talking about your /createJWT which creates the token and sends it as a response)
In your frontend code/postman you need to explicitly add the header authorization JWTtoken while creating an HTTP request and after that your backend will receive it. Kindly check if this is not being done.
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!
For example, in Meteor, there's something like
Router.plugin('ensureSignedIn');
Router.plugin('ensureSignedIn', {
except: ['home', 'atSignIn', 'atSignUp', 'atForgotPassword']
});
So unsigned user cannot access other routes except above four.
How to do this in express.js? I'm using passport.js also.
I'm not familiar with Meteor, but you can do something like the following, assuming you want to make pages available to only authenticated users (passport).
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated())
return next();
else
// Return error content: res.jsonp(...) or redirect: res.redirect('/login')
}
app.get('/account', ensureAuthenticated, function(req, res) {
// Do something with user via req.user
});
The ensureAuthenticated function is just an example, you can define your own function. Calling next() continues the request chain.
I should use middleware for protect my routes, even to protect certain verbs in the same route:
for example: in my endpoint/route.js
// the require sentences are omitted
const express = require('express');
const { /*controllerFunctions*/ } = require('./controller');
const {routeGuard} = require('/*must create a route guard*/');
const router = express.Router();
router.route('')
.get(getAllResources)
;
router.route('/:id') //
.get(validateParam,getOneResource);
router.use(routeGuard);
router.route('/:id')
.post(validateParam,validateBody,postResource)
.patch(validateParam,validateBody,patchProblemById)
.delete(validateParam,deleteResource)
;
module.exports = router;
and my routeGuard file should be like this:
const { promisify } = require('util');
const jwt = require("jsonwebtoken");
const AppError = require('./appError');
const {User} = require('./../endpoints/users/model');
const wrapper = require('./try-wrapper');//try catch wrapper
module.exports.routeGuard = wrapper(async function (req, res, next){
// the err message is the same on purpose
const notAllowed = new AppError('Unauthorized: Invalid or Nonexistent credentials',401);
let token = null;
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')){
token = req.headers.authorization.split(' ')[1];
}
if (!token) return next(notAllowed );
const payload = await promisify(jwt.verify)(token,process.env.KEY);
const user = await User.findById(payload.id);
if (!user) return next( notAllowed);
if ( ! user.hasSamePasswordSince(payload.iat) )return next( notAllowed );
req.user = user; // further use...
next();
});