How to sign a cookie manually using cookieParser? - node.js

For the sake of testing, I need to provide a signed cookie with HTTP request. So that, my Express app server can consider it as a signed cookie and put it into req.signedCookies object.
However I cannot find a appropriate method in docs.
I'd like to do the following:
let signed = cookieParser.signYourCookie({ cookieName: 'cookieValue' }, secretString);
// => cookieName=cookieValue.9PuJzypXeGq3tc2fFvlukjgNZ518jk
That is an operation opposite to cookieParser.signedCookie(str, secret) method. ExpressJS does it automatically under the hood, but there is a need to sign a cookie manually sometimes and the method seems missing.
To explain why I need this. I use Chai-http and need to set a cookie with the request. And I need it to be a signed cookie, so my server could find it it req.signedCookies object:
chai.request('http://foo.com')
.get('/url/path')
.set('my-signed-cookie', 'value-of-my-signed-cookie')

The plugin doesn't have public methods for that. Which is odd, actually. So I pulled the piece from plugin's code.
Do in your app:
var crypto = require('crypto');
function sign(val, secret){
return val + '.' + crypto
.createHmac('sha256', secret)
.update(val)
.digest('base64')
.replace(/=+$/, '');
};
// Pay attention to `s:` prefix. With that, plugin considers it as a signed cookie, apparently
.set('cookie', 'my-signed-cookie=s:' + sign('value-of-my-signed-cookie', 'my-cookie-secret'))
// Is equivalent to
.set('cookie', 'my-signed-cookie=s:value-of-my-signed-cookie.Dq+0CW44ZLfvzVWqEZEcK51X6auKaz771jFy4Zs4lWk')

Related

get access token from back and cache it to browser

