Sending jwt to the client in Express - javascript

First, let me give you an overview of my app, it is fairly simple.
The basic idea is that a user enters his accountname and password into an html form on the /Login page like:
<form action="/Login" method="post" target="_blank">
<label for="fname">First name:</label>
<input type="text" id="name" name="username"><br><br>
<label for="lname">Last name:</label>
<input type="text" id="password" name="password"><br><br>
<input type="submit" value="Submit">
</form>
This works as expected, it makes a post request to /Login transferring username and password.
When the data gets to the server, the server checks if the username and password are valid, if they are a new JSON web token will be created. I use the npm library "bcrypt" for the verification of the account and "jsonwebtoken" for the creation of the token. This looks like this:
const accessToken = jwt.sign(user, process.env.JSON_TOKEN_SECRET, { expiresIn: "10m" })
where the user is just an object, with an id key and a value whatever the name of the account is. If the username was admin it would look like this:
const user = { id: "admin" }
So now I want to deliver this token to the client and I do not know exactly how. I have tried:
res.json({ accessToken })
But all this does is displaying the JSON on the screen. I have also tried a regular res.send() but it won't deliver the token correctly to the user. I want a webtoken authentication so I can use protected routes in my project. If I want to visit a protected route, I get an error, that there is no authentication header, which probably means that the browser didn't receive the token correctly. The route protection is just basic middleware:
const authHeader = req.headers["authorization"] //if console logged authHeader is undefined
const token = authHeader && authHeader.split(" ")[1]
jwt.verify(token, process.env.JSON_TOKEN_SECRET, (err, user) => {
//Some error handling
next()
})
Next, I tried using Postman, where I made a POST request to /Login and got my token in the body of the response back. I pasted the token manually in a Bearer Authorization header and made a GET request to a protected route, which worked.
So how can I send the webtoken correctly to the client?

When you login with POST request it always send token in response so use some function to store that response in localStorage or sessionStorage or after successfully login you could send other request for getting a token from express server.

when user signs in you create json web token. You have a login controller.
const postLogin=(req,res)=>{
// you read the req.body, get username,email, password etc
// then you use email to see if user exists in db
// if user exists, then you verify the password
// if everything works so far you create the token
// token is kinda locked version of your inputs
// when you send it to server, only that server unlocks it with secret-key
let token;
try {
token = jwt.sign({ _id: this._id, email: this.email }, "this-is-secret", {
expiresIn: "1h",
});
} catch (e) {
throw new Error(e.message);
}
res
.status(200)
.header("x-auth-token", token)
.json({ token, userId: existingUser._id.toString() });
}
x-auth=Extended authorization

Related

Validating JWT Token in vue.js Router

I am using the following code to generate a JWT token:
jwt.sign(id, TOKEN_SECRET, { expiresIn: '24h' });
Once generated, I send the token to the client, which stores it within a cookie:
document.cookie = `session=${token}` + ';' + expires + ';path=/'
Furthermore, I am using vue.js Router for my navigation. From my understanding, if one adds the following code in the router file, one can insert middle-ware in order to protect some routes.
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth) {
let token = Vue.cookie.get('session')
if (token == null) {
next({
path: '/',
params: { nextUrl: to.fullPath }
})
}
} else {
next()
}
})
However, I am having difficulty understanding how can one verify the validity of the JWT token using this approach, which needs to be done on the server, where the TOKEN_SECRET is stored, and not on the client side.
Let me start with this: your goal in guarding routes is to prevent the user from having a bad experience by proceeding to a page that will attempt to retrieve information that they are not authorized to view.
So, you don't need to validate the token on the client side. Since a token will only be in hand if the server validated the user and returned a token, you - the author of the client code - can use the presence of the token as a means to inform what route to take the user through.
In other words, the client having a token is all the validation you need to allow the user through to protected routes.
Remember, it is not as though a protected page has private data in and of itself. A protected page will always retrieve that protected data from the server, which means that the server has the chance to authenticate the token after all.

Token Authentication - JWT

