Skip to content

Instantly share code, notes, and snippets.

@canterberry
Created July 21, 2022 22:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save canterberry/36c60b89c4ead378587bb06fe7239e81 to your computer and use it in GitHub Desktop.
Save canterberry/36c60b89c4ead378587bb06fe7239e81 to your computer and use it in GitHub Desktop.
TWJ is JWT for Node.js apps. Here's how to use it.
import { createSigner, createValidator, createVerifier } from 'twj/v0';
import { generateKeyPairSync } from 'node:crypto';
/**
* Generate a key-pair, if you don't already have one, as a JWK object.
* Here's how you can do that using native APIs provided by Node.js
* In production scenarios, you'll already have this somewhere under
* configuration management alongside your other secrets.
*/
const { privateKey, publicKey } = generateKeyPairSync('ec', {
namedCurve: 'P-256',
privateKeyEncoding: { format: 'jwk' },
publicKeyEncoding: { format: 'jwk' }
});
/**
* NOTE: The keys we generated above do not have a `kid` property by default,
* so now would be a good time to generate one and sprinkle that in.
* Obviously, you'll want to do something more clever than what I've shown below.
* One idea is to hash the public key in JSON string format and use the hex encoding as the kid.
*/
privateKey.kid = publicKey.kid = 'the-best-key-i-ever-did-make';
/**
* The issuer will `sign` a set of JWT claims, using its private key, to produce a signed JWT token.
* The audience will `verify` that token using the issuer's public key, to extract the original JWT claims.
* Here, we initialize both of those functions.
* In this example, we'll play the role of both issuer and audience, so we can show how to initialize and use both.
*/
const sign = createSigner({ key: privateKey });
const verify = createVerifier({ key: publicKey });
/**
* After a token is successfully verified, we still need to `validate` the JWT claims in that token.
* Here, we initialize that function with the configuration we'd like to use.
* Commented-out are some example options you can provide.
* If you provide no configuration options, only temporal claims (like exp and iat) are validated, using the system clock and a default clock tolerance of 30 seconds.
*/
const validate = createValidator({
// audiences: ['https://audience.example.com'],
// issuers: ['https://issuer.example.com'],
// clockToleranceSeconds: 30,
// currentTimeSeconds: () => Math.floor(new Date().getTime() / 1000)
});
/**
* Alright, let's get to work!
* Here, we sign a simple set of claims including only a subject.
* This is where you'd sprinkle in any standard JWT claims you like,
* or add your own.
* Some ideas for claims you probably want to include: aud, iss, exp, iat, sub, scope
*/
const token = await sign({
sub: 'alice@example.com'
});
// Let's have a look at the token!
console.log(token);
/*
* Now let's pretend we have received that token from a client (say, via an HTTP Authorization header)
* Here's how we extract the claims from it using the verifier we initialized earlier.
*/
const claims = await verify(token);
// Let's have a look at those claims!
console.log(claims);
/**
* Now, let's validate those claims.
* Here is where we'll enforce some standard rules, per the validator's configuration.
* You're welcome to do any additional validation you'd like, or forego TWJ's validator
* altogether if you want to just do your own.
*/
await validate(claims);
// Finally, let's just log a note that we're done.
console.log('OK');
/**
* Congratulations! That's all there is to it!
* JWT isn't that hard, after all.
* If you're feeling good about that and thought it was interesting,
* you might want to explore more cryptography.
* The whole sign/verify bit is a common concept in "public key cryptography",
* also known as PKI. If that's new to you, and you want to learn more, I
* advise you to check out PGP (aka: GPG). That's another very common application
* of public/private key-pairs to sign and verify things.
* From there, check out Diffie-Hellman key exchange (aka: DH key exchange).
* That's how you can turn a public/private key-pair into a full-on encrypted
* communication channel, which is the underlying tech for a lot of secure tools
* we use everywhere, like TLS/HTTPS and SSH. Whoa!
*/
{
"private": true,
"main": "main.mjs",
"module": "main.mjs",
"type": "module",
"scripts": {
"test": "node main.mjs"
},
"dependencies": {
"twj": "^0.1.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment