Node JS: JWT verify vs redis query performance comparison? - node.js

I have implemented JWT token for authentication to my API which is served using node.js.
On every request sent to the server, I do a jwt.verify() but I'm wondering if this is more CPU intensive and therefore less scalable than storing the token in Redis, and retrieving the userId.
Example:
const jwt = require('jsonwebtoken')
const app = require('express')
app.get('/user',(req, res) => {
const { headers: { authorization } } = req
let token = null
if (authorization && authorization.split(' ')[0] === 'Bearer') {
token = authorization.split(' ')[1]
}
jwt.verify(token, process.env.TOKEN_SECRET, (err, decoded) => {
if(err || !decoded){
return res.json({success: false, message: `Not valid token`})
}
//
// Continue with my logic
//
})
})
So I'm wondering if anyone knows if it's better performance-wise to do jwt.verify() vs a redis.get()?

I think that's a subject to debate on but the answer heavily depends on what goals you are trying to achieve / which hardware do you use / how you plan to scale (as you have mentioned scalability)
TL;DR I would stick with jwt.verify() if it doesn't cause performance issues with your current load
For regular JWT token created with HS256 Algorithm, jwt.verify() is not a CPU intensive task that runs under <1ms on the modern CPU
For a Redis case we need to take into account two cases
We run a Redis instance on the same machine so we don't have to make a network call and lose time on latency or deal with the risk of the network error. But we anyway introduce a latency because 2 separate processes have to talk to each other but it would be the same <1ms
We have one global Redis server/cluster which stores our JWT tokens and we have to deal with network stuff and increase latency up to 30-50ms and even more
Also when we introduce Redis to our system we create an additional layer of the complexity to our system which has to maintained and etc.
Regardless of the fact that Redis brings an extra level of complexity, it's more Node.js-oriented philosophy to make everything async as much as we can and don't lock the CPU (EventLoop) with heavy tasks.
BUT using the Redis approach, we are kind of losing the advantages of the JWT itself (us userId already encrypted in token), and maybe we need to look at the solution which uses sessions to store userId instead of JWT. In most cases, we need to store JWT somewhere when the token becomes expired and we need to access the refresh token to create a new JWT.
So, I think that the best way would be to stick with jwt.verify() if it doesn't cause performance issues with your current load (like your server is not used at peak level all the time but that's another issue and topic to discuss)
P. S. You can use Node APIs like process.hrtime() to simply measure performance of your running code

Related

Building a Slack app that authenticates into external system using OAUTH