When we use jsonwebtoken in Node, we sign a particular token for the user and send it back. However, when we verify the token when the user sends it in the header (Authentication: <token>), how does the jwt know that that token which it is verifying is for that particular user and not for some other user who also sent a request at the same time? Does it store the token somewhere internally?
At the time of login, you sign a token where payload is the userId, which is nothing but the _id field in the queried user object.
loginUser: async (req, res, next) => {
try {
const { email, password } = req.body
const user = await User.findOne({ email })
const token = auth.signToken({ userId: user._id })
res.status(200).json({ user, token })
} catch (error) {
return next(error)
}
}
auth.js
function signToken(payload) {
return jwt.sign(payload, JWTSECRET)
}
function verifyToken(req, res, next) {
const token = req.headers.Authorization || req.headers.authorization || ""
if (!token) {
return res.status(403).json({ error: "Not authorized" })
}
jwt.verify(token,JWTSECRET, (err, decodedObj) => {
if (err) {
return res.status(403).json({ error: "Not authorized" })
}
req.user = decodedObj
next()
})
}
module.exports = { signToken, verifyToken }
In the callback of jwt.verify, you get a decodedObj which is like:
{ userId: '5edb3ae6d6b129183c1393bc', iat: 1591425786 }
where iat is the time at which the jwt was issued.
req.user = decodedObj
Here, we're "attaching" the decoded data to the user object so that when a request is made to the protected route, we can get the userId from the request object, like req.user.userId, and then query it from the database.
When you sign a token, you provide a payload which can be a userId and a secret. So, the token gets signed. After that, you need to verify the token in case you're trying to access some protected page that requires a token.
So, when you send a request to that protected route, like this:
router.get("/me", auth.verifyToken, usersController.identifyUser)
where identifyUser is a controller function that just identifies the logged in user by checking the userId(remember the user object contains the decoded object data).
how does the jwt know that that token which it is verifying is for that particular user and not for some other user who also sent a request at the same time? Does it store the token somewhere internally?
It's because of the payload that you give, which is unique to the user.
The Authentication token is stored in an Authentication Server, so when you send the Authentication token in your request header, the Authentication Server authenticated your client.
After being authenticated by Authentication Server, the client can now pass JWT to make API calls to the Application Server. Since client is allowed to make API calls, Application Server can verify the JWT token the client has sent and can process the API call.
Note that for making API calls, the client has to send a Authorization: Bearer <token> for each API call, which is stored at the server (aka Authorization Server)
the token is most store in the client
when the token verifying successfully, we will get some user info, etc account id, so we can use account id to find more user info in the database, and check the use is really exist
maybe it is useful for you?
You will typically sign the token with the user id when sending it from the server. So when the client then sends back that token you decode it and it will return the id to you. Which you then use to find the user in the data base

How to authorize all users with different tokens using jwt in Node JS

I have a node.js app. I'm using Json Web Token for authorization. When I Login with a user who is in my database, it creates a token for the user. But I can use that token for another user and it works too again. I need to have different tokens for all users and I should not use one user's token for another user. (I dont have internet on my work pc so I cant write my codes on my computer here sorry about that)
Here is my verify-token.js (middleware):
const jwt = require("jsonwebtoken");
module.exports = (req, res, next) => {
try {
const token = req.headers.authorization.split(" ")[1];
const decodedToken = jwt.verify(token, "secret_key");
req.userData = decodedToken;
next();
} catch (error) {
return res.status(401).send({
message: "Auth failed"
});
}
};
And here is my login code (im creating the token here)
if password is true:
const token = jwt.sign(
{
email: user.email,
password: user.password
},
"secret_key",
{
expiresIn: "2h"
}
);
return res.status(200).send({ message: "success", token: token });
And in app.js:
const checkAuth = require('../middleware/checkauth');
router.get('/api/company',checkAuth,companyController.list);
I expect that one token should be for just one user, and for every login it should creates a new token for all users. Any suggestion?
You're including the email in your token body; why not also include the user ID as a claim (field)? When you verify the token, if it's successful, it will return the body to you, so then you'll know which user that token was made for and reject if it's not the one who's making the request.
To ensure two people don't use the same token at the same time, you can keep a list of every valid token when you generate it, and when a token expires or is revoked (for instance, when the user signs out or reports an imposter, if it gets that far) remove it from the list. During verification, if the token is not on the list, don't even bother decoding it, just reject it immediately.
If you give your tokens decently small expiration windows (I believe the recommendation is to make them last no longer than 1 hour), you shouldn't have to worry about such things much.
EDIT To clarify, you'll never have a way to know for sure that the person who gave you the token is who they claim to be. You only know if your server created the token and if the token is currently valid. If you really want to prevent replay attacks (that is, make absolutely sure there's no way for two people to use the same token at once), you'll need to generate a new token every time one is used. If you keep that whitelist that I mentioned above, this regeneration ensures every token becomes invalid as soon as it's used once.
You can also, to be EXTRA confident, include a jti claim in the token body; this is a field intended to be filled with a random unique value every time a token is generated, so that you can keep track of the jti's you've received and not allow the same one to come in more than once. It's about the same as just keeping track of the tokens, though.

