Socket
Socket
Sign inDemoInstall

@connectedcars/jwtutils

Package Overview
Dependencies
114
Maintainers
1
Versions
33
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.0.8 to 1.0.9

.npmignore

2

bin/jwtdecode.js

@@ -16,3 +16,3 @@ #!/usr/bin/env node

let issuer = process.argv[5]
let audiences = process.argv[6]
let audiences = process.argv[6].split(',')

@@ -19,0 +19,0 @@ let publicKey = fs.readFileSync(publicKeyPath)

@@ -42,2 +42,4 @@ 'use strict'

// Read stderr
let stderrStr = ''
let errorData = []

@@ -48,10 +50,9 @@ jwtEncode.stderr.on('data', data => {

jwtEncode.stderr.on('data', () => {
let error = Buffer.concat(errorData).toString('utf8')
if (error != '') {
done(new Error(error))
}
stderrStr = Buffer.concat(errorData).toString('utf8')
})
// Read token
let stdoutStr = ''
let decodedData = []
let error
jwtEncode.stdout.on('data', data => {

@@ -61,8 +62,20 @@ decodedData.push(data)

jwtEncode.stdout.on('end', () => {
let decodedBodyStr = Buffer.concat(decodedData).toString('utf8').trim()
let decodedBody = JSON.parse(decodedBodyStr)
expect(jwtBody, 'to equal', decodedBody)
done()
try {
stdoutStr = Buffer.concat(decodedData).toString('utf8').trim()
let decodedBody = JSON.parse(stdoutStr)
expect(decodedBody, 'to equal', jwtBody)
} catch (e) {
error = e
}
})
jwtEncode.on('exit', (code, signal) => {
if (error) {
console.log(`stdout:${stdoutStr}\nstderr:${stderrStr}\nexit:${code}`)
done(error)
} else {
done()
}
})
}).slow(2000)
})
{
"name": "@connectedcars/jwtutils",
"version": "1.0.8",
"version": "1.0.9",
"description": "Zero dependency JWT encoding/decoding for Node",

@@ -5,0 +5,0 @@ "main": "src/index.js",

@@ -121,2 +121,10 @@ # node-jwtutils

'1@RS256': publicKey // Fx. use key from before
},
'https://jwt.io/custom': { // Overwrite default validation for this issuer
'1@RS256': { // Same options can also be used directly with decode
publicKey: publicKey,
expiresMax: 3600, // Don't allow token that has a lifetime over 1 hour
expiresSkew: 600, // Allow tokens that expired up to 10 minutes ago
nbfIatSkew: 300, // Allow tokens that has nbf or iat in the future by up to 5 minutes
}
}

@@ -123,0 +131,0 @@ }

@@ -81,2 +81,19 @@ 'use strict'

'4@RS256': rsaOtherPublicKey.substr(2)
},
'test@custom.com': {
'1@RS256': {
publicKey: rsaPublicKey,
expiresSkew: 600,
expiresMax: 86400
}
},
'test@expired.com': {
'1@RS256': {
publicKey: rsaPublicKey,
validators: {
exp: () => {
throw new JwtVerifyError('Always expired')
}
}
}
}

@@ -89,5 +106,8 @@ }

let jwt = oldJwtUtils.encode(rsaPrivateKey, jwtHeader, jwtBody)
let decodedJwtBody = oldJwtUtils.decode(jwt, pubKeys, [
'https://host/oauth/token'
])
let decodedJwtBody = oldJwtUtils.decode(
jwt,
pubKeys,
['https://host/oauth/token'],
300
)
expect(jwtBody, 'to equal', decodedJwtBody)

@@ -138,2 +158,82 @@ })

})
it('success with expired token', () => {
let customJwtBody = Object.assign({}, jwtBody)
customJwtBody.iss = 'test@custom.com'
customJwtBody.exp -= 600
let jwt = JwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody)
let decodedJwtBody = JwtUtils.decode(jwt, pubKeys, [
'https://host/oauth/token'
])
expect(customJwtBody, 'to equal', decodedJwtBody)
})
it('token outside maximum expires', () => {
let customJwtBody = Object.assign({}, jwtBody)
customJwtBody.iss = 'test@custom.com'
customJwtBody.exp += 172800
let jwt = JwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody)
expect(
() => {
JwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token'])
},
'to throw',
`Expires in the future by more than 86400 seconds`
)
})
it('always fail with expired', () => {
let customJwtBody = Object.assign({}, jwtBody)
customJwtBody.iss = 'test@expired.com'
let jwt = JwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody)
expect(
() => {
JwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token'])
},
'to throw',
`Always expired`
)
})
it('token outside maximum expires using decode options', () => {
let customJwtBody = Object.assign({}, jwtBody)
customJwtBody.exp += 172800
let jwt = JwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody)
expect(
() => {
JwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token'], {
expiresMax: 600
})
},
'to throw',
`Expires in the future by more than 600 seconds`
)
})
it('token outside maximum expires using nbf', () => {
let customJwtBody = Object.assign({}, jwtBody)
customJwtBody.exp += 172800
customJwtBody.nbf = customJwtBody.iat
delete customJwtBody.iat
let jwt = JwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody)
expect(
() => {
JwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token'], {
expiresMax: 600
})
},
'to throw',
`Expires in the future by more than 600 seconds`
)
})
it('token outside maximum expires using unixNow', () => {
let customJwtBody = Object.assign({}, jwtBody)
customJwtBody.exp += 172800
delete customJwtBody.iat
let jwt = JwtUtils.encode(rsaPrivateKey, jwtHeader, customJwtBody)
expect(
() => {
JwtUtils.decode(jwt, pubKeys, ['https://host/oauth/token'], {
expiresMax: 600
})
},
'to throw',
`Expires in the future by more than 600 seconds`
)
})
it('unknown aud', () => {

@@ -235,3 +335,3 @@ let jwt = JwtUtils.encode(rsaPrivateKey, jwtHeader, jwtBody)

let customJwtHeader = Object.assign({}, jwtHeader)
customJwtHeader.kid = 3
customJwtHeader.kid = '3'
let jwt = JwtUtils.encode(rsaPrivateKey, customJwtHeader, jwtBody)

@@ -248,3 +348,3 @@ expect(

let customJwtHeader = Object.assign({}, jwtHeader)
customJwtHeader.kid = 2
customJwtHeader.kid = '2'
let jwt = JwtUtils.encode(rsaPrivateKey, customJwtHeader, jwtBody)

@@ -273,3 +373,3 @@ expect(

let customJwtHeader = Object.assign({}, jwtHeader)
customJwtHeader.kid = 4
customJwtHeader.kid = '4'
let jwt = JwtUtils.encode(rsaPrivateKey, customJwtHeader, jwtBody)

@@ -280,4 +380,4 @@ expect(

},
'to throw',
'PEM_read_bio_PUBKEY failed'
'to throw a',
Error
)

@@ -284,0 +384,0 @@ })

@@ -41,2 +41,8 @@ const express = require('express')

'1@ES256': ecPublicKey
},
'http://localhost/oauth/token/moreexpires': {
'1@ES256': {
publicKey: ecPublicKey,
expiresSkew: 600
}
}

@@ -43,0 +49,0 @@ }

@@ -8,3 +8,4 @@ 'use strict'