I am in the process of building a small test slack app and not clear on the architecture that is needed for authentication. This will be a NodeJS application that lives on Heroku.
When a user uses a /slash command, it is going to invoke logic that will query an external CRM system and return data. In order to authenticate into this external system, it would need to send the user through an OAUTH flow so that the access to the data is governed by the invoking users' permissions.
My confusion is to how to handle or persist these auth tokens/refresh tokens that we get back from the user authenticating during this process.
Example Steps:
Runs /user bob#gmail.com
Checks to see if user has authorized on this external system
If not, take the user through the external systems oauth flow
After authentication, we have the token that can be used to make callouts to the external systems API as that user.
Makes callout and returns data
How would I persist or check for the slack users auth/refresh token when they run the command to see if we already have it? If the token already existed, I wouldn't need to send them through the OAUTH flow again.
My Thoughts on the approach:
It almost seems like there needs to be a data store of some type that contains the slack user ID, auth token, and refresh token. When the user invokes the command, we check to see if that user is in the table and if so, we use their token to make the API call.
If they don't exist, we then send them through the OAUTH flow so we can store them.
Final Thoughts:
In terms of security, is having a table of tokens the correct way to do this? It almost seems like it's the equivalent of storing a plain text password if someone were to get that token.
Is there a better way to handle this or is this a common approach?
Your approach is right and the docs in slack API points out to this article that describe your use case where the third party is the Salesforce CRM.
In terms of security, is having a table of tokens the correct way to do this? ...
Yes, an attacher may steal your db data.
To avoid that you can store the tokens as encripted string.
In this way, a malicious user should:
steal your data from the db
steal your source code to understand what type of algorithm you are using to encript the tokens and the logic behind it
The approach is to spread all the info to get the clear token across system assuming
one or more systems can be compromised, not everyone!
Is there a better way to handle this or is this a common approach?
Usually the AES-256 is used, in detail aes-256-gcm or aes-256-cbc. There are some thread off
in performance and use cases you must deal with in order to prefer one or another.
Node.js supports both and an example logic could be:
const crypto = require('crypto')
const algorithm = 'aes-256-gcm'
const authTagByteLen = 16
const ivByteLen = 64
const keyByteLen = 32
const saltByteLen = 32
const oauthToken = 'messagetext'
const slackUserId = 'useThisAsPassword'
const salt = crypto.randomBytes(saltByteLen)
const key = crypto.scryptSync(
Buffer.from(slackUserId, 'base64').toString('base64'),
Buffer.from(salt, 'base64').toString('base64'),
keyByteLen)
const iv = crypto.randomBytes(ivByteLen)
const cipher = crypto.createCipheriv(algorithm, key, iv, { authTagLength: authTagByteLen })
let encryptedMessage = cipher.update(oauthToken)
encryptedMessage = Buffer.concat([encryptedMessage, cipher.final()])
const storeInDb = Buffer.concat([iv, encryptedMessage, cipher.getAuthTag()]).toString('base64')
/***
*
*/
const storeInDbBuffer = Buffer.from(storeInDb, 'base64')
const authTag = storeInDbBuffer.slice(-authTagByteLen)
const iv2 = storeInDbBuffer.slice(0, ivByteLen)
const toDencryptMessage = storeInDbBuffer.slice(ivByteLen, -authTagByteLen)
const decipher = crypto.createDecipheriv(algorithm, key, iv2, { authTagLength: authTagByteLen })
decipher.setAuthTag(authTag)
const messagetext = decipher.update(toDencryptMessage)
decipher.final()
const clearText = messagetext.toString()
console.log({
oauthToken,
storeInDb,
clearText
})
Notice:
the SALT logic will generate a new "storeInDb" string at every run without compromising the future reads
you may use the slack-user-id as password, so the attacher should know this information too
you must store the salt too or write an algorithm to generate it form the user id for example
the salt may be stored in a (redis) cache or other Services like S3, so the attaccher should break this other system to parse the tokens!
GCM example extrated by my module
You may find a CBC example here

How can I authorise an installation of an Electron application?

If you needed to do some kind of an authorization mechanism to an Electron application, what libraries/frameworks would you use for it?
Basic requirements would be that the user enters either a key or some identification information about himself and their right to use the application can be remotely allowed/blocked as necessary.
Kind of like a license key, but a bit more flexible in terms of defining when their right of use ends.
Your question is a little vague (and not really a programming question).
Are you talking about software licensing? I've researched this quite a bit and, while there are a bunch of turnkey solutions, they tend to be relatively expense (monthly subscription fees, etc).
I ended up settling on Easy Digital Downloads and their Software Licensing plugin. The latter enables setting a license expiration date if desired, update alerts and a bunch of other stuff. The tech support is also responsive. It is a WordPress system though – so you would need to set up a 'store' using WordPress.
The API is trivial to interface with through Javascript – to active a license, check license validity, and check for updates.
An open source project I found was Simple Licensing. That is free but is less well documented and there isn't any support.
It depends. I've detailed in this answer how to generate and verify cryptographically signed license keys without a license server. But cryptographic license keys like that are usually perpetual (though can have a fixed expiration inside of the key), and it sounds like you want something a bit more dynamic when it comes to a license's validity, since you mentioned that you want to be able to remotely block a license.
That's where a license server would come in.
At its core, most licensing servers are CRUD apps, meaning they're the typical server allowing you to create, read, update, and delete resources. In this case, that resource would be a license. You would want to create a server endpoint that takes a license key parameter, performs a database lookup for the key, and if the key exists check if the key is expired among other requirements such as its blocked column.
Here's a basic Express app that does that, minus the pg database bits:
const express = require('express')
const app = express()
const { Client } = require('pg')
const db = new Client()
const {
PORT = 8080
} = process
app.get('/validate/:licenseKey', async (req, res) => {
const { licenseKey } = req.params
const { rows } = await db.query('select * from licenses where key = $1::text limit 1', [licenseKey])
const [license] = rows
if (license == null) {
return res.send({ valid: false, code: 'NOT_FOUND' })
}
if (license.blocked) {
return res.send({ valid: false, code: 'BLOCKED' })
}
if (license.expiry < Date.now()) {
return res.send({ valid: false, code: 'EXPIRED' })
}
return res.send({ valid: true, code: 'VALID' })
});
app.listen(PORT, async () => {
await db.connect()
console.log(`Server is running on port ${PORT}`)
})
But in case you aren't particularly keen on writing and maintaining your own in-house licensing system, I’m the founder of a software licensing API called Keygen which can help you get up and running quickly without having to write and host your own license server.
Keygen is a typical HTTP JSON API service (i.e. there’s no software that you need to package with your app). It can be used in any programming language and with frameworks like Electron. As per your main requirement here, it supports remotely suspending (blocking) licenses.
In its simplest form, validating a license key with Keygen is as easy as hitting a single JSON API endpoint (feel free to run this in a terminal):
curl -X POST https://api.keygen.sh/v1/accounts/demo/licenses/actions/validate-key \
-d '{
"meta": {
"key": "C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3"
}
}'
I recently put together an example of adding license key validation, as well as device activation and management, to an Electron app. You can check out that repo on GitHub: https://github.com/keygen-sh/example-electron-license-activation.

node js architecture instantiating a module that gets access token for each user concurrency

How can I achieve concurrency in this situation so Node Module can serve multiple users?
I'm developing two pieces of a codebase for a client application.
Node App
Node Module (collection of classes)
Node app provides credentials for user1 to Node Module that in turns gets the access_token and then uses (both token and credentials) for subsequent calls.
I prefer that Node Module takes care of managing the access token and credentials for all calls but then it limits to 1 user since node runs in a single process.
One of my Node module classes looks like this, (see the issue) I'm providing credentials and authentication (token) to each function call besides other params (not a good interface). Wondering if this can be any better so I can have cleaner interface of each function call besides achieving concurrency.
var Entity = function() {};
Entity.prototype.requestList = function(credentials, authentication, publication, entityType) {
return new Proimse(function(resolve, reject){
});
};
module.exports = new Entity();
Edit
One solution that just came to me is to return the access_token (3rd party token) besides the authToken (my app token) back to the client, so clients cache them and send them along with each subsequent request so server side code doesn't have to maintain or manage it. Concurrency achieved.

JWT Authorization Over Socket.io Connection

