Martin Novac

JWT: Ultimate How-To Guide With Best Practices In JavaScript

JSON Web Token in Node.js from basics to code examples.

Add to Pieces

JSON Web Token (JWT) is a standard RFC 7519 for exchanging cryptographically signed JSON data. It is probably the most popular current standard of authorization on the web, especially when it comes to microservices and distributed architecture. As a developer, when you are asked to implement a modern web application, you may need to break it down into independent services. Independent services and distributed architecture have many advantages.

Creating a JSON Web Token

Tags: javascript, mkdir, dart, typescript, flutter, node.js

Node.js has a great library from auth0 guys for JWT: jsonwebtoken, which is directly featured on the JWT webpage. Create a token by calling this.

import jwt from 'jsonwebtoken';

const data = { some: 'json' };
const key = 'MuchSecretVerySecureSoSafe';

jwt.sign(data, key);

Related links:

  1. https://cloud.google.com/go/getting-started/session-handling-with-firestore
  2. https://crypto.stackexchange.com/questions/60383/what-is-the-difference-between-ecdsa-and-eddsa
  3. https://www.npmjs.com/package/express-session
  4. https://medium.com/better-programming/jwt-ultimate-how-to-guide-with-best-practices-in-javascript-f7ba4c48dfbd
  5. https://redis.com/solutions/use-cases/session-management/
  6. https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
  7. https://aws.amazon.com/caching/session-management/
  8. https://www.npmjs.com/package/jsonwebtoken
  9. https://www.npmjs.com/package/axios

Call FrontEnd App with Auth Header using Axios

Tags: reactjs, javascript

The frontend app needs to add the token in a header to every request that requires authorization. This is how such a call might look with axios library.

import axios from 'axios';

