@connectedcars/jwtutils
Advanced tools
Comparing version 1.0.4 to 1.0.5
{ | ||
"name": "@connectedcars/jwtutils", | ||
"version": "1.0.4", | ||
"version": "1.0.5", | ||
"description": "Zero dependency JWT encoding/decoding for Node", | ||
@@ -5,0 +5,0 @@ "main": "src/index.js", |
@@ -8,10 +8,35 @@ # node-jwtutils | ||
This module only supports asymmetric encryption algorithms such as RS256, | ||
RS384, RS512, ES256, ES384 and ES512. It currently does not implement symmetric | ||
encryption as this is a really bad idea for any production use. | ||
Features: | ||
## Usage | ||
* Encode and decode any RS256, RS384, RS512, ES256, ES384 and ES512 signed tokens | ||
* Support for multiple issuers and keys per issuer. | ||
* Express middleware to validate JWT's | ||
## Background | ||
Most other JWT implementations tend be complex and have a large array of | ||
dependencies because they implement the seldom used JOSE standard. | ||
This often makes the whole JWT encoding/decoding complicated and hard to | ||
understand, something it really should not be. | ||
So the focus of this module has been to make a simple, secure, fast and | ||
flexible set of utility methods to work with JWT's. The code itself is also | ||
easy to understand and less than 200 lines of code, making it much easier to | ||
security audit the code. | ||
Also note doing your own crypto is a bad idea so this module only deals with | ||
the encoding/decoding of the JWT, the underlaying crypto operations are done | ||
by Node's build-in crypto api that uses openssl. | ||
Currently only asymmetric encryption algorithms are supported as this would | ||
also be the only recommend option for production use. | ||
## Samples | ||
* [Integrates with Google Identity Platform](sample/googleoauth2v2/README.md) | ||
## Basic usage | ||
``` javascript | ||
const jwtUtils = require('jwtutils') | ||
const { JwtUtils, JwtVerifyError } = require('@connectedcars/jwtutils') | ||
@@ -51,4 +76,4 @@ let jwtHeader = { | ||
// let jwt = jwtUtils.encode(pemEncodedPrivateKey, jwtHeader, jwtBody, privateKeyPassword) | ||
let jwt = jwtUtils.encode(pemEncodedPrivateKey, jwtHeader, jwtBody) | ||
// let jwt = JwtUtils.encode(pemEncodedPrivateKey, jwtHeader, jwtBody, privateKeyPassword) | ||
let jwt = JwtUtils.encode(pemEncodedPrivateKey, jwtHeader, jwtBody) | ||
@@ -74,3 +99,3 @@ // Don't use this key for anything but testing as this is the key from jwt.io | ||
try { | ||
let decodedJwtBody = jwtUtils.decode(jwt, pubKeys, allowedAudinces) | ||
let decodedJwtBody = JwtUtils.decode(jwt, pubKeys, allowedAudinces) | ||
} catch (e) { | ||
@@ -86,7 +111,7 @@ if (e instanceof JwtVerifyError) { | ||
## Express authentication middleware | ||
## Usage of express middleware | ||
``` javascript | ||
const express = require('express') | ||
const jwtAuthMiddleware = require('./jwtauthmiddleware') | ||
const { JwtAuthMiddleware, JwtVerifyError } = require('@connectedcars/jwtutils') | ||
@@ -104,3 +129,3 @@ // Configuration | ||
// Register the middleware | ||
app.use(jwtAuthMiddleware(pubKeys, audiences)) | ||
app.use(JwtAuthMiddleware(pubKeys, audiences)) | ||
@@ -107,0 +132,0 @@ // Register an error handler to return 401 errors |
@@ -7,4 +7,3 @@ 'use strict' | ||
const jwtAuthMiddleware = require('../../src/jwtauthmiddleware') | ||
const JwtVerifyError = require('../../src/jwtverifyerror') | ||
const { JwtAuthMiddleware, JwtVerifyError } = require('../../src/.') | ||
@@ -14,20 +13,10 @@ const request = require('request') | ||
const audiences = [ | ||
'807025168921-ti2uj07r2iammimbneq706at7497gtto.apps.googleusercontent.com' | ||
] | ||
if (process.argv.length <= 2) { | ||
console.error('node index.js "google-oauth-cclientid"') | ||
process.exit(255) | ||
} | ||
const rsaPublicKey = | ||
'-----BEGIN PUBLIC KEY-----\n' + | ||
'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugd\n' + | ||
'UWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQs\n' + | ||
'HUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5D\n' + | ||
'o2kQ+X5xK9cipRgEKwIDAQAB\n' + | ||
'-----END PUBLIC KEY-----' | ||
const audiences = [process.argv[2]] | ||
const pubKeys = {} | ||
const pubKeys = { | ||
'http://localhost:3000/auth': { | ||
'1@RS256': rsaPublicKey | ||
} | ||
} | ||
const app = express() | ||
@@ -38,3 +27,3 @@ app.use('/', express.static(path.join(__dirname, 'public'))) | ||
'/api', | ||
jwtAuthMiddleware(pubKeys, audiences, user => { | ||
JwtAuthMiddleware(pubKeys, audiences, user => { | ||
// Use e-mail as subject for google tokens | ||
@@ -41,0 +30,0 @@ if (user.issuer === 'https://accounts.google.com') { |
@@ -20,6 +20,10 @@ # Sample Express app that integrates with Google Identity Platform | ||
Replace audiences with own client id in index.js | ||
## Run sample application | ||
``` bash | ||
node index.js '<OAuth Client ID>' | ||
``` | ||
## Links | ||
* Scopes: https://developers.google.com/identity/protocols/googlescopes |
@@ -5,6 +5,15 @@ 'use strict' | ||
const jwtDecode = require('./jwtdecode') | ||
const JwtVerifyError = require('./jwtverifyerror') | ||
const JwtAuthMiddleware = require('./jwtauthmiddleware') | ||
module.exports = { | ||
JwtUtils: { | ||
encode: jwtEncode, | ||
decode: jwtDecode | ||
}, | ||
JwtVerifyError: JwtVerifyError, | ||
JwtAuthMiddleware: JwtAuthMiddleware, | ||
// Support old interface | ||
encode: jwtEncode, | ||
decode: jwtDecode | ||
} |
'use strict' | ||
const expect = require('unexpected') | ||
const jwtUtils = require('./index') | ||
const JwtVerifyError = require('./jwtverifyerror') | ||
const { JwtUtils, JwtVerifyError } = require('./index') | ||
const oldJwtUtils = require('./index') | ||
@@ -86,2 +86,9 @@ const rsaPublicKey = | ||
describe('encode/decode', () => { | ||
it('success old inteface', () => { | ||
let jwt = oldJwtUtils.encode(rsaPrivateKey, jwtHeader, jwtBody) | ||
let decodedJwtBody = oldJwtUtils.decode(jwt, pubKeys, [ | ||
'https://host/oauth/token' | ||
]) | ||
expect(jwtBody, 'to equal', decodedJwtBody) | ||
}) | ||
it('success with RSA at RS256, RS384 and RS512', () => { | ||
@@ -91,4 +98,4 @@ for (let algo of ['RS256', 'RS384', 'RS512']) { | ||
customJwtHeader.alg = algo | ||
let jwt = jwtUtils.encode(rsaPrivateKey, customJwtHeader, jwtBody) | ||
let decodedJwtBody = jwtUtils.decode(jwt, pubKeys, [ | ||
let jwt = JwtUtils.encode(rsaPrivateKey, customJwtHeader, jwtBody) | ||
let decodedJwtBody = JwtUtils.decode(jwt, pubKeys, [ | ||
'https://host/oauth/token' | ||
@@ -103,4 +110,4 @@ ]) | ||
customJwtHeader.alg = algo | ||
let jwt = jwtUtils.encode(ecPrivateKey, customJwtHeader, jwtBody) | ||
let decodedJwtBody = jwtUtils.decode(jwt, pubKeys, [ | ||
let jwt = JwtUtils.encode(ecPrivateKey, customJwtHeader, jwtBody) | ||
let decodedJwtBody = JwtUtils.decode(jwt, pubKeys, [ | ||
'https://host/oauth/token' | ||
@@ -114,4 +121,4 @@ ]) | ||
delete customJwtHeader.kid | ||
let jwt = jwtUtils.encode(rsaPrivateKey, customJwtHeader, jwtBody) | ||
let decodedJwtBody = jwtUtils.decode(jwt, pubKeys, [ | ||
let jwt = JwtUtils.encode(rsaPrivateKey, customJwtHeader, jwtBody) | ||
let decodedJwtBody = JwtUtils.decode(jwt, pubKeys, [ | ||
'https://host/oauth/token' | ||
@@ -127,4 +134,4 @@ ]) | ||
] | ||
let jwt = jwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody) | ||
let decodedJwtBody = jwtUtils.decode(jwt, pubKeys, [ | ||
let jwt = JwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody) | ||
let decodedJwtBody = JwtUtils.decode(jwt, pubKeys, [ | ||
'https://host/oauth/token' | ||
@@ -135,6 +142,6 @@ ]) | ||
it('unknown aud', () => { | ||
let jwt = jwtUtils.encode(rsaPrivateKey, jwtHeader, jwtBody) | ||
let jwt = JwtUtils.encode(rsaPrivateKey, jwtHeader, jwtBody) | ||
expect( | ||
() => { | ||
jwtUtils.decode(jwt, pubKeys, ['https://myhost/oauth/token']) | ||
JwtUtils.decode(jwt, pubKeys, ['https://myhost/oauth/token']) | ||
}, | ||
@@ -149,6 +156,6 @@ 'to throw', | ||
customJwtBody.exp -= 800 | ||
let jwt = jwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody) | ||
let jwt = JwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody) | ||
expect( | ||
() => { | ||
jwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
JwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
}, | ||
@@ -162,6 +169,6 @@ 'to throw', | ||
delete customJwtBody.exp | ||
let jwt = jwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody) | ||
let jwt = JwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody) | ||
expect( | ||
() => { | ||
jwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
JwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
}, | ||
@@ -175,6 +182,6 @@ 'to throw', | ||
delete customJwtBody.iss | ||
let jwt = jwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody) | ||
let jwt = JwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody) | ||
expect( | ||
() => { | ||
jwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
JwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
}, | ||
@@ -188,6 +195,6 @@ 'to throw', | ||
customJwtBody.iat += 1200 | ||
let jwt = jwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody) | ||
let jwt = JwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody) | ||
expect( | ||
() => { | ||
jwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
JwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
}, | ||
@@ -201,6 +208,6 @@ 'to throw', | ||
customJwtBody.nbf = customJwtBody.iat + 1200 | ||
let jwt = jwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody) | ||
let jwt = JwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody) | ||
expect( | ||
() => { | ||
jwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
JwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
}, | ||
@@ -214,6 +221,6 @@ 'to throw', | ||
customJwtBody.iss += 'unknown@test.com' | ||
let jwt = jwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody) | ||
let jwt = JwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody) | ||
expect( | ||
() => { | ||
jwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
JwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
}, | ||
@@ -229,3 +236,3 @@ 'to throw', | ||
() => { | ||
jwtUtils.encode(rsaPrivateKey, customJwtHeader, jwtBody) | ||
JwtUtils.encode(rsaPrivateKey, customJwtHeader, jwtBody) | ||
}, | ||
@@ -239,6 +246,6 @@ 'to throw', | ||
customJwtHeader.kid = 3 | ||
let jwt = jwtUtils.encode(rsaPrivateKey, customJwtHeader, jwtBody) | ||
let jwt = JwtUtils.encode(rsaPrivateKey, customJwtHeader, jwtBody) | ||
expect( | ||
() => { | ||
jwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
JwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
}, | ||
@@ -252,6 +259,6 @@ 'to throw', | ||
customJwtHeader.kid = 2 | ||
let jwt = jwtUtils.encode(rsaPrivateKey, customJwtHeader, jwtBody) | ||
let jwt = JwtUtils.encode(rsaPrivateKey, customJwtHeader, jwtBody) | ||
expect( | ||
() => { | ||
jwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
JwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
}, | ||
@@ -265,5 +272,5 @@ 'to throw', | ||
customJwtHeader.kid = 2 | ||
let jwt = jwtUtils.encode(rsaPrivateKey, customJwtHeader, jwtBody) | ||
let jwt = JwtUtils.encode(rsaPrivateKey, customJwtHeader, jwtBody) | ||
try { | ||
jwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
JwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
} catch (e) { | ||
@@ -278,6 +285,6 @@ if (e instanceof JwtVerifyError) { | ||
customJwtHeader.kid = 4 | ||
let jwt = jwtUtils.encode(rsaPrivateKey, customJwtHeader, jwtBody) | ||
let jwt = JwtUtils.encode(rsaPrivateKey, customJwtHeader, jwtBody) | ||
expect( | ||
() => { | ||
jwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
JwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token']) | ||
}, | ||
@@ -284,0 +291,0 @@ 'to throw', |
'use strict' | ||
const jwtUtils = require('./index') | ||
const jwtDecode = require('./jwtdecode') | ||
const JwtVerifyError = require('./jwtverifyerror') | ||
@@ -16,3 +16,3 @@ | ||
let jwt = request.headers.authorization.substring(7) | ||
let decodedJwtBody = jwtUtils.decode(jwt, pubKeys, audiences) | ||
let decodedJwtBody = jwtDecode(jwt, pubKeys, audiences) | ||
if (!decodedJwtBody.sub) { | ||
@@ -19,0 +19,0 @@ return next(new JwtVerifyError(`Missing 'sub' in body`)) |
const express = require('express') | ||
const app = express() | ||
const http = require('http') | ||
const jwtUtils = require('./index') | ||
const jwtAuthMiddleware = require('./jwtauthmiddleware') | ||
const { JwtUtils, JwtAuthMiddleware, JwtVerifyError } = require('./index') | ||
const expect = require('unexpected') | ||
const JwtVerifyError = require('./jwtverifyerror') | ||
@@ -54,3 +52,3 @@ const ecPrivateKey = | ||
'/mapped', | ||
jwtAuthMiddleware(pubKeys, ['http://localhost/'], user => { | ||
JwtAuthMiddleware(pubKeys, ['http://localhost/'], user => { | ||
// Add test e-mail | ||
@@ -60,3 +58,3 @@ user.eMail = 'test@domain.tld' | ||
) | ||
app.use('/', jwtAuthMiddleware(pubKeys, ['http://localhost/'])) | ||
app.use('/', JwtAuthMiddleware(pubKeys, ['http://localhost/'])) | ||
app.use((err, req, res, next) => { | ||
@@ -82,3 +80,3 @@ if (err instanceof JwtVerifyError) { | ||
it('should return ok', () => { | ||
let jwt = jwtUtils.encode(ecPrivateKey, jwtHeader, jwtBody) | ||
let jwt = JwtUtils.encode(ecPrivateKey, jwtHeader, jwtBody) | ||
let responsePromise = doRequest('GET', 'localhost', port, '/', { | ||
@@ -95,3 +93,3 @@ Authorization: 'Bearer ' + jwt, | ||
it('should return ok with a new e-mail', () => { | ||
let jwt = jwtUtils.encode(ecPrivateKey, jwtHeader, jwtBody) | ||
let jwt = JwtUtils.encode(ecPrivateKey, jwtHeader, jwtBody) | ||
let responsePromise = doRequest('GET', 'localhost', port, '/mapped', { | ||
@@ -110,3 +108,3 @@ Authorization: 'Bearer ' + jwt, | ||
delete customJwtBody.sub | ||
let jwt = jwtUtils.encode(ecPrivateKey, jwtHeader, customJwtBody) | ||
let jwt = JwtUtils.encode(ecPrivateKey, jwtHeader, customJwtBody) | ||
let responsePromise = doRequest('GET', 'localhost', port, '/', { | ||
@@ -123,3 +121,3 @@ Authorization: 'Bearer ' + jwt, | ||
it('should fail because of malform JSON', () => { | ||
let jwt = jwtUtils.encode(ecPrivateKey, jwtHeader, jwtBody) | ||
let jwt = JwtUtils.encode(ecPrivateKey, jwtHeader, jwtBody) | ||
let responsePromise = doRequest('GET', 'localhost', port, '/', { | ||
@@ -138,3 +136,3 @@ Authorization: 'Bearer ' + jwt.substr(2), | ||
customJwtHeader.kid = 2 | ||
let jwt = jwtUtils.encode(ecPrivateKey, customJwtHeader, jwtBody) | ||
let jwt = JwtUtils.encode(ecPrivateKey, customJwtHeader, jwtBody) | ||
let responsePromise = doRequest('GET', 'localhost', port, '/', { | ||
@@ -141,0 +139,0 @@ Authorization: 'Bearer ' + jwt, |
'use strict' | ||
const crypto = require('crypto') | ||
const JwtVerifyError = require('./jwtverifyerror.js') | ||
const base64UrlSafe = require('./base64urlsafe') | ||
const JwtVerifyError = require('./jwtverifyerror') | ||
@@ -8,0 +8,0 @@ function jwtDecode(jwt, publicKeys, audiences, nbfIatSkrew = 300) { |
'use strict' | ||
const expect = require('unexpected') | ||
const jwtUtils = require('./index') | ||
const { JwtUtils } = require('./index') | ||
@@ -20,3 +20,3 @@ const pubKeys = {} | ||
() => { | ||
jwtUtils.decode({}, pubKeys, audiences) | ||
JwtUtils.decode({}, pubKeys, audiences) | ||
}, | ||
@@ -30,3 +30,3 @@ 'to throw', | ||
() => { | ||
jwtUtils.decode(testJwt, [], audiences) | ||
JwtUtils.decode(testJwt, [], audiences) | ||
}, | ||
@@ -40,3 +40,3 @@ 'to throw', | ||
() => { | ||
jwtUtils.decode('hello.test', pubKeys, audiences) | ||
JwtUtils.decode('hello.test', pubKeys, audiences) | ||
}, | ||
@@ -50,3 +50,3 @@ 'to throw', | ||
() => { | ||
jwtUtils.decode(testJwt.substr(10), pubKeys, audiences) | ||
JwtUtils.decode(testJwt.substr(10), pubKeys, audiences) | ||
}, | ||
@@ -60,3 +60,3 @@ 'to throw', | ||
() => { | ||
jwtUtils.decode(testJwtWrongAlg, pubKeys, audiences) | ||
JwtUtils.decode(testJwtWrongAlg, pubKeys, audiences) | ||
}, | ||
@@ -63,0 +63,0 @@ 'to throw', |
'use strict' | ||
const expect = require('unexpected') | ||
const jwtUtils = require('./index') | ||
const { JwtUtils } = require('./index') | ||
@@ -78,3 +78,3 @@ const rsaPrivateKeyEncrypted = | ||
() => { | ||
jwtUtils.encode('', '', '') | ||
JwtUtils.encode('', '', '') | ||
}, | ||
@@ -88,3 +88,3 @@ 'to throw', | ||
() => { | ||
jwtUtils.encode('', {}, {}) | ||
JwtUtils.encode('', {}, {}) | ||
}, | ||
@@ -99,3 +99,3 @@ 'to throw', | ||
customJwtHeader.alg = algo | ||
let jwt = jwtUtils.encode( | ||
let jwt = JwtUtils.encode( | ||
rsaPrivateKeyEncrypted, | ||
@@ -106,3 +106,3 @@ customJwtHeader, | ||
) | ||
let decodedJwtBody = jwtUtils.decode(jwt, pubKeys, [ | ||
let decodedJwtBody = JwtUtils.decode(jwt, pubKeys, [ | ||
'https://host/oauth/token' | ||
@@ -109,0 +109,0 @@ ]) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
47935
1033
161