function jwtDecode(jwt, publicKeys, audiences, nbfIatSkrew = 300) {
const defaultOptions = { expiresSkew: 0, expiresMax: 0, nbfIatSkew: 300 }
function jwtDecode(jwt, publicKeys, audiences, options = defaultOptions) {
if (typeof jwt !== 'string') {

@@ -20,2 +21,17 @@ throw new Error('jwt needs to a string')

if (!Array.isArray(audiences)) {
throw new Error('audiences needs to be an array of allowed audiences')
}
if (typeof options === 'number') {
// Backwards compatibility with old api
options = {
nbfIatSkew: options
}
}
if (typeof options !== 'object' || Array.isArray(publicKeys)) {
throw new Error('options needs to a map of { nbfIatSkew: 300, ... }')
}
let parts = jwt.split(/\./)

@@ -65,11 +81,14 @@ if (parts.length !== 3) {

let signature = base64UrlSafe.decode(parts[2])
// Find public key
let pubkey =
typeof header.kid === 'string'
? issuer[`${header.kid}@${header.alg}`]
: issuer[`default@${header.alg}`]
const verifier = crypto.createVerify(algo)
verifier.write(`${parts[0]}.${parts[1]}`, 'utf8')
verifier.end()
let issuerOptions = {}
if (typeof pubkey === 'object' && pubkey !== null && pubkey.publicKey) {
issuerOptions = pubkey
pubkey = pubkey.publicKey
}
let pubkey = header.kid
? issuer[`${header.kid}@${header.alg}`]
: issuer[`default@${header.alg}`]
if (!pubkey) {

@@ -81,2 +100,7 @@ throw new JwtVerifyError(

// Validate signature
let signature = base64UrlSafe.decode(parts[2])
const verifier = crypto.createVerify(algo)
verifier.write(`${parts[0]}.${parts[1]}`, 'utf8')
verifier.end()
if (!verifier.verify(pubkey, signature)) {

@@ -88,32 +112,62 @@ throw new JwtVerifyError(

let auds = Array.isArray(body.aud) ? body.aud : [body.aud]
if (!auds.some(aud => audiences.includes(aud))) {
throw new JwtVerifyError(`Unknown audience '${auds.join(',')}'`)
let unixNow = Math.floor(Date.now() / 1000)
let validators = {
aud: validateAudience,
exp: validateExpires,
iat: validateIssuedAt,
nbf: validateNotBefore
}
Object.assign(validators, options.validators || {})
Object.assign(validators, issuerOptions.validators || {})
let unixNow = Math.floor(Date.now() / 1000)
let validationOptions = {}
Object.assign(validationOptions, options)
Object.assign(validationOptions, issuerOptions)
if (body.iat && body.iat > unixNow + nbfIatSkrew) {
validators.aud(body, audiences, validationOptions)
validators.iat(body, unixNow, validationOptions)
validators.nbf(body, unixNow, validationOptions)
validators.exp(body, unixNow, validationOptions)
return body
}
function validateNotBefore(body, unixNow, options) {
if (body.nbf && body.nbf > unixNow + options.nbfIatSkew) {
throw new JwtVerifyError(
`Issued at in the future by more than ${nbfIatSkrew} seconds`
`Not before in the future by more than ${options.nbfIatSkew} seconds`
)
}
}
if (body.nbf && body.nbf > unixNow + nbfIatSkrew) {
function validateIssuedAt(body, unixNow, options) {
if (body.iat && body.iat > unixNow + options.nbfIatSkew) {
throw new JwtVerifyError(
`Not before in the future by more than ${nbfIatSkrew} seconds`
`Issued at in the future by more than ${options.nbfIatSkew} seconds`
)
}
}
function validateAudience(body, audiences, options) {
let auds = Array.isArray(body.aud) ? body.aud : [body.aud]
if (!auds.some(aud => audiences.includes(aud))) {
throw new JwtVerifyError(`Unknown audience '${auds.join(',')}'`)
}
}
function validateExpires(body, unixNow, options = {}) {
if (!body.exp) {
throw new JwtVerifyError(`No expires set on token`)
}
if (body.exp <= unixNow) {
let notBefore = body.iat || body.nbf || unixNow
if (options.expiresMax && body.exp > notBefore + options.expiresMax) {
throw new JwtVerifyError(
`Expires in the future by more than ${options.expiresMax} seconds`
)
}
if (body.exp + (options.expiresSkew || 0) <= unixNow) {
throw new JwtVerifyError('Token has expired')
}
return body
}
module.exports = jwtDecode

@@ -35,2 +35,20 @@ 'use strict'

})
it('invalid audiences input', () => {
expect(
() => {
JwtUtils.decode(testJwt, pubKeys, '')
},
'to throw',
'audiences needs to be an array of allowed audiences'
)
})
it('invalid options input', () => {
expect(
() => {
JwtUtils.decode(testJwt, pubKeys, audiences, '')
},
'to throw',
'options needs to a map of { nbfIatSkew: 300, ... }'
)
})
it('too few spaces', () => {

@@ -37,0 +55,0 @@ expect(

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc