Skip to content

Commit

Permalink
feat: verifyJWT against nbf if present but use iat if not (#45)
Browse files Browse the repository at this point in the history
* feat: verifyJWT against nbf if present but use iat if not

* fix: apply skew in the opposite direction when checking exp

* test: make some test inputs more clear

* test: use hard coded JWTs to test validFrom teimstamp

* style: remove unused constants
  • Loading branch information
mi-xu committed Aug 15, 2019
1 parent a315380 commit b56ac64
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 46 deletions.
13 changes: 7 additions & 6 deletions src/JWT.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,12 +225,13 @@ export async function verifyJWT(
)
const now: number = Math.floor(Date.now() / 1000)
if (signer) {
if (payload.nbf && payload.nbf > now + NBF_SKEW) {
throw new Error(
`JWT not valid yet (issued in the future): nbf: ${
payload.nbf
} > now: ${now}`
)
const nowSkewed = now + NBF_SKEW
if (payload.nbf) {
if (payload.nbf > nowSkewed) {
throw new Error(`JWT not valid before nbf: ${payload.nbf}`)
}
} else if (payload.iat && payload.iat > nowSkewed) {
throw new Error(`JWT not valid yet (issued in the future) iat: ${payload.iat}`)
}
if (payload.exp && payload.exp <= now - NBF_SKEW) {
throw new Error(`JWT has expired: exp: ${payload.exp} < now: ${now}`)
Expand Down
63 changes: 39 additions & 24 deletions src/__tests__/JWT-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,14 +297,42 @@ describe('verifyJWT()', () => {
})
})

it('accepts a valid nbf', () => {
return createJWT({ nbf: NOW + NBF_SKEW }, { issuer: did, signer }).then(
jwt =>
verifyJWT(jwt).then(
({ payload }) => expect(payload).toMatchSnapshot(),
error => expect(error).toBeNull()
)
)
describe('validFrom timestamp', () => {
it('passes when nbf is in the past', async () => {
// tslint:disable-next-line: max-line-length
const jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJpYXQiOjE0ODUzMjExMzMsIm5iZiI6MTQ4NTI2MTEzMywiaXNzIjoiZGlkOnVwb3J0OjJuUXRpUUc2Q2dtMUdZVEJhYUtBZ3I3NnVZN2lTZXhVa3FYIn0.btzVz7fZsoSEDa7JyWo3cYWL63pkWTKTz8OUzepIesfSFeBozUjX2oq1xOJ2OyzuinnLGwtSqY303VoyALrafA'
expect(verifyJWT(jwt)).resolves.not.toThrow()
})
it('passes when nbf is in the past and iat is in the future', async () => {
// tslint:disable-next-line: max-line-length
const jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJpYXQiOjE0ODUzODExMzMsIm5iZiI6MTQ4NTI2MTEzMywiaXNzIjoiZGlkOnVwb3J0OjJuUXRpUUc2Q2dtMUdZVEJhYUtBZ3I3NnVZN2lTZXhVa3FYIn0.ELsPnDC_YTTkT5hxw09UCLSjWVje9mDs1n_mpvlo2Wk5VJONSy-FDAzm5TunzzCeLixU04m6dD4w6Uk3-OVkww'
expect(verifyJWT(jwt)).resolves.not.toThrow()
})
it('fails when nbf is in the future', async () => {
// tslint:disable-next-line: max-line-length
const jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJpYXQiOjE0ODUzMjExMzMsIm5iZiI6MTQ4NTM4MTEzMywiaXNzIjoiZGlkOnVwb3J0OjJuUXRpUUc2Q2dtMUdZVEJhYUtBZ3I3NnVZN2lTZXhVa3FYIn0.rcFuhVHtie3Y09pWxBSf1dnjaVh6FFQLHh-83N-uLty3M5ADJ-jVFFkyt_Eupl8Kr735-oPGn_D1Nj9rl4s_Kw'
expect(verifyJWT(jwt)).rejects.toThrow()
})
it('fails when nbf is in the future and iat is in the past', async () => {
// tslint:disable-next-line: max-line-length
const jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJpYXQiOjE0ODUyNjExMzMsIm5iZiI6MTQ4NTM4MTEzMywiaXNzIjoiZGlkOnVwb3J0OjJuUXRpUUc2Q2dtMUdZVEJhYUtBZ3I3NnVZN2lTZXhVa3FYIn0.jiVI11IcKNOvnDrJBzojKtNAGaZbEcafcqW-wfP78g6-6RucjYPBi5qvKje35IOvITWvvpXpK48IW-17Srh02w'
expect(verifyJWT(jwt)).rejects.toThrow()
})
it('passes when nbf is missing and iat is in the past', async () => {
// tslint:disable-next-line: max-line-length
const jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJpYXQiOjE0ODUyNjExMzMsImlzcyI6ImRpZDp1cG9ydDoyblF0aVFHNkNnbTFHWVRCYWFLQWdyNzZ1WTdpU2V4VWtxWCJ9.1VwGHDm7f9V-1Fa545uAwF9NfU3RI8yqRFW6XAHOg0FBeM7krC_rEf0PwqbKFO8MiIBELBwUhW_fT4oZsuggUA'
expect(verifyJWT(jwt)).resolves.not.toThrow()
})
it('fails when nbf is missing and iat is in the future', async () => {
// tslint:disable-next-line: max-line-length
const jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJpYXQiOjE0ODUzODExMzMsImlzcyI6ImRpZDp1cG9ydDoyblF0aVFHNkNnbTFHWVRCYWFLQWdyNzZ1WTdpU2V4VWtxWCJ9.jU0R8qP3aUX_3DiFt9tIONiq_P5OooFc-ypUwpqK4plGyw6WiI0FTGfZvq7pOarKrjmSojE9Sm_3ETfMpdQckg'
expect(verifyJWT(jwt)).rejects.toThrow()
})
it('passes when nbf and iat are both missing', async () => {
// tslint:disable-next-line: max-line-length
const jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJpc3MiOiJkaWQ6dXBvcnQ6Mm5RdGlRRzZDZ20xR1lUQmFhS0Fncjc2dVk3aVNleFVrcVgifQ.5kGKU9ljebhTqvfVDu9MH7vGAqRH0GDTbZNGH45YmhUySgBTyI7u-MkkRit72eFvQAqBfzw6wNUbGf9FPC5AtQ'
expect(verifyJWT(jwt)).resolves.not.toThrow()
})
})

it('handles ES256K-R algorithm', () => {
Expand All @@ -331,25 +359,12 @@ describe('verifyJWT()', () => {
)
})

it('rejects an nbf in the future', () => {
return createJWT({ nbf: NOW + NBF_SKEW + 1 }, { issuer: did, signer }).then(
jwt =>
verifyJWT(jwt)
.catch(error =>
expect(error.message).toEqual(
'JWT not valid yet (issued in the future): nbf: 1485321434 > now: 1485321133'
)
)
.then(p => expect(p).toBeFalsy())
)
})

it('accepts a valid exp', () => {
return createJWT(
{ exp: NOW - NBF_SKEW + 1 },
{ issuer: did, signer, expiresIn: 1 }
{ exp: NOW },
{ issuer: did, signer }
).then(jwt =>
verifyJWT(jwt).then(({ payload }) => expect(payload).toMatchSnapshot())
verifyJWT(jwt).then(({ payload }) => expect(payload).toBeDefined())
)
})

Expand Down
16 changes: 0 additions & 16 deletions src/__tests__/__snapshots__/JWT-test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -81,22 +81,6 @@ Object {
}
`;

exports[`verifyJWT() accepts a valid exp 1`] = `
Object {
"exp": 1485320834,
"iat": 1485321133,
"iss": "did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX",
}
`;

exports[`verifyJWT() accepts a valid nbf 1`] = `
Object {
"iat": 1485321133,
"iss": "did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX",
"nbf": 1485321433,
}
`;

exports[`verifyJWT() handles ES256K-R algorithm 1`] = `
Object {
"hello": "world",
Expand Down

0 comments on commit b56ac64

Please sign in to comment.