I am building a web app using Symfony 4 and I am trying to implement the back end as a REST API.
As part of the login process, I have created an endpoint that returns a JWT upon receiving a valid username and password. I then save the JWT to local storage in the browser as so:
$(document).ready(function() {
$('.js-login-form').submit(function (e) {
e.preventDefault();
let username = $('#_username').val();
let password = $('#_password').val();
let data = JSON.stringify({_username: username, _password: password});
$.ajax({
method: 'POST',
url: '/api/tokens',
contentType: "application/json",
dataType: 'json',
data: data,
success: function(data) {
localStorage.setItem('token', data.token);
// Add Header to the request Authorization: "Bearer " . data.token
window.location='/app';
},
error: function(jqXHR) {
var errorData = JSON.parse(jqXHR.responseText);
console.log(errorData);
}
});
});
});
My question is how I can add the JWT to the header so I can redirect the user to the password protected area. My guard authenticator will then validate the token accordingly.
Many thanks
Do you mean add JWT to the header in backend?
If yes, you can create middleware to check user is auth or not.
This is example :
// add jwt module
const jwt = require('jsonwebtoken');
async function isAuthValid(req,res,next) {
try {
// check header
let token = req.headers['authorization'];
if (token.startsWith('Bearer ')) {
// check token type
token = token.slice(7, token.length).trimLeft();
} else {
return res.send('wrong token type');
}
if (token) {
// ? check token
let verifiedUser = await jwt.verify(token, 'somesecret', (err, decoded) => {
if (err) {
return res.send(err.message);
} else {
return decoded
}
});
req.authUser = verifiedUser
next();
}
} catch (err) {
return res.send('no token provided');
}
}
module.exports = isAuthValid
Registering as middleware on your route
const express = require('express');
const isAuthValid = require('./checkAuthUser.js');
const app = express();
app.get('/myRoute', isAuthValid, (req,res) => {
return res.send('protected endpoint')
});
Related
I am trying to intercept a request from apollo client to apollo server and return either a different typeDef field or an undefined one without the client resulting in an error.
This is to check the users refresh token if there access token is expired, and send a new access token instead of the nomral query they were expecting.
Here is my code:
Server:
const server = new ApolloServer({
typeDefs,
resolvers,
cors: {
origin: "http://localhost:3000",
credentials: true,
},
context: async ({ req, res }) => {
let isAuthenticated = false;
// get the user token from the headers
const authorization = req.get("Authorization");
if (authorization) {
let token = req.headers.authorization.replace(/\(/g, "");
let verifysomething = jwt.verify(
token,
process.env.JWT_SECRET,
(err, decoded) => {
console.log("access decoded");
console.log(decoded);
if (err) {
return err;
}
return decoded;
}
);
if (verifysomething.message == "jwt expired") {
let refreshToken = req.headers.cookie.replace(/^[^=]+=/, "");
let verifyRefresh = jwt.verify(
refreshToken,
process.env.JWT_SECRET,
(err, decoded) => {
if (err) return err;
const accessToken = jwt.sign(
{ userId: decoded.userId },
process.env.JWT_SECRET,
{
expiresIn: "20s",
}
);
isAuthenticated = "newAccess";
return accessToken;
}
);
//end return if now access token but has refresh token
return { isAuthenticated, res, verifyRefresh };
}
let user = await User.findOne({ _id: verifysomething.userId });
if (user) {
isAuthenticated = true;
return {
isAuthenticated,
user,
};
}
}
return { isAuthenticated, res };
},
});
Resolvers:
const resolvers = {
Query: {
getCartItems: authenticate(
async (_, { id }) =>
await cartItem.find({ user: id }).populate("user").populate("item")
),
}}
Authenticate:
function authenticate(resolver) {
return function (root, args, context, info) {
if (context.isAuthenticated == true) {
return resolver(root, args, context, info);
}
else if ((isAuthenticated = "newAccess")) {
return context.verifyRefresh;
}
throw new Error(`Access Denied!`);
};
}
To clarify, the issue is that a request going to getCartItems will be expecting a retun like:
type CartItemPayload {
item: Item
user: User
}
but if their access token is expired I would like to just send them a new one first instead.
Now one way I thought to solve this was by adding a new mutation query for refresh token and on each request from the client I can decode the access token jwt to see if it is expired and if so then I make a request to the refreshToken mutation instead of the getCartItems query for example, and that way I would be able to get the expected typeDef return and easily control the data I am receiving.
The problem I see with that is this just feels like a problem that is supposed to be solved on the serverside. I am new to apollo and graphql and am not using an express server for middleware nor am I using redux for middleware on the client side and not sure if what I am trying to achieve is possible this way.
I am open to any solutions or suggestions for this problem, thanks.
I am developing a web app with an Express sever, and I can't find the reason for the page not to load after I call res.render().
My folder structure is as follows (omitting the files and folders that are unnecessary for this question):
- app
- views
- partials
adminDashboard.ejs
welcome.ejs
- lib
- controllers
auth.controller.js
- models
user.model.js
- routes
routes.js
- public
- js
login.js
server.js
The code in question is as follows:
login.js
loginForm.addEventListener('submit', (event) => {
event.preventDefault()
const username = document.getElementById("inputUsername").value
const password = document.getElementById("inputPassword").value
const user = {
username: username,
password: password
}
return fetch(`/login`, {
mode: 'cors',
headers: {
'Content-Type': 'application/json'
},
method: 'POST',
body: JSON.stringify(user)
})
.then(response => window.location.reload)
})
auth.controller.js
async function login(request, response) {
try {
//We must validate what is sent by the frond end
if (!(request.body.username, request.body.password)) {
response.status(400).send("Inputs missing")
}
//Next we verify if the user actually exists in the database
const existingUser = await User.getByUsername(request.body.username);
//We will include the password comparison logic in this if block
if (existingUser.status == 201 && (await bcrypt.compare(request.body.password, existingUser.user.Password))) {
//Create a more lean user object
const loggedUser = existingUser.user
delete loggedUser.Password
const token = jwt.sign({
username: request.body.username
},
process.env.TOKEN_KEY
)
loggedUser.token = token
return response.status(200).render('adminDashboard.ejs')
} else {
response.status(400).send("User not found")
}
} catch (err) {
//No better way of handling this error as of now
console.log(err)
}
}
async function isLoggedIn(req, res, next) {
const authHeader = await req.headers['authorization']
const token = authHeader && authHeader.split(' ')[1]
if (token == null) return res.status(200).render('welcome.ejs')
jwt.verify(token, process.env.TOKEN_KEY, (err, user) => {
if (err) return res.status(403).redirect('/login')
//req.user = user
next()
})
}
module.exports = {
login: login,
isLoggedIn: isLoggedIn
}
routes.js
var router = require("express").Router()
const auth = require("../controllers/auth.controller.js")
router.post("/login", auth.login)
Now, the problem is whenever I click login in the login page and it runs the fetch with the user credentials, nothing happens. I've used console.log to see up to where everything runs and found that everything runs as it should but when response.render is called in auth.controller.js nothing happens and the browser stays in the login page.
Thank you very much in advance!
Js and web development I've tried searching the web crazy but I couldn't find a solution that could help me...
I have completed creating a project for a small lab...now I'm trying to create its login page and creating a web token using JWT...
I manage to successfully create a user and hash user password using bcrypt.... and successfully create an access token and refresh token....
I have also created a middleware that will authenticate the token
now I don't know how to send the token to that middleware
This is the authenticate token function
function authenticateToken(req, res, next)
{
try {
// header contains the token
// format
// Bearer Token
// inside autherization header
var authHeader = req.headers['authorization'];
var token = authHeader && authHeader.split(' ')[1]
if (token == null) {
// Meaning the user has not send a token.
// return res.sendStatus(401);
res.redirect('/login/');
}
// req.token = token;
// let headers = new Headers({ 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token });
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user)=>{
if (err) {
console.log("invalid token");
res.redirect('/login/');
return res.sendStatus(403);
// invalid token
}
req.user = user;
next();
});
} catch (error) {
return res.send(error.message);
}
}
I will not post all the data as its not relevant as it will populate the text area and will increase the difficulty level in understanding.... I will only share the part where I'm stuck.
this is the part where I check if the user exists and password is correct after successful authentication then I want to redirect to the dashboard page...or the main page
I cant send the access token to the route with the middleware of user authentication
router.post('/authenticate', (req,res,next)=>{
// console.log("Authenticate");
// console.log(req.body);
// console.log("Authenticate");
var email = req.body.email;
var password = req.body.password;
var sqlQuery = "select * from user where email = '"+email+"' and display = 'yes' ;;";
try {
con.query(sqlQuery,(error,result)=>{
if (!error) {
// console.log(result);
var oriPassword = result[0].PASSWORD;
var user =
{
id : result[0].ID,
name : result[0].LASTNAME,
mobileNo : result[0].MOBILENO,
};
bcrypt.compare(password, oriPassword,(err,res)=>{
if (!err) {
var accessToken = generateAccessToken(user);
var refreshToken = jwt.sign(user, process.env.REFRESH_TOKEN_SCRET);
sqlQuery = "update user set AccessToken = '"+accessToken+"' ,refreshtoken =
'"+refreshToken+"' where id = "+user.id+";";
con.query(sqlQuery,(error,result)=>{
if (!error) {
console.log("RefreshToken Inserted.");
console.log({accessToken:accessToken, refreshToken:refreshToken});
req.headers.authorization = accessToken;
} else {
console.log(error.message);
}
});
}
else {
}
});
console.log("redirecting to login user");
// console.log("Response Header");
// console.log(res.header.authorization );
res.redirect('/login/loginUser');
// res.send({accessToken:accessToken, refreshToken:refreshToken});
} else {
console.log(error.message);
}
});
} catch (error) {
console.log(error.message);
}
});
the route I want to go
router.get('/loginUser',authenticateToken,(req,res,next)=>{
// console.log(req.user);
// res.render("pages/dashboard/index");
// res.redirect("/");
res.send("Success");
console.log("Login SuccessFull..");
});
please help me I'm stuck in this form 3 days...
From what i get, you want to send some data(in this case, access token) to a certain route. You can always use query strings. Check out how it is used here.
However, I am not sure if passing around tokens in non-public api is secure.
You can parse request header to the route with the token like this.
And you can access that token in the middleware function by using this function.
function getTokenFromHeader() {
if (
(req.headers.authorization &&
req.headers.authorization.split(' ')[0] === 'Token') ||
(req.headers.authorization &&
req.headers.authorization.split(' ')[0] === 'Bearer')
) {
return req.headers.authorization.split(' ')[1];
}
return null;
}
I am trying to build an authentication system so, i used node , mysql,express for that so now i am simply saving and checking user exist in database can access but now i added JWT to it, so now i want this JWT token to store in localstorage or in cookies so, can someone guide me how can i do so
this is my authentication controller.js
var Cryptr = require('cryptr');
cryptr = new Cryptr('myTotalySecretKey');
var express = require('express');
const ap = express();
var jwt = require('jsonwebtoken');
var connection = require('./../config');
module.exports.authenticate = function (req, res) {
var email = req.body.email;
var password = req.body.password;
connection.query('SELECT * FROM users WHERE email = ?', [email], function (error, results, fields) {
if (error) {
res.json({
status: false,
message: 'there are some error with query'
});
} else {
if (results.length > 0) {
decryptedString = cryptr.decrypt(results[0].password);
if (password == decryptedString) {
jwt.sign({ email, password },
'secretkey',
{ expiresIn: '10days' },
(err, token) => {
console.log('token:' + token);
module.exports = token;
console.log(token);
res.redirect('/home.html');
}
);
} else {
res.redirect('/login.html');
console.log("Wrong Input");
}
}
else {
res.redirect('/login.html');
}
}
});
};
now i want to pass the token value to the local-storage or cookies so that i can restrict someone from acessing a page, i am reallly new to node js so any help would be appriciated
First I should notify you that do not put any secret things like password in jwt payload because the values of the payload could be accessed easily, you can try to copy paste a jwt in jwt.io site and see the payload.
set jwt in cookie like below, this will use express cookie method that does set Http Set-Cookie header:
res.cookie('jwt', generated_cookie)
.redirect('/home.html');
Also if you want to use localStorage you can set jwt in header and then in your code get the jwt from the header of login request and save it in localStorage and after that you should pass it as header in all other request, but this approach is a better solution for api calls like when you use react or vue ...
res.set({x-token: generated_token});
// In your code get
// get token from response
localStorage.setItem('token', token);
// now whenever calling api pass token as header
I show you one solution using jwt token, you choose another way:
Back-end file e.g. api.js
let jwt = require('jsonwebtoken')
let secret = 'yourSecret'; //secret key necessary to encode token
let Cryptr = require('cryptr');
let cryptr = new Cryptr('myTotalySecretKey');
module.exports = function(router,upload) {
function tokenAuth(req, res, next){
let token = req.body.token || req.body.query || req.headers['x-access-token']
if(token){
jwt.verify(token, secret, function(err,decoded){
if(err){
res.json({ authenticated: false, message:'Invalid token'})
} else {
req.decoded = decoded;
next()
}
})
} else {
res.json({success:false, message:'No token provided'});
}
}
router.post('/authenticate', function(req, res){
connection.query('SELECT * FROM users WHERE email = ?', [email], function (error, results, fields){
if(error) {
res.json({ success:false, message: err })
}
if(!results.length){
res.json({success:false, message:'User no found'})
} else if (results.length>0){
if(!req.body.password){
res.json({success:false, message:'Password was not provided'});
} else {
var validPassword = cryptr.decrypt(results[0].password);
if(validPassword === req.body.password){
res.json({success:false, message:'Incorrect password'})
} else {
var token = jwt.sign({username: results[0].username, email: results[0].email}, secret, {expiresIn: '24h'})
res.json({success:true, message:'You have logged in correctly!', token: token })
}
}
}
})
})
//If you want create a route for authenticated users for example comment posts, you can use our `tokenAuth function`
router.post('/post/comment',tokenAuth,function(req,res){
//access only for authenticated users
}
return router
}
This tokenAuth function we'll be use in paths restricted to authenticated users
server file e.g. server.js
const express = require('express');
const app = express();
const port = process.env.PORT || 80;
const http = require('http').Server(app);
const routes = require(path_to_api.js)(router);
app.use('/myApi', routes)
//***Here you should implement more details about your project such as routes, body parsers and other middlewares*****//
//Connect to your database
http.listen(port, ()=> console.log(`Server running on ${port}`))
Front-end file e.g. controller.js
function(login){
return fetch('/myApi/authenticate',{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(login)
}).then(result=>result.json()).then(data=> window.localStorage.setItem('token', data.token))
}
//`login` argument should be an object and should be like {username: 'user username', password: 'user password'}
In order to make a user store cookies, you can use the Set-Cookie header. From MDN:
Set-Cookie: <cookie-name>=<cookie-value>
In order to pass a header using Express, you can use res.set(), e.g. res.set("Set-Cookie", "Token=" + token). I also suggest you use the HttpOnly cookie directive, since it seems from your post that you don't access this token directly via Javascript and you simply want to check it when the client requests a webpage: res.set("Set-Cookie", "Token=" + token + "; HttpOnly").
The client will send the Cookie header to you when it requests a resource. You can check this header using req.header('Cookie'), and the output will be "Token=<token>" if the user is authenticated. You can then check this token for authenticity.
So I have been researching for hours and trying different things and have been researching for hours to no avail. The call is to get a JWT token after providing user and pass.
function listenForLogin() {
console.log('listening')
$('#submit-btn').on('click', e => {
e.preventDefault();
console.log('button-pressed');
const username = $('#user-input').val().trim();
const password = $('#pass-input').val().trim();
var user = {}
user.username = username;
user.password = password
console.log(user);
$('#user-input').val('');
$('#pass-input').val('');
authenticateUser(user);
});
}
//send to autenticate
function authenticateUser(user) {
console.log('trying to authenticate');
const settings = {
url:"/api/auth/login",
data: JSON.stringify(user),
dataType: "json",
method:"POST",
success: (data) => {
console.log('authenticated user');
redirectWithToken(data.authToken, user);
},
error: (err) => console.log(err)
}
$.ajax(settings);
}
When it hits the server morgan sees that there was a request but i get back a status of 400. here is my routes
'use strict';
const express = require('express');
const passport = require('passport');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');
const {JWT_SECRET, JWT_EXPIRY} = require('dotenv').config();
const router = express.Router();
const createAuthToken = function(user) {
return jwt.sign({user}, 'shade', {
subject: user.username,
expiresIn: '7d',
algorithm: 'HS256'
});
};
const localAuth = passport.authenticate('local', {session: false});
router.use(bodyParser.json());
router.post('/login', localAuth, (req, res) => {
const authToken = createAuthToken(req.user.serialize());
res.json({authToken});
});
const jwtAuth = passport.authenticate('jwt', {session: false});
router.post('/refresh', jwtAuth, (req, res) => {
console.log('refresh targeted');
const authToken = createAuthToken(req.user);
res.json({authToken});
});
router.get('/dashboard/:user', jwtAuth, (req, res) => {
res.redirect(`https:flow-state.herokuapp.com/dashboard/${req.params.user}`);
})
module.exports = router;
and I am having a hard time understanding how passport.authenticate('localAuth') works so here is my strategies file just in case you need that
Update: I am getting some kind of error when checking the requests on fiddler.
RESPONSE BYTES (by Content-Type)
~headers~: 132
~???????~: 11
anybody got any clue what that means?
Did you miss the content-type in the ajax settings?
Add contentType: "application/json" in the ajax settings and try again.
Note :
dataType defines the data type expected of the server response.
contentType defines the data type of the content which will be sent to the server. Default is: "application/x-www-form-urlencoded"
8 hours later and a big headache a solution is here. #vignz.pie you were right but I needed to send the 'Content-Type': 'application/json' in the headers along with strigify the data setting the processData: false did the trick. Thanks for the help!
function listenForLogin() {
console.log('listening')
$('#submit-btn').on('click', e => {
e.preventDefault();
console.log('button-pressed');
const username = $('#user-input').val().trim();
const password = $('#pass-input').val().trim();
$('#user-input').val('');
$('#pass-input').val('');
authenticateUser(username, password);
});
}
//send to autenticate
function authenticateUser(user, pass) {
var info = {
username: user,
password: pass
};
console.log(info)
const settings = {
url:"/api/auth/login",
headers: {
"Content-Type": "application/json"
},
data: JSON.stringify(info),
processData: false,
dataType: "json",
method:"POST",
success: (data) => {
console.log('authenticated user');
redirectWithToken(data.authToken, user);
},
error: (err) => console.log(err)
}
$.ajax(settings);
}