The fact that I haven't found an existing answer for this makes me think I'm asking the wrong question. Please feel free to (gently or otherwise) push me onto a better path if necessary.
We use a dedicated auth server, the purpose of which is to (1) given login credentials, return a JWT with a near-term exp or (2) given a JWT, according to a set of rules, issue a new JWT. A refresh, essentially.
That all works ace, until it's hacked. But for now, it's ace.
When it comes to socket.io connections to non-auth servers, however, we're shooting more than a bit from the hip. I wonder if somebody would be so kind as to evaluate this process. (I'm happy to post more code; you tell me if it's relevant).
1) initial socket.io connection results in a challenge:
this.socket.emit('authenticate'); // the challenge
this.authTimeout = setTimeout(() => {
this.socket.disconnect('unauthorized', errors);
}, TIME_TO_AUTHENTICATE); // the response kills this!
this.socket.on('authenticate', token => {
clearTimeout(this.authTimeout);
this._authenticate(token)
})
2) subsequent messages must contain a "payload" message in the form:
payload = {token: 'foo', message: 'bar'}, which token would be accepted if valid or returned if invalid.
In addition, the resource server sends its own periodic heartbeat, which must be acknowledged by heartbeat {token}.
My question, thus is: this seems too easy; am I cutting corners somewhere? Could you defeat this feeble fortification?
Just to be clear, we're looking to roll our own module here. I'm happy to look at anything existing; just haven't found anything I could begin to convince the bosses is fully baked for our needs.
Many thanks in advance.
I cannot fully analyse the method or ensure it doesn't have flaws, however I'd like to point out some things that came up to mind:
Apart from disconnecting the user in case of timeout on authentication challenge, you must ensure that the server does not send any non-public message to this user until after the authorization challenge is actually fulfilled successfully. Otherwise, there is a period until timeout where the user could receive a message without being authenticated.
I assume that you are also disconnecting the socket if token is invalid (or someway preventing non-public message to be sent).
This article is about authenticating socket.io communications using JWT. It is from 2014 so it might be a little bit out of date but I think that the core concept is still valid.
Associated with the article, there is a tool built specifically to authenticate socket.io connections using jwt. Even if you don't want to use it, you might want to explore its code looking for "inspiration". You can find it here: socketio-jwt.
You can see that this tool is able to use two different approaches:
An approach pretty similar to yours:
from socketio-jwt/blob/master/lib/index.js
if(options.required){
var auth_timeout = setTimeout(function () {
socket.disconnect('unauthorized');
}, options.timeout || 5000);
}
socket.on('authenticate', function (data) {
// ...
// Token validation
// Emit "authenticated" event if token is valid, the server can use
// this event as a point to send messages, once token is valid
});
A "One roundtrip" approach that basically uses query strings during handshake. And whose main drawback is that the token is exposed in the URL, so it might be logged, or getting exposed.

Generating API tokens using node

I am writing an app that will expose an API. The application allows people to create workspaces and add users to them. Each user will have a unique token. When they make an API call, they will use that token (which will identify them as that user using that workspace.
At the moment I am doing this:
var w = new Workspace(); // This is a mongoose model
w.name = req.body.workspace;
w.activeFlag = true;
crypto.randomBytes(16, function(err, buf) {
if(err){
next(new g.errors.BadError503("Could not generate token") );
} else {
var token = buf.toString('hex');
// Access is the list of users who can access it. NOTE that
// the token is all they will pass when they use the API
w.access = { login: req.session.login, token:token, isOwner: true };
w.save( function(err){
if(err){
next(new g.errors.BadError503("Database error saving workspace") );
Is this a good way to generate API tokens?
Since the token is name+workspace, maybe I should do something like md5(username+workspace+secret_string) ...?
If you using mongodb just use ObjectId, othewise I recommend substack's hat module.
To generate id is simple as
var hat = require('hat');
var id = hat();
console.log(id); // 1c24171393dc5de04ffcb21f1182ab28
How does this code make sure your token is unique? I believe you could have collision of numbers with this code. I believe you need to have a sort of sequence number like in this commit from socket.io.
Also you could use npm projects like for example:
UUIID (v4)
hat
to ensure uniqueness.
I think the following are the best solution for Generating API tokens
JWT (Json web Token)
Speakeasy - This generate token based on timebased twofactor authentication like google authenticator
Speakeasy is more secure because this key is only available for a small time period (e.g, 30 second)
Why not just use UUIDv4 if you are looking for something unique? If you are interested in some other type of hashing (as mentioned previous hat is a good choice), you might look at speakeasy - https://github.com/markbao/speakeasy. It not only generates random keys but it can also create timebased twofactor authentication keys if you ever really want to layer on additional security strength.

Resources