Using hidden input in POST Express

I need to send a POST to an express controller, using previous data from a get.
I have an email being sent out for a reset password link. User clicks the link, which looks like example.com/user/reset/[reset token]
User enters new password, and then posts the request updating the db.
But I can't seem to post the reset token with the request
This in my GET controller
res.render('resetPassword');
req.body['hiddenToken'] = req.params.token;
But in my POST controller, I can't seem to access that req.body.hiddenToken key
My handlebars code for the hidden input
<input type="hidden" class="form-control" name="hiddenToken">
GET controller
// User Reset GET Route -> verifies token, adds a hidden elm to page, posts data
router.get('/reset/:token', (req, res) => {
PasswordToken.findOne({token: req.params.token})
.then(token => {
if (!token) {
res.render('resetPassword');
req.body['hiddenToken'] = req.params.token;
}
else {
req.flash('error_msg', 'Token not Found, Try Submitting Again');
res.redirect('/user/login')
}
})
});
And in POST, console.log(req.body); returns
{ hiddenToken: '',
password: '123456789',
passwordConf: '123456789'
}
Maybe there is a way in handlebars to inject the params into the value tag?
Setting properties on req.body doesn't make much sense in a route,
as it will be lost afterwards (the request ends). As you said, you could pass the token to handlebars:
res.render('resetPassword', { token: req.params.token });
Then inside handlebars, add it as the input value:
<input type="hidden" class="form-control" name="hiddenToken" value="{{token}}">
Now the token gets sent to the client inside the HTML, then when the user sends the form the token will get sent back as part of the POST requests data.

How to Refresh Firebase Session Cookie

