Recognizing first user login (Node.js/JWT) - node.js

I've developed an authentication system who consists on Node.js/Express and JWT.
I need to recognize the first time a user logs on and show him a message relevant only on that one time.
How can I do it?
This is the auth middleware i'm using to verify the user:
const auth = (req, res, next) => {
const token = req.header("x-auth-token")
if (!token) return res.status(401).json({ msg : "No token, authorization failed" })
try {
const decoded = jwt.verify(token, config.get("jwtSecret"))
req.user = decoded
next()
}
catch {
res.status(400).json({ msg : "Token is not valid" })
}
}

For this approach, you should let User to login with Basic Authentication for the first time (Email/Username + Password). After you validate that the user is the valid user on the backend, then you should store his _id in JWT and send him that JWT. On each next request, that user should send that JWT, and you can use your JWT authentication (but for initial login, you should go with Basic Authentication).

Related

What's the best way to add social login (Sign in with Google) to existing email/password app and database?

I want to integrate sign in by google to an app that already has account signup and login.
I followed some youtube tutorial and I now have a working sign in by google working on my frontend. It returns a JWT upon successful log in. Decoding it gives me an object that contains an email, name, family name, pfp, and some other properties that I don't know what they are for.
What do I do with that ?
In my express server I have a register route
router.post("/register", async (req, res, next) => {
try {
// expects {email, password} in req.body
const { password, email } = req.body;
const user = await User.create(req.body);
const token = jwt.sign(
{ id: user.dataValues.id },
process.env.SESSION_SECRET,
{ expiresIn: 86400 }
);
res.json({
...user.dataValues,
token,
});
} catch (error) {
if (error.name === "SequelizeUniqueConstraintError") {
return res.status(401).json({ error: "Email already exists" });
} else if (error.name === "SequelizeValidationError") {
return res.status(401).json({ error: "Validation error" });
} else next(error);
}
});
Login route is similar.
The database I used was postgres with sequelize ORM, User.create(...) basically just creates a user and stores the hashed password to verify later.
As you could see, if they use google auth it wont have a password while regular signup would. Do I just allow User.create to also create a user if password not given? Would that be secure? What is the correct way to go about this?
A user who logs on to your app must
either type their email address and password into your login form, then your app checks the password hash and creates the token = jwt.sign(...). I would recommend that your JWT also contains the email address, preferably in the same format that Google's JWT uses.
or start a Google logon flow, then Google sends your app a JWT. During this flow, no password hash is looked up from your user database, but if the Google email address is not already in your database, it is a new user for whom you must insert a record into your database (only email address, no password).
The JWT should have an iss claim that tells you whether it was issued by your app or by Google. In Google's case, the JWT is signed by Google, and in your /register and /login routes you must verify the signature with jwt.verify using Google's public key (presumably this). (Actually, registration and login don't differ much if you use a third party authentication service like Google's.)
I assume that in both cases you store the JWT in a session cookie
res.cookie("jwt", token, {httpOnly: true});
and every subsequent request must repeat the signature verification of the JWT
try {
var jwt = jwt.verify(req.cookies.jwt, publicKey);
if (jwt.exp <= new Date().getTime() / 1000)
throw "expired";
// Token verification succeeded
} catch(e) {
// Token verification failed
}
(either with your own app's public key or with Google's, depending on the iss). Only after successful verification does the request count as authenticated, and you can then retrieve the user record from your user store based on the email address.
The password (hash) in your use database is thus optional, but even users with a password could use Google for logon.

Refresh token how to handle POST routing

