node-red-contrib-jwt 0.1.0
JWT Validator and Signer
node-red-contrib-jwt
Node-red node for sign and validate JWT tokens.
Functions
- Sign msg.payload, msg.topic or msg.token using HS* or RS* algorithms.
- Validate msg.payload, msg.topic, msg.token, req.headers.authorization or access_token var using HS* or RS* algorithms.
Roadmap
- Handle certs better;
- Validations;
- Better UI.
Sample/Demo Flows
PEM Key File
This flow starts with injecting an RSA Private Key and Public Key each to a file (within node-red).
The Sign and Validate node will trigger a JWT claim to be signed using the Private key above (debugging the signed token), and then validating it with the matching Public key (debugging the validated JWT payload)
Flow JSON:
[{"id":"5a455541.3fdcac","type":"tab","label":"PEM Key","disabled":false,"info":""},{"id":"254d3459.0f6a6c","type":"inject","z":"5a455541.3fdcac","name":"Sign and Validate","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":159,"y":213,"wires":[["b41b1ce9.4af46"]]},{"id":"952eeded.4e3b8","type":"jwt sign","z":"5a455541.3fdcac","name":"","alg":"RS512","exp":"3601","jwkurl":"","jwkkid":"rsa_priv_key","secret":"","key":"rsa_priv_key","signvar":"payload","storetoken":"payload","x":558,"y":212,"wires":[["923d5346.7095a","d6f89849.4d8f88"]]},{"id":"923d5346.7095a","type":"debug","z":"5a455541.3fdcac","name":"Debug Signed JWT","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":780,"y":212,"wires":[]},{"id":"d6f89849.4d8f88","type":"jwt verify","z":"5a455541.3fdcac","name":"","alg":["RS512"],"jwkurl":"","secret":"","key":"rsa_pub_key","signvar":"payload","storetoken":"payload","x":554,"y":318,"wires":[["c4e176.85833e88"]]},{"id":"c4e176.85833e88","type":"debug","z":"5a455541.3fdcac","name":"Debug Validated JWT","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":784,"y":318,"wires":[]},{"id":"b41b1ce9.4af46","type":"template","z":"5a455541.3fdcac","name":"Create Claims","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"sub\":\"user\",\"iss\":\"http://nodered.nulli.com:1880/oauth2\",\"tokenName\":\"id_token\",\"aud\":\"client\",\"azp\":\"client\",\"tokenType\":\"JWTToken\"}","output":"json","x":352,"y":213,"wires":[["952eeded.4e3b8"]]},{"id":"4bda0708.c98b08","type":"file","z":"5a455541.3fdcac","name":"","filename":"rsa_priv_key","appendNewline":false,"createDir":false,"overwriteFile":"true","x":707,"y":82,"wires":[]},{"id":"27b80fdd.cf946","type":"inject","z":"5a455541.3fdcac","name":"","topic":"Create Key Files","payload":"","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":166,"y":84,"wires":[["394036c2.6c15ea","48b5f522.c8aa7c"]]},{"id":"394036c2.6c15ea","type":"template","z":"5a455541.3fdcac","name":"RSA Private Key","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA2+d7uinqCDZYnZpyqCGnoeAR+aCWDC0YI7ZqKzH2RyjcHOpI\nIoswrPRZgF56y1x87a/QatPZmNmctEeNSNOM1qHfZSyxHCEyqENiNS3PEzUJ9RHK\nB7eDHKZGeQYhOmtXqm3zB8ORtVrngvDBsZT9A686p7Zo21ESOlXFZBieRVyimrEe\nJqcSwKq4gYG8GU7lc7r1fZCi81KL5K/6dxdMrmQeMYXgetH5SPIBFo4Mq47oJqiV\n48T87zCFHEzEikC+f1bbsNmOfryAwZtzTHQ7i/AU6kpOiNRMizC/cP7SNodc01Mk\nE1IXAv4fXTvEcmxTyRWPp2kiPR5oZ1R76B6v3wIDAQABAoIBAG1etQ/DFu3csbg4\n/7uVeX0uJ2KDNv15VB8285jmMXn03XzYHOZpWd07bmRFGUeZiVzh64OmN/wohyK2\nz5Mf/FUnjycP0kqOTFaZpPeUAACqiXhEz7mE0QXnwPciUFZw2tod8xiycP7E5o4e\nVgePdUNm3IJbW81wheNTk2gUQmt2Giwiu8ilcvPXyvNKBRLw+XM7UXINYtBk/7xM\ntcYd19mmjumUUXK4cw4qhoNE3l+GfCJ8S+FjCazLFCFnAe2CKX3CeJv7paoxenit\niQ6lQk1nhdN4+T5CnrTao1P9nBZiBqtvOICRDc+9QgMKHmmuPzPeYuqmGPd6F8s7\nMIQ4xvECgYEA8lxCg5sH4MUZbNFSn3fkP/iisiRzth4TXHv9OmdcRdTRSJrWXxND\nWbTb+Alj5vP4mBgxMTcycPS/qLSInQJaXjxEF4aQlBNLj4iZRvZENkseKrju+MyO\nJGOmcnkzfFiZLb/TLFMhBBpfVPItZci1kBE8/Mz6b9S1PmZnBWjdJfkCgYEA6Eex\nTtySIyLq/VQcuw+fgCI0lkZ0Ctg/nbs1YHbfMcyih60bWRsDX9KvexWy60nUQHNu\n/pc73rSW8ggIfTqx2UTHJTiNwBLm/XE2HxPnzcB6zf9CBhgqoQPxgBWfxtaiGCGR\nMyrsNxXt49LMUn2p6JNuLXtaKWweDfJbziLiGpcCgYEAkQFIPouENCRZKPU4lPbb\nlrbBSQpsPNtTxDLe+JGc8J77NJCHkEzrMAH41jdwEV+JLh7TV9npkRGullaloiMl\nRFqUdurpF99Phyo4Yu+3MjDRdMg/GpbwuK7yiG0hs9UimO+7fjqid1z8Csv3A34J\nu4/EyJAvirdeVV5ZZzAtmKkCgYEAmzIrmdFuIuPVtmVYvPN3UpmIIU/5gLVz6+OF\nimpiPlfH0Hcs6qBN5NxUOx0IaoTtuBJoetp5OhfXMueDJIjvA4gctxXOQCNhCr11\nt9YI70RHyRNf/cpv0R1dYoMQOj52F0F2ToXDRhr001+o/QTRXYAEAYeFel+uxQrF\npDlScQkCgYA7JewsTmXZyCfUytL9r+SxNjZLicmw2JALGBwavY5rZtSFKsmPr/zV\nDwjP9gtV6T/ObEshwDonI0tsDq6KOJB/8AI97uwcPa1c8x0jnfvRe9uoZ/qiKA+X\nqwqVkk/b6KowQo/XFMFa05batx2Xs41m5pQ/sSQPuR0FWHFVmKTF8g==\n-----END RSA PRIVATE KEY-----\n","output":"str","x":480,"y":83,"wires":[["4bda0708.c98b08"]]},{"id":"fd70bdee.e7ebd","type":"file","z":"5a455541.3fdcac","name":"","filename":"rsa_pub_key","appendNewline":false,"createDir":false,"overwriteFile":"true","x":706,"y":131,"wires":[]},{"id":"48b5f522.c8aa7c","type":"template","z":"5a455541.3fdcac","name":"RSA Public Key","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2+d7uinqCDZYnZpyqCGn\noeAR+aCWDC0YI7ZqKzH2RyjcHOpIIoswrPRZgF56y1x87a/QatPZmNmctEeNSNOM\n1qHfZSyxHCEyqENiNS3PEzUJ9RHKB7eDHKZGeQYhOmtXqm3zB8ORtVrngvDBsZT9\nA686p7Zo21ESOlXFZBieRVyimrEeJqcSwKq4gYG8GU7lc7r1fZCi81KL5K/6dxdM\nrmQeMYXgetH5SPIBFo4Mq47oJqiV48T87zCFHEzEikC+f1bbsNmOfryAwZtzTHQ7\ni/AU6kpOiNRMizC/cP7SNodc01MkE1IXAv4fXTvEcmxTyRWPp2kiPR5oZ1R76B6v\n3wIDAQAB\n-----END PUBLIC KEY-----\n","output":"str","x":479,"y":132,"wires":[["fd70bdee.e7ebd"]]}]
JWK URL Keyset
The Sign and Validate node will trigger a JWT claim to be signed using an Private EC Key retrieved from a URL hosted by node-red (debugging the signed token), and then validating it with the matching Public EC key (debugging the validated JWT payload)
Flow JSON:
[{"id":"2bb941b5.7b5d9e","type":"tab","label":"Test JWT","disabled":false,"info":""},{"id":"920519b5.2b6e38","type":"inject","z":"2bb941b5.7b5d9e","name":"Sign and Validate","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":147.5,"y":214,"wires":[["60e22738.f40298"]]},{"id":"5138b19b.cd216","type":"jwt sign","z":"2bb941b5.7b5d9e","name":"","alg":"ES512","exp":"3601","jwkurl":"http://localhost:1880/priv_jwk","jwkkid":"nulli-ec-512","secret":"","key":"","signvar":"payload","storetoken":"payload","x":552.5,"y":213,"wires":[["6e7758b9.2b6f68","f900de63.1632b"]]},{"id":"6e7758b9.2b6f68","type":"debug","z":"2bb941b5.7b5d9e","name":"Debug Signed JWT","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":774.5,"y":213,"wires":[]},{"id":"f900de63.1632b","type":"jwt verify","z":"2bb941b5.7b5d9e","name":"","alg":["ES512"],"jwkurl":"http://localhost:1880/pub_jwk","secret":"","key":"","signvar":"payload","storetoken":"payload","x":548.5,"y":319,"wires":[["f05412ad.d780a"]]},{"id":"f05412ad.d780a","type":"debug","z":"2bb941b5.7b5d9e","name":"Debug Validated JWT","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":778.5,"y":319,"wires":[]},{"id":"4ba13e9e.f47c9","type":"template","z":"2bb941b5.7b5d9e","name":"Private JWK","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n \"keys\": [\n {\n \"kty\": \"EC\",\n \"d\": \"AXLoWyI1nApmP8lD1iFo3qjwBjJluXwNdYizbrcXpgUoX6F0gTtsOEQD9AxTqElgwHDfbs_IcwOSjfCh_4QeJnUM\",\n \"use\": \"sig\",\n \"crv\": \"P-521\",\n \"kid\": \"nulli-ec-512\",\n \"x\": \"AD0pM1Vkdw12HeM5AeY6PMuMbc66uG8oRiKV9hnm2x8U_dLgA7gRYNim4Y8_rixOeoHYlMzarRtDThLBS4CBlgrG\",\n \"y\": \"ABuSEiwuSTIy02f1G2OnuSQN-9-s4KJxxXSqgMonKv3CPWaBVTJqfB4Viz9xUoY16d6ngJ2o66Kp2FUetTmxSptq\",\n \"alg\": \"ES512\"\n }\n ]\n}","output":"json","x":339.5,"y":40,"wires":[["20446d4f.225862"]]},{"id":"20446d4f.225862","type":"http response","z":"2bb941b5.7b5d9e","name":"","statusCode":"200","headers":{},"x":535.5,"y":40,"wires":[]},{"id":"5790c71a.0ca028","type":"http in","z":"2bb941b5.7b5d9e","name":"","url":"/priv_jwk","method":"get","upload":false,"swaggerDoc":"","x":143.5,"y":41,"wires":[["4ba13e9e.f47c9"]]},{"id":"60e22738.f40298","type":"template","z":"2bb941b5.7b5d9e","name":"Create Claims","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"sub\":\"user\",\"iss\":\"http://nodered.nulli.com:1880/oauth2\",\"tokenName\":\"id_token\",\"aud\":\"client\",\"azp\":\"client\",\"tokenType\":\"JWTToken\"}","output":"json","x":346.5,"y":214,"wires":[["5138b19b.cd216"]]},{"id":"640d2eb5.4dec3","type":"template","z":"2bb941b5.7b5d9e","name":"Public JWK","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n \"keys\": [\n {\n \"kty\": \"EC\",\n \"use\": \"sig\",\n \"crv\": \"P-521\",\n \"kid\": \"nulli-ec-512\",\n \"x\": \"AD0pM1Vkdw12HeM5AeY6PMuMbc66uG8oRiKV9hnm2x8U_dLgA7gRYNim4Y8_rixOeoHYlMzarRtDThLBS4CBlgrG\",\n \"y\": \"ABuSEiwuSTIy02f1G2OnuSQN-9-s4KJxxXSqgMonKv3CPWaBVTJqfB4Viz9xUoY16d6ngJ2o66Kp2FUetTmxSptq\",\n \"alg\": \"ES512\"\n }\n ]\n}","output":"json","x":342,"y":130,"wires":[["a03b5868.ec3528"]]},{"id":"a03b5868.ec3528","type":"http response","z":"2bb941b5.7b5d9e","name":"","statusCode":"200","headers":{},"x":538,"y":130,"wires":[]},{"id":"e53cbf9a.80f7f","type":"http in","z":"2bb941b5.7b5d9e","name":"","url":"/pub_jwk","method":"get","upload":false,"swaggerDoc":"","x":146,"y":131,"wires":[["640d2eb5.4dec3"]]}]
Useful sites:
- https://mkjwk.org/ - Create JWK keysets
- https://jwt.io/ - Debug JWT tokens