I'm developing a web application using Node.js/Express.js for the backend and I use Firebase for user authentication, and to manage user registration etc I use Firebase Admin SDK.
When a user want to login I sign him in using Firebase Client SDK like this:
// Handling User SignIn
$('#signin').on('click', function(e){
e.preventDefault();
let form = $('#signin-form'),
email = form.find('#email').val(),
pass = form.find('#password').val(),
errorWrapper = form.find('.error-wrapper');
if(email && pass){
firebase.auth().signInWithEmailAndPassword(email, pass)
.catch(err => {
showError(errorWrapper, err.code)
});
}else {
showError(errorWrapper, 'auth/required');
}
});
Below this code, I set an observer to watch for when the user successfully sign in, After a successfull sign in I get a Firebase ID token which I send to an endpoint on the server to exchange it for a session cookie that has the same claims the ID token since the later expires after 1 hour.
// POST to session login endpoint.
let postIdTokenToSessionLogin = function(url, idToken, csrfToken) {
return $.ajax({
type: 'POST',
url: url,
data: {
idToken: idToken,
csrfToken: csrfToken
},
contentType: 'application/x-www-form-urlencoded'
});
};
// Handling SignedIn Users
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
user.getIdToken().then(function(idToken) {
let csrfToken = getCookie('csrfToken');
return postIdTokenToSessionLogin('/auth/signin', idToken, csrfToken)
.then(() => {
location.href = '/dashboard';
}).catch(err => {
location.href = '/signin';
});
});
});
} else {
// No user is signed in.
}
});
Sign in endpoint on the server looks like this:
// Session signin endpoint.
router.post('/auth/signin', (req, res) => {
// Omitted Code...
firebase.auth().verifyIdToken(idToken).then(decodedClaims => {
return firebase.auth().createSessionCookie(idToken, {
expiresIn
});
}).then(sessionCookie => {
// Omitted Code...
res.cookie('session', sessionCookie, options);
res.end(JSON.stringify({
status: 'success'
}));
}).catch(err => {
res.status(401).send('UNAUTHORIZED REQUEST!');
});
});
I have created a middle ware to verify user session cookie before giving him access to protected content that looks like this:
function isAuthenticated(auth) {
return (req, res, next) => {
let sessionCookie = req.cookies.session || '';
firebase.auth().verifySessionCookie(sessionCookie, true).then(decodedClaims => {
if (auth) {
return res.redirect('/dashboard')
} else {
res.locals.user = decodedClaims;
next();
}
}).catch(err => {
if (auth) next();
else return res.redirect('/signin')
});
}
}
To show user information on the view I set the decoded claims on res.locals.user variable and pass it to the next middle ware where I render the view and passing that variable like this.
router.get('/', (req, res) => {
res.render('dashboard/settings', {
user: res.locals.user
});
});
So far everything is fine, now the problem comes after the user go to his dashboard to change his information (name and email), when he submits the form that has his name and email to an endpoint on the server I update his credentials using Firebase Admin SDK
// Handling User Profile Update
function settingsRouter(req, res) {
// Validate User Information ...
// Update User Info
let displayName = req.body.fullName,
email = req.body.email
let userRecord = {
email,
displayName
}
return updateUser(res.locals.user.sub, userRecord).then(userRecord => {
res.locals.user = userRecord;
return res.render('dashboard/settings', {
user: res.locals.user
});
}).catch(err => {
return res.status(422).render('dashboard/settings', {
user: res.locals.user
});
});
}
Now the view gets updated when the user submits the form because I set the res.locals.user variable to the new userRecord but once he refreshes the page the view shows the old credentials because before any get request for a protected content the middle ware isAuthenticated gets executed and the later gets user information from the session cookie which contains the old user credentials before he updated them.
So far these are the conclusions that I came to and what I tried to do:
If I want the view to render properly I should sign out and sign in again to get a new Firebase ID token to create a new session cookie which is not an option.
I tried to refresh the session cookie by creating a new ID token from the Admin SDK but it doesn't seem to have this option available and I can't do that through the client SDK because the user is already signed in.
Storing the ID token to use later in creating session cookies is not an option as they expire after 1 hour.
I Googled the hell out of this problem before posting here so any help is so much appreciated.
I am facing a very similar scenario with one of my apps. I think the answer lies in these clues.
From Firebase docs
Firebase Auth provides server-side session cookie management for traditional websites that rely on session cookies. This solution has several advantages over client-side short-lived ID tokens, which may require a redirect mechanism each time to update the session cookie on expiration:
So they're hinting here that you want to manage the session and it's lifetime from the server.
Second clue is in the docs
Assuming an application is using httpOnly server side cookies, sign in a user on the login page using the client SDKs. A Firebase ID token is generated, and the ID token is then sent via HTTP POST to a session login endpoint where, using the Admin SDK, a session cookie is generated. On success, the state should be cleared from the client side storage.
If you look at the example code, the even explicitly set persistence to None to clear state from the client using firebase.auth().setPersistence(firebase.auth.Auth.Persistence.NONE);
So they are intending there to be no state on the client beyond the initial auth. They explicitly clear that state and expect an httponly cookie so the client can't grab the cookie (which really is just the ID token) and use it to get a new one.
It is odd that there is no clear way of refreshing the token client-side but there it is. You can only really create a session cookie with a super long lifetime and decide on the server when to delete the cookie or revoke the refresh token etc.
So that leaves the other option: manage state client-side. Some examples and tutorials simply send the ID token from the client to the server in a cookie. The satte sits on the client and the client can use the ID token to use all firebase features. The server can verify the user identity and use the token etc.
This scenario should work better. If the server needs to kick the user then it can delete the cookie revoke the refresh token (a bit harsh admittedly).
Hope that helps. Another scheme would be to build custom tokens, then you have complete control.

Categories

Resources