hey guys. i'm working with axios,nodeJS and vue . i want
to generate an accessToken in node (it's done) and get it
with axios (it's done too) and cache it to browser cache with
vue(don't know how), so whenever i want to send a request to
node,i could send it with headers of axios , i dont know if it's
possible or not ? need some help around here.
Yes, that's possible and is pretty common.
To do it, you just have to store the token in the browser memory (usually in the localStorage) and then use axios interceptors on the requests to automatically add the token.
How you can add items to local storage?
localStorage.setItem('my-key',my-value)
you can find the complete docs here.
axios.interceptors.request.use(
config => {
//here retrieve the token and add it (if present)
//most probably you would use VUEX for storing it
//in you app
const token = yourLocalStorageAccessor.getAccessToken();
if (token) {
config.headers['Authorization'] = 'Bearer ' + token;
}
// config.headers['Content-Type'] = 'application/json';
return config;
},
error => {
Promise.reject(error)
});
You can read about axios interceptors here.

Is there a module that allows you to generate JWT token in angular on client-side itself without actually having to make a request to server-side?

What I want to achieve is to be able to generate JWT token for signing in user to another website which has a readme page, the JWT token should be generated on the client-side itself which is in angular. I can't seem to able to find a module which would do that for me. Angular-jwt only handles jwt token doesn't generate them. I want to do something similar to below in angular on runtime to generate the redirect link with appropriate auth_token.
The code below is how it can be done in nodejs but is there a way to make this work in angular without having to make any request to the server side?
import 'jsonwebtoken';
function readMeLogin(){
const sign = require('jsonwebtoken').sign;
const user = {
name: 'abc', //Actual user name
email: 'abc#gmail.com', //Actual user email
// User's API Key
apiKey: 'key', //STATIC
};
const auth_token = sign(user, 'secretKey');//STATIC
const readMeUrl = 'url.com'; //STATIC
return '${readMeUrl}?auth_token=${auth_token}'
};

Unable to validate Twilio request in Google cloud function

I have a Google cloud function to which Twilio sends POST requests with SMS statuses but I am unable to verify that the requests are coming from Twilio using any of the methods outlined in https://www.twilio.com/docs/usage/security
My first attempt consisted of using the validateRequest function, as shown in the code below
const twilio = require('twilio');
let url = 'https://....cloudfunctions.net/...'
let token = 'XXXX';
let header = request.headers['x-twilio-signature'];
let sortedKeys = Object.keys(request.body).sort();
let sortedParams = {};
sortedKeys.forEach(key => {
sortedParams[key] = request.body[key];
});
let validated = twilio.validateRequest(token, header, url, sortedParams);
I confirmed that the value of token matched the auth token from the Twilio account settings, sortedParams contained alphabetically sorted camel-cased Twilio request params and the url matched that which was passed to the Twilio client when creating the SMS. However, validateRequest would always return false.
My next attempt involved hashing the combination of the url and request params by copying the code from https://www.twilio.com/docs/libraries/reference/twilio-node/3.18.0/webhooks_webhooks.js.html
const crypto = require('crypto')
sortedKeys.forEach(key => {
url = `${url}${key}${request.body[key]}`;
});
let signature = crypto
.createHmac('sha1', token)
.update(Buffer.from(url, 'utf-8'))
.digest('base64');
Upon comparing the value of signature to that of the header, the two never matched.
Twilio developer evangelist here.
I recommend using the validateRequest method as that does most of the work for you.
You don't need to perform the parameter sorting that you've attempted, JavaScript objects are unordered and the library sorts and appends the parameters to the URL string already.
Things you need to check are that the URL is the exact webhook URL you set in your Twilio console, including the entire path and any query parameters that are included.
Also, have you ensured that request.body is populated and that your express app is using body-parser to parse the incoming request as url encoded form parameters?
app.use(bodyParser.urlencoded({ extended: false }));
If you are trying to validate the request as middleware, make sure that the request validation is done after body parsing.
Does any of that help at all?
It turns out that the there was nothing wrong with the validateRequest but rather the way I was declaring the token. Instead of hard-coding it in the function's code, it was being retrieved from a Google storage bucket as a buffer and then converted to a string. For unknown reasons, even though visually, the retrieved value matched the original token, a === comparison returned false. Once I hard-coded the token, everything worked.

Using JWT tokens. Is there a better approach?

I'm using JWT tokens via nJWT package to authenticate my users to my Socket.io using socket.io-jwt package.
More or less, the code looks like this. User sends a POST reques to play/login via HTML form to generate a JWT token. Then, socket.io client initializes using that token.
/**
* Create Express server.
*/
const app = express();
const http = require('http').Server(app);
const io = require('socket.io')(http);
const socketioJwt = require('socketio-jwt');
app.set('jwt.secret', secureRandom(256, {
type: 'Buffer'
}));
app.post('/play/login', (req, res) => {
// validate user's req.body.email and req.body.password
const claims = {
iss: "http://app.dev", // The URL of your service
sub: "user-1", // The UID of the user in your system
scope: "game"
};
const jwt = nJwt.create(claims, app.get("jwt.secret"));
const token = jwt.compact();
new Cookies(req,res).set('access_token', token, {
httpOnly: true,
secure: process.env.ENVIRONMENT === "production"
});
tokenUserRelations[token] = req.body.email;
res.json({
code: 200,
token: token
});
});
/**
* Add Socket IO auth middleware
*/
io.set('authorization', socketioJwt.authorize({
secret: app.get("jwt.secret"),
handshake: true
}));
io.sockets.on('connection', function (socket) {
socket.on('chat message', function (req) {
io.emit("chat message emit", {
email: tokenUserRelations[socket.handshake.query.token],
msg: req.msg
});
});
socket.on('debug', function (req) {
io.emit("debug emit", {
playersOnline: Object.keys(tokenUserRelations).length
});
});
socket.on('disconnect', function (req) {
delete tokenUserRelations[socket.handshake.query.token];
});
});
io.listen(app.get('socket.port'), () => {
console.log('Started! Socket server listening on port %d in %s mode', app.get('socket.port'), app.get('env'));
});
Right now, it works properly, but in order to track emails from tokens, I had to do this:
tokenUserRelations[token] = req.body.email;
so I can relate which user the token points to.
I have a feeling that keeping token<->email relations in a global object is going to cause me headaches in the future, especially when tokens/cookies expires.
Is there any better way about this? I need to know which user that JWT token points to so I can do some business logic with them.
Thank you.
A token can contain information about anything you want, this information is encrypted along the token.
What you can do is encrypt a user id in the token, when you receive a request, decrypt the token (which is anyway done when you verify it), and use the user id as normal.
This way, if the token expire, the new token will have the same user id, and your code will not be impacted.
This is what I did in one of my web app, and it worked fine. However, I was using the official jwt module
You don't show anything in your code about how tokenUserRelations is created or maintained, but as soon as I hear "global" a red flag goes up in my head.
The JWT standard includes the concept of embedding 'claims' in the token itself; you're already doing so with your claims constant. That data format is arbitrary and can be trusted by your app so long as the overall JWT gets validated. Note that you'll want to verify JWT on every request. So, stuffing email into that claims object is not just fine, it's what most folks do.
As a sidenote, you should be careful about how you're setting your 'jwt.secret' right now. What you have now will generate a new one every time the app starts up, which means that a) all your users will be logged out and have to re-login every time the app restarts, and b) you can't make use of multiple processes or multiple servers if you need to in the future.
Better to pull that from the environment (e.g. an env var) than to generate it on app start, unless you're just doing so for debugging purposes.
Adding to the excellent answers above, it is also important that if you decide to store your jwt.secret in a file and pull that in when the code loads that you do not add that to your git repository (or whatever other VCS you are using). Make sure you include a path to 'jwt.secret' in your .gitignore file. Then when you are ready to deploy your production code you can then set that key as an environment variable as suggested. And you will have a record of that key in your local environment if you ever need to reset it.
Using JWTs is an excellent and convenient way of securing your api, but it is essential to follow best practice.

How can you use cookies with superagent?

I'm doing cookie session management with express with something like this:
req.session.authentication = auth;
And I verify the authenticated urls with something like
if(!req.session.authentication){res.send(401);}
Now I'm building tests for the URLs with mocha, superagent and should, however I can't seem to find a way to get/set the cookie with superagent. I even tried to request the login before the authenticated test but it is not working,
I have tried adding the request to the login in the before statement for the mocha BDD suite, however it is still telling me that the request is unauthorized, I have tested the authentication doing the requests from the browser, however it is not working from the suite any ideas why?
Use superagent.agent() (instead of plain old superagent) to make requests have persistent cookies. See 'Saving cookies' in the superagent docs, or the code examples: agency.js, controller.test.js.
Seems like following code works fine;
req.set('Cookie', "cookieName1=cookieValue1;cookieName2=cookieValue2");
If the issue is in sending cookies for CORS requests use .withCredentials() method
described here
request
.get('http://localhost:4001/')
.withCredentials()
.end(function(err, res) { })
Since you mentioned you need to both get and set the cookie:
Get:
const request = await Superagent.get('...')
const cookie = request.header['set-cookie']
Set:
Superagent.post('...').set('Cookie', 'cookie_info')
2020 +
A clean way to do it is:
create a simple cookie store
abstract set Cookie to send it in each request
update the cookie only when needed
Note I keep the same URL because I use graphql but you can make it a parameter:
const graph = agent =>
agent.post('/graph')
.set('cookie', cookieStore.get());
const handleCookie = res =>
cookieStore.set(res.headers['set-cookie'][0]);
let currentCookie="";
const cookieStore = {
set: cookie=>{currentCookie=cookie},
get: cookie=>currentCookie,
};
module.exports = {graph,connectTestUser,handleCookieResponse};
You can now just use graph(agent) to send a request and handleCookie(response) when you have a response that may update your cookie (set or clear), example:
graph(agent).end((err,res) => {
if (err) return done(err);
res.statusCode.should.equal(200);
handleCookie(res);
return done();
});
Add a cookie to agent cookiejar:
const request = require('superagent');
const {Cookie} = require('cookiejar')
const agent = request.agent()
agent.jar.setCookie(new Cookie("foo=bar"))

Resources