Where should I set the authorization header after create the token? - javascript

I am triying to implement a securuty system based on tokens. The problem is that I dont know where I must set the authorization header after create it in order to check it in all of my diferent routes. My code is the next.
I want to do it WITHOUT USING POSTMAN or any program like that.
This is the route for user login, where I create the token
router.post('/login',(req,res)=>{
const user = req.body.user;
const token = jwt.sign({user},'secret_key');// generamos un identificador para el usuario que acaba de registrarse
res.json({
token
});
});
Then, I have this route to test it works
router.get('/protected',ensureToken,(req,res)=>{
jwt.verify(req.token,'secret_key',(err,data)=>{
if(err){
res.sendStatus(403);
}else{
res.json({
text:'protected'
});
}
});
});
And finally, this is the middleware
function ensureToken(req,res,next){
const bearerHeader = req.headers['authorization'];
console.log(bearerHeader);
if(typeof bearerHeader != 'undefined'){
const bearer = bearerHeader.split(" ");
const bearerToken= bearer[1];
req.token = bearerToken; //almacenamos el token en el objeto de la peticion
next();
}else{
res.sendStatus(403);//status de no permitido
}
}
Where I should set the authorization header for all of my routes type 'get' as the protected route?

Verifying
router.get('/verify', async (req, res, next) => {
try {
const token = req.headers['x-access-token']//client should this value
if (!token){
return res.status(401).send({
success: false,
message: 'Unauthorized request',
})
}
else if (isExpiredToken(token)){
return res.status(300).send({
success: false,
message: 'Token is expired',
})
}
const decoded = jwt.verify(token, SECRET_KEY)
const expiredAt = moment.unix(decoded.exp).subtract(#YOUR_EXPIRED_TIME, 'minutes')
const now = moment()
let newToken = null
if (now.isAfter(expiredAt)) {//refresh the token
const userFromDB = await User.findOne({
where: {
id: decoded.id,
},
})
const content = util.sanitize(userFromDB)
newToken = await jwt.sign(content, SECRET_KEY, {
audience: content.email,
issuer: 'YOUR_APP',
expiresIn: 'YOUR_EXPIRED_TIME',
})
console.log(
`VERIFY\tToken refreshed automatically for user-${content.id}`
)
}
res.send({
success: true,
nextToken: newToken,
})
} catch (e) {
console.log(e)
res.status(500).send({
success: false,
message: 'Internal server error',
})
}
})
Middleware
async function authMiddleware(req, res, next){
/* In this case, user can authenticate with header['x-access-token'] or body['accessToken']*/
const token = req.header['x-access-token'] || req.body['accessToken'] || undefined
if(!token) return res.status(401).send({ success: false, message: 'unauthorized' })
try{
const user = await jwt.verify(token, secret)
req.user = { ...user }
return next()
}
catch(e){
console.log(e)
return res.status(500).send({ success : false, message : 'internal server error' })
}
}

Related

User is not authenticated jswtoken

I have created a login page and a about page the user will only access the about page if the user is logged in.
I am trying to authenticate the user by using the tokens generated while signing in, but the token is not getting authenticated even after signing in with the correct credentials. I don't know what is the problem?
This is code to my sign-in and token generating method
const express = require("express");
const { default: mongoose } = require("mongoose");
const router = express.Router();
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
require("../db/conn");
const User = require("../model/userSchema");
const cookieParser = require('cookie-parser');
const Authenticate = require("../middleware/authenticate");
router.use(cookieParser());
//LOgin route
router.post("/signin", (req, res)=>{
if(!req.body.email || !req.body.password){
return res.status(400).json({error: "Plz fill the required data"});
}else{
bcrypt.hash(req.body.password, 12, function (err, hash) {
User.findOne({email: req.body.email}, function (err, foundUser) {
if(err){
console.log(err);
}else{
if(foundUser){
bcrypt.compare(req.body.password, foundUser.password, function (err, result) {
if(result){
return res.json({message: "successfully log in"})
}else{
return res.json({message: "incorrect password"});
}
});
const email = req.body.email;
const token = jwt.sign(
{ user_id: foundUser._id, email },
process.env.TOKEN_KEY,
{
expiresIn: "720h",
}
);
foundUser.tokens = foundUser.tokens.concat({token: token});
foundUser.save();
// res.status(200).json(foundUser);
console.log(foundUser);
}else{
return res.status(400).json({message: "user not found"});
};
}
})
})
}
});
//about us page
router.get("/about", Authenticate, function (req, res) {
console.log("about running");
res.send(req.rootUser);
});
module.exports = router;
this is the code to authenticate the user
require("dotenv").config({path: "./config.env"});
const jwt = require("jsonwebtoken");
const User = require("../model/userSchema");
const Authenticate = async(req, res, next) =>{
try {
const token = req.cookies.jwtoken;
const verifyToken = jwt.verify(token, process.env.TOKEN_KEY);
const rootUser = await User.findOne({ _id: verifyToken._id, "tokens.token": token});
if(!rootUser) {
throw new Error("User not found")
}
req.token = token;
req.rootUser = rootUser;
req.userID = rootUser._id;
next();
} catch (err) {
console.log(err);
return res.status(401).send("Unauthorized: No token provided");
}
}
module.exports = Authenticate;
This is react based code of: About-page to display it or not based on user's authenticity.
const navigate = useNavigate();
const callAboutPage = async() =>{
try {
const res = await fetch("/about",{
method: "GET",
headers: {
Accept: "application/json",
"Content-Type" : "application/json"
},
credentials: "include"
});
const data = await res.json();
console.log(data);
if(!res.status === 200){
const error = new Error(res.error);
throw error;
}
} catch (err) {
console.log(err);
navigate("/login");
}
}
As said in the comment looks like there is a issue on the process for setting up the jwtoken, and when you sign in, you just need to find the user and compare the password, there is no need to do the hash with Bcrypt, since you're not registing new user, for example, i will use Async/await instead of callback function, in order for you to read it much more easier:
//Login route
router.post("/signin", async (req, res)=> {
const { reqEmail, reqPassword } = req.body; //destructuring so less thing to write at the next step
if(!reqEmail || !reqPassword) {
return res.status(400).json({message: "Plz fill the required data"});
}
try {
const foundUser = await User.findOne({email: reqEmail})
if(!foundUser) {
return res.status(400).json({message: "Wrong username or password!"})
}
const result = await bcrypt.compare(reqPassword, foundUser.password);
if(!result){
return res.json({message: "Wrong username or password!"})
} else {
const accessToken = jwt.sign(
{ user_id: foundUser._id, email: foundUser.email},
process.env.TOKEN_KEY,
{ expiresIn: "720h",}
);
// I am confuse what are you trying to do here, in your place I would set up on the cookie since you do that on your authentification.
res.cookie("jwt", accessToken, {
maxAge: 60000, // 60 sec for testing
httpOnly: true,
sameSite: false, //false only for dev
secure: false, //false only for dev
})
res.status(200).json(foundUser);
};
} catch (error) {
return res.status(500).json({message: `${error}`})
}
Than the authentification middleware :
// ...
const Authenticate = (req, res, next) => {
const accessToken = req.cookies.jwt
if(!accessToken) {
return res.status(401).json({error: "Unauthorized: No token provided"});
}
try {
const user = jwt.verify(accessToken, process.env.TOKEN_KEY)
if(user) {
req.user = user
return next();
}
} catch (error) {
return res.status(403).json({error: "Forbidden token error"})
}
}
about page component it's simple for now since you don't manage any state
const navigate = useNavigate();
const callAboutPage = async() =>{
try {
const res = await fetch("/about",{
headers: {
"Content-Type": "application/json"
},
credentials: "include"
});
if(res.status === 200){
const data = await res.json();
// set up the state for rendering
console.log(data);
} else {
// you can also create a state to catch the error message from the backend, in this case the response json should be move to above the if statement.
throw new Error("You must log in to get access")
// than you can display this error message, or from the backend using state for this bloc, and the catch bloc
// navigate to /login
}
} catch (err) {
console.log(err);
navigate("/login");
}
}
router.use(cookieParser());
Try to use cookieParser with app.use instead. (app from express instense)
Expample:
const app = express();
app.use(cookieParser());
and try to put it before server listening in index.js or app.js file.
Hope it help.

How to store Cookies in flutter and use it to authenticate if user is logged in or not

I'm trying to create a platform, once I log in it, creates a token and store it in the cookie. I have successfully been able to create a route that stores the cookie using node js(I could see it saved in postman). But once I try to use the route in my flutter app, it seems the the token doesn't save anywhere.
How do I save the cookie and use it to validate if the user should be logged in or should log in again
Login.dart
Future<void> _signIn(email, password) async {
try {
setState(() {
LoadingWidget.show(context);
bool _absorbme = true;
});
var url = "http://192.168.8.23:3000/route/login"; // iOS
final http.Response response = await http.post(
Uri.parse(url),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'email': email.toLowerCase(),
'password': password,
}),
);
SharedPreferences prefs = await SharedPreferences.getInstance();
var parse = jsonDecode(response.body);
await prefs.setString('msg', parse["msg"]);
await prefs.setString('success', parse["success"]);
String? msg = prefs.getString("msg");
} finally {
setState(() {
LoadingWidget.hide(context);
bool _absorbme = false;
});
}
}
My Route.js file
const express = require('express');
const router = express.Router();
const Users = require('./model/model')
const passport = require('passport')
const bcrypt = require("bcrypt");
const { createTokens, validateToken, authenticateToken } = require("./JWT");
const Server = require('http-proxy');
router.get('/', (req, res)=>{
res.send('Hello world')
})
router.post("/signup", async (req, res) => {
const { username, email, password } = req.body;
let user = await Users.findOne({ email });
if (user){
return res.status(400).json({success: 'false', msg: "Email Already Exist"});
}
bcrypt.hash(password, 10).then((hash) => {
Users.create({
username: username,
password: hash,
email:email,
})
.then(() => {
res.json({success: 'true', msg: 'login Successfully'});
})
.catch((err) => {
if (err) {
res.status(400).json({success: 'false', msg: 'Failed to save'});
}
});
});
});
router.post("/login", async (req, res) => {
const { email, password } = req.body;
let user = await Users.findOne({ email });
if (!user) {
return res.status(400).json({success: 'false', msg: 'Authentication Failed, User not found'});
}
const dbPassword = user.password;
bcrypt.compare(password, dbPassword).then((match) => {
if (!match) {
res
.status(400)
.json({success: 'false', msg: 'Authentication failed, wrong password'});
} else {
const accessToken = createTokens(user);
var token = res.cookie("access-token", accessToken, {
maxAge: 60 * 60 * 24 * 30 * 1000,
httpOnly: true,
});
res
.status(200)
.json({success: 'true', msg: 'Successfully logged In'});
}
});
});
router.get("/profile", validateToken, async (req, res) => {
let user = await Users.findOne({email: req.decoded.email});
return res.json({
email: user.email,
balance: user.balance,
name: user.username
})
router.get("/logout", validateToken, (req, res) => {
return res
.clearCookie("access-token")
.status(200)
.json({success: "true" , msg: "Successfully logged out" });
});
router.get("/authenticate" ,authenticateToken, (req,res)=>{
})
module.exports = router;
enter image description here
Why not use JWT it can help you manage your users for the numbers of hours you specified
Create authorize endpoint on your api to refresh token when expired.
In dart side, create an authenticated client singleton.
class AuthenticatedClient extends http.BaseClient {
factory AuthenticatedClient() => _instance;
AuthenticatedClient._();
static final _instance = AuthenticatedClient._();
int? expiresAt;
String? token;
final _client = http.Client();
void setParams(http.Response res) {
final response = jsonDecode(res.body);
expiresAt = response['expiresAt'];
token = response['token'];
}
Future<void> authorize() async {
// Send a request to refresh token, update token and expiresAt
// accordingly. Note that, you can't use _client to send request, use
// htt.put, http.post.
// Ex:
//
// final response = await http.put(
// Uri.parse('https://myapi.com/authorize'),
// headers: {
// 'Content-Type': 'application/json',
// },
// body: jsonEncode({
// 'email': <email>,
// 'password': <password>,
// }),
// );
//
// setParams(response);
}
#override
Future<http.StreamedResponse> send(http.BaseRequest request) async {
// Ensure you set params for the first time.
assert(expiresAt != null);
if (expiresAt! < DateTime.now().millisecondsSinceEpoch) {
await authorize();
}
request.headers['Authorization'] = 'Bearer $token';
return _client.send(request);
}
}
On your api, generate a token on login or signup request and send token to client. For this example, response format is that:
{
'token': <generated token>,
'expiresAt': <timestamp>,
}
When you got response, call setParams method of AuthenticatedClient and set request params. I didn't add code for error handling to the example, don't forget to add.
Use AuthenticatedClient like this:
AuthenticatedClient().get(Uri.parse('https://myapi/profile'));

How can I access ["x-access-token"] in Vue.js front end?

I have an application that uses ["x-access-token"] as token in the header, when I try to access the token in the header I keep gettting "no token provided". I use jwt for authentication
This is how I'm accessing it in the script tag:
<script>
import axios from "axios";
export default {
data() {
return {
name: "",
email: "",
};
},
created() {
//user is not authorized
if (localStorage.getItem("token") === null) {
this.$router.push("/login");
}
},
mounted() {
axios
.get("http://localhost:5000/api/auth/user", {
headers: {
Authorization:'Bearer' + token,
token: localStorage.getItem("token") }
})
.then(res => {
console.log(res)
this.name = res.data.foundUser.name;
this.email = res.data.foundUser.email;
});
},
};
</script>
this is my authorization middleware for verifying tokens in the browser, my header uses "x-access-token"
const jwt = require("jsonwebtoken");
module.exports = function (req, res, next) {
let token = req.headers["x-access-token"] || req.headers["authorization"];
let checkBearer = "Bearer ";
if (token) {
if (token.startsWith(checkBearer)) {
token = token.slice(checkBearer.length, token.length);
}
jwt.verify(token, process.env.SECRET, (err, decoded) => {
if (err) {
res.json({
success: false,
message: "Failed to authenticate"
});
} else {
req.decoded = decoded;
next();
}
});
} else {
res.json({
success: false,
message: "No token Provided"
});
}
};
and my authenticated route to get the current logged in user
// Get Profile
router.get("/auth/user", verifyToken, async (req, res) => {
try {
let foundUser = await User.findOne({
_id: req.decoded._id
}).populate(
"address"
);
if (foundUser) {
res.json({
success: true,
user: foundUser
});
}
} catch (err) {
res.status(500).json({
success: false,
message: err.message
});
}
});
When I check my local storage I see the token but can't seem to understand why I keep getting no token provided when trying to get the user in my front end
try
const token = localStorage.getItem("token")
axios
.get("http://localhost:5000/api/auth/user", {
headers: {
Authorization:'Bearer ' + token,
'x-access-token': token
}
})

how to set a new cookie without response object(express.js)

Before talking about issue, you need some background knowledge about my application.
I use node.js, express.js and mongoDB (with mongoose).
I was making a basic login function to do that I choose JWT for token and passport.js for authentication. and login flow is like below
Issue access token(expires in 10mins) and refresh token (expires in 2 weeks)
Store both token in cookies and store refresh token in DB too
Make a strategy for authentication.
Use strategy when you go to '/current' page.
Below is my code:
user.js:
router.post('/login', (req, res) => {
const { userid, password } = req.body;
// find email
User.findOne({ userid })
.then(user => {
if (!user) {
return res.json({
loginSuccess: false,
message: "Auth failed, ID not found"
});
}
// compare password
bcrypt.compare(password, user.password)
.then(isMatch => {
if (isMatch) {
const accessPayload = {
id: user.id,
userid: user.userid,
role: user.role
};
// console.log(accessPayload.id);
const refreshPayload = {
id: user.id
}
jwt.sign(accessPayload, JWT_ACCESS_SECRET, { expiresIn: JWT_ACCESS_EXPIRATION_TIME}, (err, token) => {
const accessToken = 'Bearer ' + token
const accessT = token
jwt.sign(refreshPayload, JWT_REFRESH_SECRET, { expiresIn: JWT_REFRESH_EXPIRATION_TIME }, (err, refreshT) => {
//note that maxAge is in milliseconds
res.cookie('accessToken', accessT, { httpOnly: true }) //secure: true
res.cookie('refreshToken', refreshT, { httpOnly: true })
res.json({
success: true,
refreshToken: 'Bearer ' + refreshT,
accessToken: accessToken
})
User.saveRefreshToken(refreshT)
//이거 모듈화 왜앙돼
});
});
} else {
return res.json({ loginSuccess: false, message: "Wrong password" });
}
});
});
});
router.get('/current',passport.authenticate('custom', { session: false }), (req, res) => {
res.json({
id: req.user.id,
userid: req.user.userid,
role: req.user.role
// id: req.user.id,
});
})'
and my strategy(custom) is like below
require('dotenv').config();
const { auth } = require('../middleware/auth');
const { append } = require('express/lib/response');
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const passportCustom = require('passport-custom');
const CustomStrategy = passportCustom.Strategy;
// const res = require('express/lib/response');
const jwt = require('jsonwebtoken');
const mongoose = require('mongoose');
const User = mongoose.model('User');
const { verifyToken } = require('../utils/jwt');
module.exports = custom => {
custom.use('custom', new CustomStrategy(
function (req, done) {
const accessToken = req.cookies['accessToken'];
const refreshToken = req.cookies['refreshToken'];
//when both token are valid
if (
req && verifyToken('access', accessToken)
&& verifyToken('refresh', refreshToken)
) {
const verifyAccessToken = verifyToken('access', accessToken);
const user = {
id: verifyAccessToken.id,
userid: verifyAccessToken.userid,
role: verifyAccessToken.role
};
console.log(user)
return done(null, user);
}
// when refresh token is valid but access token expired
else if (
req && !verifyToken('access', accessToken)
&& verifyToken('refresh', refreshToken)
) {
console.log('you need new access token')
const refreshTokenInfo = verifyToken('refresh', refreshToken);
User.findById({ _id: refreshTokenInfo.id }, (err, user) => {
const Payload = {
id: user.id,
userid: user.userid,
role: user.role
}
console.log('old access token : ' +req.cookies['accessToken'])
jwt.sign(Payload, JWT_ACCESS_SECRET, { expiresIn: JWT_ACCESS_EXPIRATION_TIME }, (err, accessToken) => {
if (err) {
console.log(err)
}
console.log('new accesstoken : ' + accessToken)
});
return done(null, true)
})
return done(null, true)
}
}
))
}
The problem is at the case of ' // when refresh token is valid but access token expired'
When accessToken is expired but refresh one is valid I want to make new access token via information from DB(access DB when refresh token is correct) and set in cookie before done() However, I couldn't set cookie because there is no response object in parameter :/.
As far as I know, people use cookie to store access token and sometimes refresh it. I wonder how can I refresh my cookie in jwtStrategy and pass it to /current router so it recognize my refreshed access cookie
Thanks for reading, your help will be appreciated.

Cannot delete a player object after checking for a `created_by` attribute

I have a DELETE operation as part of a NodeJS API I have built. This delete should do the following:
Delete a player object with the ID provided
It should not delete a player created by another user.
In the tests it passes if the player is created by another user but fails when it attempts to delete the object. Here is the code:
router.delete('/:id', validateBearerToken, function(req, res) {
let playerId = req.params.id;
//get player object
let player = Player.find({created_by: playerId
}, function(err) {
if (err) return res.status(409).send('There was a problem finding the players.');
});
if (player.created_by !== getUserFromBearerToken(req.token)) {
return res.status(404).send('The player not created by this user');
}
Player.findByIdAndRemove(playerId, function(err) {
if (err) {
return res.status(404).send('There was a problem deleting the player.');
}
res.status(200).send({
success: true
});
});
});
validateBearerToken is used to check if the user executing the delete operation is valid
function validateBearerToken(req, res, next) {
let bearerToken;
let bearerHeader = req.headers.authorization;
if (typeof bearerHeader !== 'undefined') {
let bearer = bearerHeader.split('Bearer ');
bearerToken = bearer[1];
req.token = bearerToken;
next();
} else {
res.status(403).send();
}
}
gertUserFromBearerToken is used to get the id of the logged in user to compare with the 'created_by` id in the test:
function getUserFromBearerToken(token) {
const decodedtoken = jwt.decode(token, process.env.JWT_SECRET);
return decodedtoken.id;
}
you verification middle-ware (validateBearerToken) should be like this
function validateBearerToken(req, res, next) {
var token = req.headers.authorization || req.headers['x-access-token'];
if (!token)
return res.status(403).send({ auth: false, message: 'No token provided.' });
jwt.verify(token, process.env.JWT_SECRET, function(err, decoded) {
if (err)
return res.status(500).send({ auth: false, message: 'Failed to authenticate token.' });
// if everything good, save to request for use in other routes
req.userId = decoded.id;
next();
});
}
then in delete route check for id like this
if (player.created_by !== req.userId) {
return res.status(404).send('The player not created by this user');
}

Categories

Resources