How to create a ES256 Token

node v8.16.0
version: 5.0.2
console.log('hello world')
Wouldn't it be nice to have an alternative authentication flow between untrusted parties and not have to manage secrets and keys for multiple issuers and multiple audiences? Modern cryptography algorithms such as ES256 provide fast and secure token transmission. Objectives * Learn how to secure payloads between two untrusted parties * Understand JSON Web Tokens * Understand Authentication Flow between an issuer and an audience * Understand the importance of the ES256 algorithm * Understand the role of the JSON Web Keyset in the authentication flow Scope This document walks through a process of creating and verifying JsonWebTokens using ES256. JsonWebTokens help secure communication between two systems, such as browser to server, or server to server. Signing a token allows the receiver to trust the sender or "issuer". You can sign a token with several different algorithms. This process provides a stateless approach to authentication. HS256 is an algorithm which requires sharing a secret phrase. Both sides must know the secret out of band. RS256 algorithm requres a rsa private key to sign the JsonWebToken and a public rsa key to verify the JsonWebToken. We are going to use the ES256 algorithm which requires an ECDSA private key and a public key to verify the JsonWebToken. ECDSA cryptography is as strong as RSA cryptography yet smaller and faster. For details see: First, let's generate a keypair. A key pair contains a public and private key. The private key is used to sign the token. By signing the token you are able to prove to the audience that you are the issuer of the token. > If you remember anything from reading this, remember to keep the private key in a > secure place. DO NOT SHARE ON VERSION CONTROL. DO NOT STORE IN YOUR SOURCE CODE WHERE IT CAN BE EXPOSED ON THE CLIENT SUCH AS THE WEB BROWSER. The public key is used to verify the token and can be provided to the public.
// modules const { merge, omit, compose } = require("ramda"); const uuid = require("uuid/v4"); var jwk = require("jwk-lite"); var jwk2pem = require("jwk-to-pem"); const kid = uuid() /* generate ES256 Key Pair A key pair contains a private and public key. Using the ECDSA algorithm the jwk.generateKey function generates the keys and returns them via a promise */ const keys = await jwk.generateKey("ES256"); /* create json jwk docs In order to use these keys, we need to export them to a js object and modify the shape of the object to include some meta data to describe the keys. */ const privateJwk = convert(await jwk.exportKey(keys.privateKey)) const publicJwk = convert(await jwk.exportKey(keys.publicKey)) /* Using the jwk2pem library we can convert the key to a pem file, which our Jwt module prefers. You should be able to see the pem in the output below for the privateKey */ const pem = jwk2pem(privateJwk, { private: true }) // helper functions /* JWT Libraries ( provide sign and verify functionality using JsonWebKeys (JWK) In order for the JsonWebKeys (JWK) documents to be usuable by the JWT Libraries, convert the returned js object by appending the kid, alg, and use nodes for each key. */ function convert (k) { const core = { kid, alg: "ES256", use: "sig" }; const unneededKeys = ["ext", "key_ops"]; return compose(merge(core), omit(unneededKeys))(k) } // Show the private key pem
Now that we have a private key, let's sign our token. The token will consist of a header and a payload. The header and payload should not contain secrets. The payload has some common claims or nodes that will help define: * iss - Who issued the token * aud - Who should be receiving the token * exp - DateTime that the token will expire - UnixTime * iat - DateTime that the token was issued - UnixTime * jti - JSON token identifier - unique identifier * sub - Subject of the token The payload may have custom nodes that should be considered non secret information: * tenant - custom claim to identify a tenant for this request that may be used for whitelisting The header has specific claims or nodes required that helps the receiver of the token to validate the token. * alg - algorithm * typ - token type * kid - key identifier * jku - url to the public key
const jwtPayload = { iss: "", aud: "", exp: Math.round( / 1000 + 300), iat: Math.round( / 1000), jti: uuid(), tenant: "mytenant" } const jwtHeader = { alg: "ES256", typ: "JWT", kid, jku: "" } JSON.stringify(jwtPayload, null, 2);
Now that we have the payload, header, and private key, we are ready to sign and create a JsonWebToken. Let's use the ES256 algorithm to securely sign the token.
const jwt = require('jsonwebtoken') const token = jwt.sign(jwtPayload, pem, { header: jwtHeader})
Now that we have our token, we are ready send it to our audience. The audience in return will need to verify the token. To verify the token the audience will use the public key, which can be access within a publically available JSON Web Keyset resource served from an url endpoint such as {base_url}/.well-known/jwks.json. Below is an example of how you might serve the public key in a JSON Web Key Set in an express server.
const express = require('express') const app = express() /* This endpoint can be included in the jwt header so that the service can locate the public key to validate the token. */ app.get('/.well-known/jwks.json', (req, res) => { res.send([publicJwk]) }) /* This is a simple endpoint to show a link to the jwks public key file */ app.get('/', (req, res) => res.send(` <!doctype html> <html> <head> <title>ES256 Token Example</title> </head> <body> <h1>ES256 Token Example</h1> <a href="/.well-known/jwks.json">Click here to see jwks.json</a> </body> </html> `)) app.listen(3000)
Or you can use and the public key to validate the token.
` <div style="height: 200px"> <a href="${token}" target="_blank">Open JWT.IO</a> </div> `
const publicPem = jwk2pem(publicJwk)

no comments

    sign in to comment