cons postToMyApi = payload =>
  axios.post(
      '/v1/my-api',
      {
          "request": payload.request
      },
      {
          headers: {
              "Accept": "application/json",
              "Content-Type": "application/json",
              "Authorization": \`Bearer \${payload.token}\`
          }
      }
  );

Related links:

  1. https://cloud.google.com/go/getting-started/session-handling-with-firestore
  2. https://crypto.stackexchange.com/questions/60383/what-is-the-difference-between-ecdsa-and-eddsa
  3. https://www.npmjs.com/package/express-session
  4. https://medium.com/better-programming/jwt-ultimate-how-to-guide-with-best-practices-in-javascript-f7ba4c48dfbd
  5. https://redis.com/solutions/use-cases/session-management/
  6. https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
  7. https://aws.amazon.com/caching/session-management/
  8. https://www.npmjs.com/package/jsonwebtoken
  9. https://www.npmjs.com/package/axios

Server Side Verification of Token and Access Secrets

Tags: jsonwebtoken, jwt, javascript

Example of accessing verifying tokens on the server side and accessing its secrets.

import jwt from 'jsonwebtoken';

const key = 'MuchSecretVerySecureSoSafe';

const theSecret = jwt.verify(token, key); // returns the original encrypted JSON

Related links:

  1. https://cloud.google.com/go/getting-started/session-handling-with-firestore
  2. https://crypto.stackexchange.com/questions/60383/what-is-the-difference-between-ecdsa-and-eddsa
  3. https://www.npmjs.com/package/express-session
  4. https://medium.com/better-programming/jwt-ultimate-how-to-guide-with-best-practices-in-javascript-f7ba4c48dfbd
  5. https://redis.com/solutions/use-cases/session-management/
  6. https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
  7. https://aws.amazon.com/caching/session-management/
  8. https://www.npmjs.com/package/jsonwebtoken
  9. https://www.npmjs.com/package/axios

Full Jsonwebtoken Implementation with HS512

Tags: javascript, key, jwt, markdown, jsonwebtoken

A full implementation of a jsonwebtoken with HS512 would look like this and can be used to model other web tokens.

import jwt from 'jsonwebtoken';

// don't fortget to generate your own private and public key
// by openssl: https://gist.github.com/MeetMartin/8b3e72e4b94cdc6d063671a69440a7e2

const privateKey =
`-----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIANjT0EzYXVqsHeKitVMvAQ57pzZWcv5QEWsCap4hl3mk/DIkRCYzg
9YGcBtLWhuNitEeKFGLi91rohc2EzmTVIbOgBwYFK4EEACOhgYkDgYYABAFjzu7a
UwPT7fIFtwc89Vrkj4a1iXOYNhWEZ97V2EbpFq3AU28o7MoV+IbSv/VrGsHA/1SD
OOFabnyu3QbVxOywXQAEOowT5Hw6ue+ULtRqQ7m7gs1UMyaf6k9Sqv1zxkrNoWBA
OzMWISxnOoNE3Zn3jaUTv9jBSki5mheMALDaSoIGxw==
-----END EC PRIVATE KEY-----
`;
const publicKey =
`-----BEGIN PUBLIC KEY-----
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBY87u2lMD0+3yBbcHPPVa5I+GtYlz
mDYVhGfe1dhG6RatwFNvKOzKFfiG0r/1axrBwP9UgzjhWm58rt0G1cTssF0ABDqM
E+R8OrnvlC7UakO5u4LNVDMmn+pPUqr9c8ZKzaFgQDszFiEsZzqDRN2Z942lE7/Y
wUpIuZoXjACw2kqCBsc=
-----END PUBLIC KEY-----
`;

const data = {
    iss: 'https://domain.tld',
    sub: 'martin@mail.tld',
    aud: 'https://domain.tld',
    exp: Math.floor(Date.now() / 1000) + (60 * 60), // 1 hour
    nbf: Math.floor(Date.now() / 1000),
    jti: 'asfasgsadg',
    data: { some: 'information' }
};

const encrypted = jwt.sign(data, privateKey, { algorithm: 'ES512' });
console.log('encrypted', encrypted);
/* base64 decoded token is
{"alg":"ES512","typ":"JWT"}{"iss":"https://domain.tld","sub":"martin@mail.tld","aud":"https://domain.tld","exp":1630993148,"nbf":1630989548,"jti":"asfasgsadg","data":{"some":"information"},"iat":1630989548}Y?ykԒFjp'9ܮjv]]_ߜB~%X oX3@'VB=eqU&}TY%̒!%mLF
claims are not encrypted without combining JWT with JWE
*/

const decrypted = jwt.verify(encrypted, publicKey, { algorithms: ['ES512'], audience: 'https://domain.tld', issuer: 'https://domain.tld' });
console.log('decrypted', decrypted);
// verify throws error if token is expired or some other claim fails
// you should verify sub and jti by your own logic

/* script output:
encrypted eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2RpZGF1dGgubWVldC1tYXJ0aW4uY29tIiwic3ViIjoiZGlkOmtleTphc2dhc2dhcyIsImF1ZCI6Imh0dHBzOi8vZGlkYXV0aC5tZWV0LW1hcnRpbi5jb20iLCJleHAiOjE2MzA5ODQ5NjksIm5iZiI6MTYzMDk4MTM2OSwianRpIjoiYXNmYXNnc2FkZyIsImlhdCI6MTYzMDk4MTM2OX0.AVzU4urVb4ibEmblnrohDHRvIQmYSx-Hzc2xixqXtmlDm34I8CSLJ-lgS8ORAFZFV0xnr7JIeeXuQIjRs-Pte5fyAQjzlds0C_PjUFbWSnyhsG6zQWUJVZknlTuqR69Q_SuMAW3sknaR8E2pEZZYsoc4Jvlar85h-24w-3Aj1_uKIxIj
decrypted {
  iss: 'https://domain.tld',
  sub: 'martin@mail.tld',
  aud: 'https://domain.tld',
  exp: 1630984969,
  nbf: 1630981369,
  jti: 'asfasgsadg',
  iat: 1630981369
}
*/

Related links:

  1. https://cloud.google.com/go/getting-started/session-handling-with-firestore
  2. https://crypto.stackexchange.com/questions/60383/what-is-the-difference-between-ecdsa-and-eddsa
  3. https://www.npmjs.com/package/express-session
  4. https://medium.com/better-programming/jwt-ultimate-how-to-guide-with-best-practices-in-javascript-f7ba4c48dfbd
  5. https://redis.com/solutions/use-cases/session-management/
  6. https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
  7. https://aws.amazon.com/caching/session-management/
  8. https://www.npmjs.com/package/jsonwebtoken
  9. https://www.npmjs.com/package/axios

Jose Example Library in Node.js

Tags: javascript, jose, keys, node.js

A better Node.js library for JWT is jose. It is a bit more complicated to use but it supports the use of JWK, or JWE, as well as signing with EdDSA, so I would recommend it to you as an option. This is an example of using the library with EdDSA and importing PEM private and public keys (also notice how short the EdDSA keys are even though offer better protection).

import { SignJWT, jwtVerify, importSPKI, importPKCS8 } from 'jose';

const privateKey =
`-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIMpFEKC3T8wWYu2e+63MuicRSt4ddWXGIZFXw4vnk+aL
-----END PRIVATE KEY-----`;

const publicKey =
`-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA+L7HHlAU8Zviz0MCX4VSY1xRnX0UTSwb2bQPF6Oqh0g=
-----END PUBLIC KEY-----`;

const data = {
    iss: 'https://domain.tld',
    sub: 'martin@mail.tld',
    aud: 'https://domain.tld',
    exp: Math.floor(Date.now() / 1000) + (60 * 60), // 1 hour
    nbf: Math.floor(Date.now() / 1000),
    jti: 'asfasgsadg',
    data: { some: 'information' }
};

(async () => {
  const importedPrivateKey = await importPKCS8(privateKey, 'EdDSA');
  const jwt = await new SignJWT(data).setProtectedHeader({ alg: 'EdDSA' }).sign(importedPrivateKey);
  console.log('JWT', jwt);

  const importedPublicKey = await importSPKI(publicKey, 'EdDSA');
  const { payload, protectedHeader } = await jwtVerify(jwt, importedPublicKey, {
    issuer: 'https://domain.tld',
    audience: 'https://domain.tld'
  });
  console.log('protected header', protectedHeader);
  console.log('jwt payload', payload);
})();

Related links:

  1. https://cloud.google.com/go/getting-started/session-handling-with-firestore
  2. https://crypto.stackexchange.com/questions/60383/what-is-the-difference-between-ecdsa-and-eddsa
  3. https://www.npmjs.com/package/express-session
  4. https://medium.com/better-programming/jwt-ultimate-how-to-guide-with-best-practices-in-javascript-f7ba4c48dfbd
  5. https://redis.com/solutions/use-cases/session-management/
  6. https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
  7. https://aws.amazon.com/caching/session-management/
  8. https://www.npmjs.com/package/jsonwebtoken
  9. https://www.npmjs.com/package/axios

Secure Cookie Implementation in Node.js

Tags: javascript, node.js, express, passport.js

A cookie with the Secure attribute is sent to the server only with an encrypted request over the HTTPS protocol (however, on localhost only, you can still use HTTP). This is an example implementation for Express in Nodej.js.

const express = require('express');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');

// csrf protection
const csrf = require('csurf');
// session management using cookies
const session = require('express-session');

var csrfProtection = csrf({ cookie: true });
var parseForm = bodyParser.urlencoded({ extended: false });
  
var app = express();
  
app.use(cookieParser());

app.use(session({
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: true,
  cookie: {
    secure: true,
    httpOnly: true  // httpOnly is true by default
  }
}));
  
app.get('/page', csrfProtection, function (req, res) {
  // pass the csrfToken to the page
  res.render('page', { csrfToken: req.csrfToken() });
});
  
app.post('/process', parseForm,
      csrfProtection, function (req, res) {
  res.send('Successfully Validated!!');
});
  
app.listen(3000, err => {
   if (err) {
     console.log(err);
   }
   console.log('Server running on http://localhost:3000.');
});

Related links:

  1. https://cloud.google.com/go/getting-started/session-handling-with-firestore
  2. https://crypto.stackexchange.com/questions/60383/what-is-the-difference-between-ecdsa-and-eddsa
  3. https://www.npmjs.com/package/express-session
  4. https://medium.com/better-programming/jwt-ultimate-how-to-guide-with-best-practices-in-javascript-f7ba4c48dfbd
  5. https://redis.com/solutions/use-cases/session-management/
  6. https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
  7. https://aws.amazon.com/caching/session-management/
  8. https://www.npmjs.com/package/jsonwebtoken
  9. https://www.npmjs.com/package/axios

Session Storage in JWT Implementation

Tags: javascript,  session, javascript, jwt

The difference between these two is that local storage is more permanent. Session storage is cleared when the user closes the website window. Local storage data have to be explicitly deleted. Unlike cookies, local storage is sandboxed to a specific domain and its data cannot be accessed by any other domain including sub-domains. But remember that you are still vulnerable to Cross-Site Scripting (XSS). Both cookie and web storage solutions are vulnerable to XSS. Local storage is used the most in JWT implementations. However, session storage is the more secure option here.

const myJWT = 'eyJhbGciOfJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkaWWiOiJka…afyMH0.WrIEBW5LNLjfGWqIA4XKsyIiuWzbIIpNadfZVkmA6hPs';

// store JWT in session storage
sessionStorage.setItem('JWT', myJWT);

console.log('My JWT is', sessionStorage.getItem('JWT'));

// remove JWT from storage
sessionStorage.removeItem('JWT');

// clear whole storage
sessionStorage.clear();

// localStorage uses the same API
// example: localStorage.setItem('JWT', myJWT);

Related links:

  1. https://cloud.google.com/go/getting-started/session-handling-with-firestore
  2. https://crypto.stackexchange.com/questions/60383/what-is-the-difference-between-ecdsa-and-eddsa
  3. https://www.npmjs.com/package/express-session
  4. https://medium.com/better-programming/jwt-ultimate-how-to-guide-with-best-practices-in-javascript-f7ba4c48dfbd
  5. https://redis.com/solutions/use-cases/session-management/
  6. https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
  7. https://aws.amazon.com/caching/session-management/
  8. https://www.npmjs.com/package/jsonwebtoken
  9. https://www.npmjs.com/package/axios

Generating RSA Public and Private Keys via openssl

Tags: openssl, shell, batchfile

When using jsonwebtoken node library, your best option is ES512, which is Elliptic Curve Digital Signature Algorithm (ECDSA) using a P-521 curve and SHA-512 hash algorithm. ECDSA is also used by bitcoin. ECDSA is another asymmetric encryption like RSA and it is considered to be the more secure option.

# private key
openssl genpkey -algorithm rsa -out rsa-private.pem
# public key
openssl pkey -in rsa-private.pem -pubout -out rsa-public.pem

Related links:

  1. https://cloud.google.com/go/getting-started/session-handling-with-firestore
  2. https://crypto.stackexchange.com/questions/60383/what-is-the-difference-between-ecdsa-and-eddsa
  3. https://www.npmjs.com/package/express-session
  4. https://medium.com/better-programming/jwt-ultimate-how-to-guide-with-best-practices-in-javascript-f7ba4c48dfbd
  5. https://redis.com/solutions/use-cases/session-management/
  6. https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
  7. https://aws.amazon.com/caching/session-management/
  8. https://www.npmjs.com/package/jsonwebtoken
  9. https://www.npmjs.com/package/axios

Generating ECDSA ES512 Public and Private Keys via openssl

Tags: batchfile, shell, openssl

The approach here is the same as with RSA. Use your private key to sign the token as part of authentication and use the public key in your services.

# private key
openssl ecparam -genkey -name secp521r1 -noout -out es512-private.pem
# public key
openssl ec -in ecdsa-p521-private.pem -pubout -out es512-public.pem

Related links:

  1. https://cloud.google.com/go/getting-started/session-handling-with-firestore
  2. https://crypto.stackexchange.com/questions/60383/what-is-the-difference-between-ecdsa-and-eddsa
  3. https://www.npmjs.com/package/express-session
  4. https://medium.com/better-programming/jwt-ultimate-how-to-guide-with-best-practices-in-javascript-f7ba4c48dfbd
  5. https://redis.com/solutions/use-cases/session-management/
  6. https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
  7. https://aws.amazon.com/caching/session-management/
  8. https://www.npmjs.com/package/jsonwebtoken
  9. https://www.npmjs.com/package/axios=

EdDSA ed25519 Public and Private Keys by openssl

Tags: batchfile, shell, openssl

EdDSA, as you can guess, is an asymmetric algorithm that uses public and private keys so its use is the same as with RSA or ECDSA. EdDSA has better performance and even shorter keys than ECDSA while providing better security. You can generate your EdDSA ed25519 public and private keys by openssl.

# private key
openssl genpkey -algorithm ed25519 -out eddsa-private.pem
# public key
openssl pkey -in eddsa-private.pem -pubout -out eddsa-public.pem

Related links:

  1. https://cloud.google.com/go/getting-started/session-handling-with-firestore
  2. https://crypto.stackexchange.com/questions/60383/what-is-the-difference-between-ecdsa-and-eddsa
  3. https://www.npmjs.com/package/express-session
  4. https://medium.com/better-programming/jwt-ultimate-how-to-guide-with-best-practices-in-javascript-f7ba4c48dfbd
  5. https://redis.com/solutions/use-cases/session-management/
  6. https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
  7. https://aws.amazon.com/caching/session-management/
  8. https://www.npmjs.com/package/jsonwebtoken
  9. https://www.npmjs.com/package/axios