How to parse signedCookies in express? - node.js

I send a cookie to my front-end thanks to express:
// signing the token
static generateToken(user: PartialUser): Cookie {
return jwt.sign({ _id: user._id }, process.env.JWT_TOKEN_KEY, {
expiresIn: "14d",
});
// sending the cookie
return res
.status(200)
.cookie("myApp", token, {
expires: new Date(Date.now() + msPerDay * 14),
httpOnly: true,
secure: true,
})
.json({ user });
// initializing cookie parser in index.js:
app.use(cookieParser(process.env.JWT_TOKEN_KEY));
//parsing the cookie
const authenticate = (req: Authenticate, res: Response, next: NextFunction) => {
const { myApp } = req.signedCookies;
if (req.signedCookies) {
return jwt.verify(
myApp,
process.env.JWT_TOKEN_KEY,
(error, parsedToken) => {
if (error) {
return res.sendStatus(403);
}
req.cookie = { _id: parsedToken._id };
return next();
}
);
}
return res.sendStatus(401);
};
The req.signedCookies object is always empty. So all my routes return a 403 - forbidden access. However, if I don't specify secure: true when sending the cookie, it works, because I can access it in req.cookies. The network tab shows that the cookie is correctly send along the client request.
How to fix this?
ps: I'm fine with using req.cookies, but my express server is hosted on Heroku and it never sends the cookie to the client, which is a custom https domain. This is why I'm trying the secure:true option. For now, it only works in localhost. Maybe the solution is elsewhere?

A cookie signature on one hand, and the secure option on the other hand, are actually two different things.
The secure option restricts the cookie to be sent over https only. This is intended at avoiding eavesdropping over the network. Incoming cookies that are set as secure will by default always be exposed on req.cookies by cookie-parser.
A cookie signature on the other hand is basically a cryptographic hash that is intended at making a cookie tamper-proof. It seems that with the cookie-parser package, you sign a cookie with the signed: true option. Only incoming cookies that have been explicitly signed will be exposed on req.signedCookies. Note this is all regardless of the secure option.

Related

How to set a cookie with Axios and ExpressJS

I want to set a cookie after logging in. This is my log in method:
(The res.cookie stuff doesn't work)
router.post("/login", (req, res) => {
const userForToken = { username: req.body.username };
const token = auth.generateAccessToken(userForToken);
axios
.post(userAPI + req.path, req.body)
.then(() => {
res.cookie("talloc_user_cookie_token", token, {
maxAge: 60 * 60 * 24 * 7,
httpOnly: true,
});
res.send({
username: req.body.username,
token: token,
});
console.log(
`✅ Succesfully logged as <<${req.body.username}>> Status Code: ${res.statusCode}`
);
})
.catch(() => {
res.sendStatus(404);
console.log("⛔ Error logging in. Status Code: ", res.statusCode);
});
});
I have been reading other questions and forums and everybody says to use { withCredentials: true }, which in my case just gets the request to not work and just catch the error. I have also saw that I'd need to add the Access-Control-Allow-Origin header in the request, but It doesn't seem to work too.
For anyone wondering, I am trying to make a double token submit, where I have a JWT token set in localStorage, and a cookie that also has that token, so whenever my app's middleware authenticates the user, it has to check whether the token is correct and also if the token in localStorage is equals to the cookie's token, because cookie's values cannot be changed, whereas in localStorage it is in fact changable.

Sending a cookie as a response with Firebase Callable Functions

I am trying to send a cookie with options set to it as a response using a Firebase callable cloud function (https.onCall). I see in the Firebase docs that this can be done with express:
(The below is taken directly form the Firebase docs)
app.post('/sessionLogin', (req, res) => {
// Get the ID token passed and the CSRF token.
const idToken = req.body.idToken.toString();
const csrfToken = req.body.csrfToken.toString();
// Guard against CSRF attacks.
if (csrfToken !== req.cookies.csrfToken) {
res.status(401).send('UNAUTHORIZED REQUEST!');
return;
}
// Set session expiration to 5 days.
const expiresIn = 60 * 60 * 24 * 5 * 1000;
// Create the session cookie. This will also verify the ID token in the process.
// The session cookie will have the same claims as the ID token.
// To only allow session cookie setting on recent sign-in, auth_time in ID token
// can be checked to ensure user was recently signed in before creating a session cookie.
getAuth()
.createSessionCookie(idToken, { expiresIn })
.then(
(sessionCookie) => {
// Set cookie policy for session cookie.
const options = { maxAge: expiresIn, httpOnly: true, secure: true };
res.cookie('session', sessionCookie, options);
res.end(JSON.stringify({ status: 'success' }));
},
(error) => {
res.status(401).send('UNAUTHORIZED REQUEST!');
}
);
});
I have implemented the callable function, but I do now know how to attach the options to my cookie string.
The below is my code:
// I want the return type to be a Promise of a cookie object, not a string
export const setCookie = https.onCall(async (context: https.CallableContext): Promise<string> => {
try {
console.log(context);
const auth: Auth = getAuth();
const idToken: DecodedIdToken = await auth.verifyIdToken(context.instanceIdToken!); // https://firebase.google.com/docs/auth/admin/verify-id-tokens#web
console.log("idToken: ", idToken);
const cookie: string = await auth.createSessionCookie(idToken.uid, { expiresIn: 300000 });
const options = {
maxAge: 300000,
httpOnly: true,
secure: true,
sameSite: "strict",
};
// res.cookie("session", cookie, options);
return cookie; // should be assigned to __session cookie with domain .web.app
// httpOnly=true, secure=true and sameSite=strict set.
} catch (error) {
console.log("ERROR FOUND: ", error);
throw new https.HttpsError("unknown", "Error found in setCookie");
}
});
Is there any way I can do this using a Callable Firebase Cloud Function? All the documentation and resources I have found require express to send an cookie with Node.
Thanks!
The documentation you're linking to assumes you are writing standard nodejs backend code using express. However, your code is using a callable type function. They are not the same and do not have the same capabilities. Callable functions don't let you set cookies in the response. You can only send a JSON payload back to the client; the SDK handles all of the HTTP headers and they are outside of your control.
Perhaps you should look into using a standard HTTP type function (onRequest), where you do have some control over the headers in the response.

JWT Authentication with React/Node

I am facing a 'strange' behavior while trying to implement an authentication using JWT httpOnly in a Node/React application.
Basically I have a "Login" service that would provide a response containing a httpOnly access token if the user and password are correct. If so, the response will be as follows:
(NODEjs - Login snippet)
res.cookie(
"access_token",
{ token: token },
{
maxAge: 3600,
httpOnly: true
}
)
That means that from now on I can only call my APIs by using this web token in this case using parameter withCredentials = true
(Frontend Snippet)
handleTokenVerification(event){
axios.post('/auth/getUserInfo', {withCredentials: true})
.then(function(data) {
console.log(data)
}).catch(err =>{
console.log('ERRO: ' + err)
})
event.preventDefault()
}
It does work when a call a API for the first time, after that, it seems that this cookie was reset some how because de API does not work anymore.
The problem was in the maxAge: 3600 parameter. Was too low and I was not able to perform a couple requests before it expired.

How to secure JWT token and refresh token in nodejs/angular

i am new in node js. I am building a simple notes taking app and wanted to use JWT tokens for authentication and to secure my API's. On research i came to know that i need to create two tokens:
access token (short expire time like 10 minutes)
refresh token (longer expire time 30 days)
My config file
"secret": "*************",
"refreshTokenSecret": "*************",
"port": 5000,
"tokenLife": 900,
"refreshTokenLife": 86400
Code for middleware
const jwt = require('jsonwebtoken')
const config = require('./config')
module.exports = (req,res,next) => {
const token = req.body.token || req.query.token || req.headers['x-access-token']
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, config.secret, function(err, decoded) {
if (err) {
return res.status(401).json({"error": true, "message": 'Unauthorized access.' });
}
req.decoded = decoded;
next();
});
} else {
// if there is no token
// return an error
return res.status(403).send({
"error": true,
"message": 'No token provided.'
});
}
}
Here is the response
access token can be saved in local storage. but articles said save refresh token as http-only cookie.
i need the answer of following points (Keeping in mind that i am just a beginner):
How to store refresh token as http-only cookie (any node-js code
example would be a great help)?
How to secure it on client side and should I save refresh token to database?
Is there any other better solution to secure my API's?
You can use an http-only cookie using the following:
public authenticateUser(user: User, res: Response) {
const authJwtToken = this.generateJWT({
email: user.email,
uuid: user.uuid
});
const cookieOptions = {
maxAge: 3600000,
secure: true,
httpOnly: true
};
res.cookie('access_token', authJwtToken, cookieOptions);
}
// you can then res.send({...}) or wtv
Not that there is nothing from preventing you to store more than one cookie so I can't see a reason why not storing both of them in the same manner.
Now whether you will store it on the database depends on what you want to achieve.
Generally it is not required but note that in that case the server cannot in any way invalidate a single JWT. (You could in theory change the signing key but this would invalidate all of them).
In case you want to be able to achieve functionality such as 'log me out of all devices' you would need to store the JWTs issued for each user in a database (preferably an in-memory one such as Redis or Memcached) and do a second check with the extra information on whether they have been invalidated or not - even though such functionality is typically achieved using sessions instead of JWT
See this example how i secured my getByRefId api in nodjs :
In routes file :
router.get("/refId/:refId", helper.auth, groupController.getByRefId);
helper.auth is function :
auth: (req, res, next) => {
var token = req.body.token || req.headers['authorization'] || req.headers['Authorization'];
if (token.startsWith('Bearer ')) {
// Remove Bearer from string
token = token.slice(7, token.length).trimLeft();
}
if (token) {
jwt.verify(token, 'MY_SECRET', function (err, decoded) {
if (err) {
console.error('JWT Verification Error', err);
return res.status(403).send(err);
} else {
req.payload = decoded;
return next();
}
});
} else {
res.status(403).send('Token not provided');
}
}
This use jwt = require('jsonwebtoken') library you can install it in nodejs project

How to receive a cookie on reactjs from nodejs

I'm attempting to create a login where login data is sent to the nodejs server and if that login data is correct the server will send a JWT token through "res.cookie", what I would like to know is how will the reactjs client receive this cookie and also clear this cookie.
app.post('/login', (req, res) => {
const userData = {
email: req.body.email,
password: req.body.password
}
if(userData.email === email && userData.password === password){
const payload = { email };
const token = jwt.sign(payload, secret, {
expiresIn: '1h'
});
console.log(token)
res.cookie('token', token, { httpOnly: true })
.sendStatus(200);
}else{
res.send('incorrect params')
}
console.log(userData)
})
Here are the steps that need to happen.
User types their username and password and clicks sign in
Server receives credentials and validates them.
Server then creates a JWT for the users session and the server creates a cookie that contains the value of the jwt
the server simply returns and the cookie will be delivered as long as it is part of the response object.
hit control/command + i and view the cookie in chrome under your applications tab on the dev tools.
Also you probably want to use the nodejs https://www.npmjs.com/package/cookie-parser package to make your life easier.

Resources