374 lines
11 KiB
JavaScript
374 lines
11 KiB
JavaScript
import { Eltro as t, assert} from 'eltro'
|
|
import encode from '../../api/jwt/encode.mjs'
|
|
import decode from '../../api/jwt/decode.mjs'
|
|
import defaults from '../../api/defaults.mjs'
|
|
|
|
const unixNow = Math.floor(Date.now() / 1000)
|
|
const jwtHeader = {
|
|
typ: 'JWT',
|
|
alg: 'HS256',
|
|
kid: '2'
|
|
}
|
|
|
|
const jwtBody = {
|
|
aud: 'https://host/oauth/token',
|
|
iss: 'test@test.com',
|
|
iat: unixNow,
|
|
exp: unixNow + 600,
|
|
scope: ['http://stuff', 'http://stuff2']
|
|
}
|
|
|
|
const pubKeys = {
|
|
'test@test.com': {
|
|
'default@HS256': 'sharedkey',
|
|
'2@HS256': 'sharedkey',
|
|
'2@HS384': 'sharedkey',
|
|
'2@HS512': 'sharedkey',
|
|
'5@HS256': 'wrongkey'
|
|
},
|
|
'test@custom.com': {
|
|
'2@HS256': {
|
|
key: 'sharedkey',
|
|
expiresSkew: 600,
|
|
expiresMax: 86400
|
|
},
|
|
'3@HS256': {
|
|
},
|
|
'4@HS256': [],
|
|
'5@HS256': { key: 'sharedkey' },
|
|
},
|
|
}
|
|
|
|
t.describe('encode/decode', function() {
|
|
['HS256', 'HS384', 'HS512'].forEach(function(algo) {
|
|
t.test('success with ' + algo, function() {
|
|
let customJwtHeader = defaults(jwtHeader)
|
|
customJwtHeader.kid = '2'
|
|
customJwtHeader.alg = algo
|
|
let jwt = encode(customJwtHeader, jwtBody, 'sharedkey')
|
|
let decodedJwtBody = decode(jwt, pubKeys, [
|
|
'https://host/oauth/token'
|
|
])
|
|
assert.deepStrictEqual(jwtBody, decodedJwtBody)
|
|
})
|
|
})
|
|
t.test('success without kid', function() {
|
|
let customJwtHeader = defaults(jwtHeader)
|
|
delete customJwtHeader.kid
|
|
let jwt = encode(customJwtHeader, jwtBody, 'sharedkey')
|
|
let decodedJwtBody = decode(jwt, pubKeys, [
|
|
'https://host/oauth/token'
|
|
])
|
|
assert.deepStrictEqual(jwtBody, decodedJwtBody)
|
|
})
|
|
t.test('success without iat or iss', function() {
|
|
let customJwtBody = defaults(jwtBody)
|
|
delete customJwtBody.iat
|
|
delete customJwtBody.exp
|
|
let jwt = encode(jwtHeader, customJwtBody, 'sharedkey')
|
|
let decodedJwtBody = decode(jwt, pubKeys, [
|
|
'https://host/oauth/token'
|
|
])
|
|
assert.ok(decodedJwtBody.iat)
|
|
assert.ok(decodedJwtBody.iat >= unixNow)
|
|
assert.ok(decodedJwtBody.iat < unixNow + 2)
|
|
assert.ok(decodedJwtBody.exp)
|
|
assert.ok(decodedJwtBody.exp >= unixNow)
|
|
assert.ok(decodedJwtBody.exp > unixNow + 200)
|
|
assert.ok(decodedJwtBody.exp < unixNow + 400)
|
|
})
|
|
t.test('success with object key', function() {
|
|
let customJwtBody = defaults(jwtBody)
|
|
customJwtBody.kid = '5'
|
|
customJwtBody.iss = 'test@custom.com'
|
|
let jwt = encode(jwtHeader, jwtBody, 'sharedkey')
|
|
let decodedJwtBody = decode(jwt, pubKeys, [
|
|
'https://host/oauth/token'
|
|
])
|
|
assert.deepStrictEqual(jwtBody, decodedJwtBody)
|
|
})
|
|
t.test('success with array aud', function() {
|
|
let customJwtBody = defaults(jwtBody)
|
|
customJwtBody.aud = [
|
|
'https://myhost/oauth/token',
|
|
'https://host/oauth/token'
|
|
]
|
|
let jwt = encode(jwtHeader, customJwtBody, 'sharedkey')
|
|
let decodedJwtBody = decode(jwt, pubKeys, [
|
|
'https://host/oauth/token'
|
|
])
|
|
assert.deepStrictEqual(customJwtBody, decodedJwtBody)
|
|
})
|
|
t.test('success with no aud and empty audience list', function() {
|
|
let customJwtBody = defaults(jwtBody)
|
|
delete customJwtBody.aud
|
|
let jwt = encode(jwtHeader, customJwtBody, 'sharedkey')
|
|
let decodedJwtBody = decode(jwt, pubKeys, [])
|
|
assert.deepStrictEqual(customJwtBody, decodedJwtBody)
|
|
})
|
|
t.test('success with expired token', function() {
|
|
let customJwtBody = defaults(jwtBody)
|
|
customJwtBody.iss = 'test@custom.com'
|
|
customJwtBody.exp -= 600
|
|
let jwt = encode(jwtHeader, customJwtBody, 'sharedkey')
|
|
let decodedJwtBody = decode(jwt, pubKeys, [
|
|
'https://host/oauth/token'
|
|
])
|
|
assert.deepStrictEqual(customJwtBody, decodedJwtBody)
|
|
})
|
|
t.test('token outside maximum expires', function() {
|
|
let customJwtBody = defaults(jwtBody)
|
|
customJwtBody.iss = 'test@custom.com'
|
|
customJwtBody.exp += 172800
|
|
let jwt = encode(jwtHeader, customJwtBody, 'sharedkey')
|
|
assert.throws(
|
|
function() {
|
|
decode(jwt, pubKeys, ['https://host/oauth/token'])
|
|
},
|
|
/Expires in the future by more than 86400 seconds/
|
|
)
|
|
})
|
|
t.test('token outside maximum expires using decode options', function() {
|
|
let customJwtBody = defaults(jwtBody)
|
|
customJwtBody.exp += 172800
|
|
let jwt = encode(jwtHeader, customJwtBody, 'sharedkey')
|
|
assert.throws(
|
|
function() {
|
|
decode(jwt, pubKeys, ['https://host/oauth/token'], {
|
|
expiresMax: 600
|
|
})
|
|
},
|
|
/Expires in the future by more than 600 seconds/
|
|
)
|
|
})
|
|
t.test('token outside maximum expires using nbf', function() {
|
|
let customJwtBody = defaults(jwtBody)
|
|
customJwtBody.exp += 172800
|
|
customJwtBody.nbf = customJwtBody.iat
|
|
delete customJwtBody.iat
|
|
let jwt = encode(jwtHeader, customJwtBody, 'sharedkey')
|
|
assert.throws(
|
|
function() {
|
|
decode(jwt, pubKeys, ['https://host/oauth/token'], {
|
|
expiresMax: 600
|
|
})
|
|
},
|
|
/Expires in the future by more than 600 seconds/
|
|
)
|
|
})
|
|
t.test('token outside maximum expires using unixNow', function() {
|
|
let customJwtBody = defaults(jwtBody)
|
|
customJwtBody.exp += 172800
|
|
delete customJwtBody.iat
|
|
let jwt = encode(jwtHeader, customJwtBody, 'sharedkey')
|
|
assert.throws(
|
|
function() {
|
|
decode(jwt, pubKeys, ['https://host/oauth/token'], {
|
|
expiresMax: 600
|
|
})
|
|
},
|
|
/Expires in the future by more than 600 seconds/
|
|
)
|
|
})
|
|
t.test('unknown aud', function() {
|
|
let jwt = encode(jwtHeader, jwtBody, 'sharedkey')
|
|
assert.throws(
|
|
function() {
|
|
decode(jwt, pubKeys, ['https://myhost/oauth/token'])
|
|
},
|
|
/Unknown audience 'https:\/\/host\/oauth\/token'/
|
|
)
|
|
})
|
|
t.test('expired', function() {
|
|
let customJwtBody = defaults(jwtBody)
|
|
customJwtBody.iat -= 1200
|
|
customJwtBody.exp -= 800
|
|
let jwt = encode(jwtHeader, customJwtBody, 'sharedkey')
|
|
assert.throws(
|
|
function() {
|
|
decode(jwt, pubKeys, ['https://host/oauth/token'])
|
|
},
|
|
/Token has expired/
|
|
)
|
|
})
|
|
t.test('invalid', function() {
|
|
assert.throws(
|
|
function() {
|
|
decode('asdfasdgassdga.asdfasdg.sadfsadfas', pubKeys, ['https://host/oauth/token'])
|
|
},
|
|
/Token was invalid/
|
|
)
|
|
})
|
|
t.test('missing exp', function() {
|
|
let customJwtBody = defaults(jwtBody)
|
|
delete customJwtBody.exp
|
|
let jwt = encode(jwtHeader, customJwtBody, 'sharedkey', true)
|
|
assert.throws(
|
|
function() {
|
|
decode(jwt, pubKeys, ['https://host/oauth/token'])
|
|
},
|
|
/No expires set on token/
|
|
)
|
|
})
|
|
t.test('missing iss', function() {
|
|
let customJwtBody = defaults(jwtBody)
|
|
delete customJwtBody.iss
|
|
let jwt = encode(jwtHeader, customJwtBody, 'sharedkey')
|
|
assert.throws(
|
|
function() {
|
|
decode(jwt, pubKeys, ['https://host/oauth/token'])
|
|
},
|
|
/No issuer set/
|
|
)
|
|
})
|
|
t.test('iat invalid', function() {
|
|
let customJwtBody = defaults(jwtBody)
|
|
customJwtBody.iat += 1200
|
|
let jwt = encode(jwtHeader, customJwtBody, 'sharedkey')
|
|
assert.throws(
|
|
function() {
|
|
decode(jwt, pubKeys, ['https://host/oauth/token'])
|
|
},
|
|
/Issued at in the future by more than 300 seconds/
|
|
)
|
|
})
|
|
t.test('nbf invalid', function() {
|
|
let customJwtBody = defaults(jwtBody)
|
|
customJwtBody.nbf = customJwtBody.iat + 1200
|
|
let jwt = encode(jwtHeader, customJwtBody, 'sharedkey')
|
|
assert.throws(
|
|
function() {
|
|
decode(jwt, pubKeys, ['https://host/oauth/token'])
|
|
},
|
|
/Not before in the future by more than 300 seconds/
|
|
)
|
|
})
|
|
t.test('unknown issuer', function() {
|
|
let customJwtBody = defaults(jwtBody)
|
|
customJwtBody.iss = 'unknown@test.com'
|
|
let jwt = encode(jwtHeader, customJwtBody, 'sharedkey')
|
|
assert.throws(
|
|
function() {
|
|
decode(jwt, pubKeys, ['https://host/oauth/token'])
|
|
},
|
|
/Unknown issuer 'unknown@test.com'/
|
|
)
|
|
})
|
|
t.test('wrong alg', function() {
|
|
let customJwtHeader = defaults(jwtHeader)
|
|
customJwtHeader.alg = 'HS128'
|
|
assert.throws(
|
|
function() {
|
|
encode(customJwtHeader, jwtBody, 'sharedkey')
|
|
},
|
|
/Only alg HS256, HS384 and HS512 are supported/
|
|
)
|
|
})
|
|
t.test('unknown kid', function() {
|
|
let customJwtHeader = defaults(jwtHeader)
|
|
customJwtHeader.kid = '3'
|
|
let jwt = encode(customJwtHeader, jwtBody, 'sharedkey')
|
|
assert.throws(
|
|
function() {
|
|
decode(jwt, pubKeys, ['https://host/oauth/token'])
|
|
},
|
|
/Unknown pubkey id '3'/
|
|
)
|
|
})
|
|
t.test('invalid signature', function() {
|
|
let customJwtHeader = defaults(jwtHeader)
|
|
customJwtHeader.kid = '2'
|
|
let jwt = encode(customJwtHeader, jwtBody, 'sharedkey')
|
|
let backup = jwt
|
|
if (jwt[jwt.length - 2] === 'A') {
|
|
jwt = jwt.slice(0, -2) + 'BB'
|
|
} else {
|
|
jwt = jwt.slice(0, -2) + 'AA'
|
|
}
|
|
try {
|
|
assert.throws(
|
|
function() {
|
|
decode(jwt, pubKeys, ['https://host/oauth/token'])
|
|
},
|
|
/Verification failed with alg 'HS256'/
|
|
)
|
|
} catch (err) {
|
|
console.log('-----')
|
|
console.log(backup)
|
|
console.log(jwt)
|
|
throw err
|
|
}
|
|
jwt = encode(customJwtHeader, jwtBody, 'sharedkey')
|
|
jwt += 'a'
|
|
assert.throws(
|
|
function() {
|
|
decode(jwt, pubKeys, ['https://host/oauth/token'])
|
|
},
|
|
/Verification failed with alg 'HS256'/
|
|
)
|
|
})
|
|
t.test('invalid shared key', function() {
|
|
let customJwtHeader = defaults(jwtHeader)
|
|
customJwtHeader.kid = '5'
|
|
customJwtHeader.alg = 'HS256'
|
|
let jwt = encode(customJwtHeader, jwtBody, 'sharedkey')
|
|
assert.throws(
|
|
function() {
|
|
decode(jwt, pubKeys, ['https://host/oauth/token'])
|
|
},
|
|
/Verification failed with alg 'HS256'/
|
|
)
|
|
})
|
|
t.test('invalid pubkey', function() {
|
|
let customJwtHeader = defaults(jwtHeader)
|
|
customJwtHeader.kid = '4'
|
|
let jwt = encode(customJwtHeader, jwtBody, 'sharedkey')
|
|
assert.throws(
|
|
function() {
|
|
decode(jwt, pubKeys, ['https://host/oauth/token'])
|
|
}
|
|
)
|
|
let customJwtBody = defaults(jwtBody)
|
|
customJwtBody.iss = 'test@custom.com'
|
|
customJwtHeader.kid = '3'
|
|
jwt = encode(customJwtHeader, customJwtBody, 'sharedkey')
|
|
assert.throws(
|
|
function() {
|
|
decode(jwt, pubKeys, ['https://host/oauth/token'])
|
|
},
|
|
/'3'.+misconfigured/
|
|
)
|
|
customJwtHeader.kid = '4'
|
|
jwt = encode(customJwtHeader, customJwtBody, 'sharedkey')
|
|
assert.throws(
|
|
function() {
|
|
decode(jwt, pubKeys, ['https://host/oauth/token'])
|
|
},
|
|
/'4'.+misconfigured/
|
|
)
|
|
})
|
|
t.test('success with broken token', function() {
|
|
let expectedJwtBody = {
|
|
id: 1,
|
|
exp: 1519802991,
|
|
iat: 1519802691,
|
|
iss: 'test@test.com',
|
|
aud: 'https://host/oauth/token'
|
|
}
|
|
let decodedJwtBody = decode(
|
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNTE5ODAyNjkxfQ.p6t378Ri2JpOCm9WtC36ttyH8ILzG9-OWT_kgMrrRfo',
|
|
pubKeys,
|
|
['https://host/oauth/token'],
|
|
{
|
|
fixup: (header, body) => {
|
|
header.kid = '2'
|
|
body.iss = 'test@test.com'
|
|
body.aud = 'https://host/oauth/token'
|
|
body.exp = body.iat + 300
|
|
},
|
|
expiresSkew: 307584000
|
|
}
|
|
)
|
|
assert.deepStrictEqual(decodedJwtBody, expectedJwtBody)
|
|
})
|
|
})
|