I am new to JWT and tokens for user verification and login. I used the following extensions for Node JS (NPM)
var jwt = require('jsonwebtoken');
const cookieParser = require('cookie-parser')
require('dotenv').config();
// Express ..
I already have a login that checks in MongoDB (Node JS as server) the user, checks email and password and then sets a cookie with access token and refresh token.
My login code is like
//create the access token with the shorter lifespan
let accessToken = jwt.sign(payload, process.env.ACCESS_TOKEN_SECRET, {
algorithm: "HS256",
expiresIn: process.env.ACCESS_TOKEN_LIFE
})
//create the refresh token with the longer lifespan
let refreshToken = jwt.sign(payload, process.env.REFRESH_TOKEN_SECRET, {
algorithm: "HS256",
expiresIn: process.env.REFRESH_TOKEN_LIFE
})
//send the access token to the client inside a cookie
res.cookie("_login", accessToken, {secure: true, httpOnly: true})
res.send()
and here is the part for refresh token post
exports.refresh = function (req, res, next){
console.log("Test");
let accessToken = req.cookies._login
if (!accessToken){
return res.status(403).send()
}
let payload
try{
payload = jwt.verify(accessToken, process.env.ACCESS_TOKEN_SECRET)
}
catch(e){
return res.status(401).send()
}
//retrieve the refresh token from the users array
let refreshToken = payload.email.refreshToken
//verify the refresh token
try{
jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET)
}
catch(e){
return res.status(401).send()
}
let newToken = jwt.sign(payload, process.env.ACCESS_TOKEN_SECRET,
{
algorithm: "HS256",
expiresIn: process.env.ACCESS_TOKEN_LIFE
})
res.cookie("_login", newToken, {secure: true, httpOnly: true})
res.send()
My question now is, since I see in so many tutorial and guides that they also work with refresh token via POST, how do I process that with the user?
Would the client send an AJAX or post to middleware for check on access token
-> If Access token is expired
--> code automatically takes refresh token and issues a new access token and gives an OK?
or the client sends to middleware where access token is checked
-> Access token is expired (result to the user)
-> client make now post request to /refresh-token result = new access and refresh token
-> And again post request to original middleware with new post request?
What is the procedure here, I can't find any workaround how this is handled.
Keep in mind that my answer is based on my experience. Feel free for anyone to edit if I happen to make a mistake in my ways.
So, in order to handle refresh token, I use this way:
When a user successfully logs in, the JWT (containing user's auth) and the refresh token (containing the user's refresh token) will be placed in the user's cookies (same like you).
The user will do his/her things inside your web application, until he/she closes the browser without signing out.
Remember that JWT always have expiration date - this expiration date will be kept in mind.
In every request, you're going to send that specific JWT (that contains the user's auth) and the refresh token to a middleware where you are going to make a POST request. If the JWT has expired, pick the refresh token and call your /refresh-token-result to get a new token. Else, just don't do anything with the refresh token and proceed with your request.
Ensure that your /refresh-token-result accepts a request token. The endpoint will check for its validity and will return a new access token.
If the refresh token had expired, log out the user. This is for security reasons and this is important!
Oh, and when a user logs out, ensure that both of your user's token and your user's refresh token is revoked properly, usually by changing the cookie value and the expiresIn attribute. For me, I usually change both cookie values to loggedOut and I'll set the expiresIn to five seconds.
Alternatively, if you're using React (an additional answer), you can do it like this:
If a user accesses your website, and the JWT expiration date is close to expiry, you can simply use a useEffect() hook in order to renew your access token.
TL;DR: Your second way is already good.
EDIT: Sample pseudocode to help you. Don't copy paste this right away, it most likely wouldn't work, but it should give you the general idea of how things work.
// middleware.js
const { token, refreshToken } = req.cookies;
// 1. If the token has not expired, call 'next()'
// assume 'isExpired' returns boolean: true or false depending on the state of your token.
if (!token.isExpired()) {
return next();
}
// 2. If the token has expired AND the refreshToken has not expired, issue a new token, THEN call 'next()'
if (token.isExpired() && !refreshToken.isExpired()) {
await issueToken();
return next();
}
// 3. Else, logout the user. I'll keep this one short.
await logoutUser();
res.status(401).json({
status: 'fail',
message: 'Your access has expired! Please log in again!',
});
And this is your controller.
// controller.js
const getAllComments = async (req, res, next) => {
const comments = await Comment.find();
res.status(200).json({
status: 'success',
data: comments,
});
}
And, this is what your route should look like.
// this import might be unresolved - keep in mind!
const middleware = require('./middleware');
const getAllComments = require('./controllers');
router.get('/api/v1/comments/', middleware,
checkToken, getAllComments); // assume checkToken is your function to check for a token's validity.
Keep in mind I did not include error handling to keep this example short.

Cant assign a JWT to the header request

