Sign and Verify
There are several ways to save the JWT for later identify. In this article, I’d like to show you how to store JWT in cookie. We simply add cookie-based session middleware cookie-session.
import express from 'express'
import cookieSession from 'cookie-session'
const app = express();
app.set('trust proxy', true)
app.use(cookieSession({
signed: false,
secure: true
}))
secure
: a boolean indicating whether the cookie is only to be sent over HTTPS (false
by default for HTTP,true
by default for HTTPS). If this is set totrue
and Node.js is not directly over a TLS connection, be sure to read how to setup Express behind proxies or the cookie may not ever set correctly.signed
: a boolean indicating whether the cookie is to be signed (true
by default).
We set ‘trust proxy’ so ‘secure’ can be used along with Ingress.
In the signup rooter we might use below code to sign and store JWT in cookie.
const userJwt = jwt.sign({
id: user.id,
email: user.email
}, 'the secret here')
req.session = { jwt: userJwt }
If using postman to test it manually. We can see the session.
However, the value here is not exact JWT but the encoded string of { jwt: userJwt }. We can use https://www.base64decode.org to decode this string and see the result below.
{"jwt":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYzNmZjOTM5YTc4MTczZTc0NWE3ZDM0MCIsImVtYWlsIjoid2lsbDZAZ21haWwuY29tIiwiaWF0IjoxNjY4MjcwMzkzfQ.7Ien9r6oykZmftb-jih8sjWBAiIgP7osPGGxCGv9vZE"}
We copy the jwt value and check it in https://jwt.io
If we continue to decode base64 of this jwt, we can see the content like this.
{"alg":"HS256","typ":"JWT"}{"id":"636fc939a78173e745a7d340","email":"will6@gmail.com","iat":1668270393}zkꌤfgo#X""
So we can easily decode to get the readable content of JWT. However, in order to check if this JWT is valid, we have to use the secret to verify it. In node.js, I use jsonwebtoken so we verify it by calling verify method with the secret.
jwt.verify(token, 'shhhhh', function(err, decoded) {
console.log(decoded.foo) // bar
});
Encrypt and compare password
However, in order to get the token users must login by username and password. The password created must be stored in database as hash. We will have two functions. One for convert the password to hash. Another for comparing the supplied password with stored password.
Here is the class Password for first authentication by password. We firstly store toHash password in database, then using compare method to compare it with supplied password by submitting form.
import { scrypt, randomBytes } from 'crypto';
import { promisify } from 'util';
const scryptAsync = promisify(scrypt);
export class Password {
static async toHash(password: string) {
const salt = randomBytes(8).toString('hex');
const buf = (await scryptAsync(password, salt, 64)) as Buffer;
return `${buf.toString('hex')}.${salt}`;
}
static async compare(storedPassword: string, suppliedPassword: string) {
const [hashedPassword, salt] = storedPassword.split('.');
const buf = (await scryptAsync(suppliedPassword, salt, 64)) as Buffer;
return buf.toString('hex') === hashedPassword;
}
}
Thus, we have a class which takes responsibility for authenticating users by username and password. We have then a function for generating token. Whatever we want to put into this token such as user id, email, permissions etc. The token is signed by a secret string to make sure this is the one we issued at next request by verify function using the same secret key. This is not only for Node.js but the same manner applied to other languague.