I have a problem using JWT. In the router responsible for the login page, on the post request, I assign a JWT for each login user, though I am not able to either send the token in the header of each page request after or actually save the token in the header. When I console log the token after each login and copy-paste it to the header in POSTMAN, it works fine, therefore i think this is my issue;
// generate a jwt for the login user
const token = jwt.sign({_id: this._id, isAdmin: this.isAdmin}, process.env.TOKEN_SECRET,
{expiresIn: 1800});
// send back to the homepage with the token in the header (?)
res.header('auth-token', token).redirect('/');
console.log(token);
And after this assignment, I have tried to access an authenticated users page and it didn't work, I think because of the reasons I have mentioned.
This is my auth middleware, responsible for the authentication I add to the routers:
const jwt = require('jsonwebtoken');
module.exports = function(req, res, next) {
const token = req.header('auth-token');
if(!token){
console.log('Access denied. No token provided.');
return res.status(401).redirect('/login');
}
try{
const decoded = jwt.verify(token, process.env.TOKEN_SECRET);
console.log(decoded);
req.user = decoded;
console.log('Access approved.');
next();
}
catch(ex){
console.log('Invalid token');
res.status(403).redirect('/');
}
}
I have no idea what am I missing. Tried to find a solution but nothing worked for me.
Thanks to the helpers!

How to send Bearer token to client and then call token from client

I have done a tutorial trying to get my head around JWT tokens. I seem to have got my head around the token creation as well as using the token to allow or disallow access to a route.
This all works great using postman, but in postman I enter the token under authorization. My question is:
1. how do I send the token to the client so it is saved on that client side.
2. How does the client return the token when they try to access a route?
I need to understand how this happens when NOT using postman. I am sure its pretty simple.
Do I just send
`res.header('Authorization', 'Bearer', + token);`
`res.header('Authorization', 'Bearer' + token);`
But can I send this with other stuff like a message / data etc?
Then when the user tries to access a protected route later, How do I access this header. IOW how is it stored client-side?
This is what I have thus far:
`//login route`
`app.post('/login', async function(req, res, next) {
const { name, password } = req.body;
if (name && password) {
let user = await getUser({ name: name });
if (!user) {
res.status(401).json({ message: 'No such user found' });
}
if (user.password === password) {
// from now on we'll identify the user by the id and the id is the
// only personalized value that goes into our token
let payload = { id: user.id };
let token = jwt.sign(payload, jwtOptions.secretOrKey);
res.json({ msg: 'ok', token: token });
} else {
res.status(401).json({ msg: 'Password is incorrect' });
}
}
});`
`// protected route
app.get('/protected', passport.authenticate('jwt', { session: false }), function(req, res) {
console.log('REQUEST HEADERS ON PROTECTED::',req.headers.authorization)
res.json('Success! You can now see this without a token.');
});`
The console.log under protected route gives me:
"REQUEST HEADERS ON PROTECTED:: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNTU2NjI3NTczfQ.gAU2VzpUpXHpcgM6_n8gf7D-xLCS59tK6K2RIlIk-L4" but I gather this is because I used the authorization in postman.
I recently worked with jwt auth using react as my front end and hapi.js as backend. To save the token on the client side, you can use localstorage like this:
You have to save this on the user login component.
localStorage.setItem('token', res.data.token);
And then, to access this token on the protected router, use this :
let token = localStorage.getItem('token');
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
I hope this may help you to solve your problem on the client side.

Access tocken authentication in express node js (without jwt or passport)

I'm building a REST API server witch generate (or fetch form Db if it already exist ) an access 32 char token and return it after user logged in , then each time user wants to do something , he/she should send the access token in header and what ever she/he wants to do in body , and with each request , server check validity of access token then respond to the user and token are valid only for a day if user wont sign out
so I wonder is this a safe method to authenticate users ? is there way to make it more secure ?
You could add a middleware into your express app and validate your token there if it's valid one proceed with it other wise return an error response to user.
app.use(function(req, res, next) {
// check header or url parameters or post parameters for token
var token = req.headers['x-access-token'];
// decode token
if (token) {
// verifies token
var isValidToken = validateToken(token); // function to validate token
if( isValidToken ){
next();
}else{
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});

Resources