Skip to content

Commit

Permalink
feat!: provide fully decoded jwt object to key callback (#335)
Browse files Browse the repository at this point in the history
* feat: provide fully decoded jwt object to key callback

* test: add tests and update types
  • Loading branch information
guilhermelimak authored May 18, 2023
1 parent b950a84 commit 8f77fd8
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 20 deletions.
10 changes: 8 additions & 2 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,15 @@ declare class TokenError extends Error {
type SignerCallback = (e: Error | TokenError | null, token: string) => void
type VerifierCallback = (e: Error | TokenError | null, payload: any) => void

type DecodedJwt = {
header: { [key: string]: any },
payload: string,
signature: string
}

type KeyFetcher =
| ((header: { [key: string]: any }) => Promise<string | Buffer>)
| ((header: { [key: string]: any }, cb: (err: Error | TokenError | null, key: string | Buffer) => void) => void)
| ((DecodedJwt: DecodedJwt) => Promise<string | Buffer>)
| ((DecodedJwt: DecodedJwt, cb: (err: Error | TokenError | null, key: string | Buffer) => void) => void)

declare function SignerSync(payload: string | Buffer | { [key: string]: any }): string
declare function SignerAsync(payload: string | Buffer | { [key: string]: any }): Promise<string>
Expand Down
2 changes: 1 addition & 1 deletion src/signer.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ function sign(
}

// Get the key asynchronously
getAsyncKey(key, header, (err, currentKey) => {
getAsyncKey(key, { header, payload }, (err, currentKey) => {
if (err) {
const error = TokenError.wrap(err, TokenError.codes.keyFetchingError, 'Cannot fetch key.')
return callback(error)
Expand Down
6 changes: 3 additions & 3 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ const algorithmMatcher = /"alg"\s*:\s*"[HERP]S(256|384)"/m
const edAlgorithmMatcher = /"alg"\s*:\s*"EdDSA"/m
const ed448CurveMatcher = /"crv"\s*:\s*"Ed448"/m

function getAsyncKey(handler, header, callback) {
const result = handler(header, callback)
function getAsyncKey(handler, decoded, callback) {
const result = handler(decoded, callback)

if (result && typeof result.then === 'function') {
result
Expand All @@ -31,7 +31,7 @@ function ensurePromiseCallback(callback) {
})

return [
function(err, token) {
function (err, token) {
if (err) {
return promiseReject(err)
}
Expand Down
2 changes: 1 addition & 1 deletion src/verifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ function verify(
}

// Get the key asynchronously
getAsyncKey(key, header, (err, currentKey) => {
getAsyncKey(key, { header, payload, signature }, (err, currentKey) => {
if (err) {
return callback(
cacheSet(cacheContext, TokenError.wrap(err, TokenError.codes.keyFetchingError, 'Cannot fetch key.'))
Expand Down
30 changes: 26 additions & 4 deletions test/signer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,25 @@ function sign(payload, options, callback) {
return signer(payload, callback)
}

test('it passes the correct decoded jwt token to the key callback', async t => {
sign(
{ a: 1 },
{
noTimestamp: true,
key: decodedJwt => {
t.strictSame(decodedJwt, {
payload: { a: 1 },
header: {
alg: undefined,
typ: 'JWT',
kid: undefined
}
})
}
}
)
})

test('it correctly returns a token - sync', t => {
t.equal(
sign({ a: 1 }, { noTimestamp: true }),
Expand Down Expand Up @@ -79,7 +98,10 @@ test('it correctly returns a token - sync', t => {

test('it correctly returns a token - async - key with callback', async t => {
t.equal(
await sign({ a: 1 }, { key: (_h, callback) => setTimeout(() => callback(null, 'secret'), 10), noTimestamp: true }),
await sign(
{ a: 1 },
{ key: (_decodedJwt, callback) => setTimeout(() => callback(null, 'secret'), 10), noTimestamp: true }
),
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoxfQ.57TF7smP9XDhIexBqPC-F1toZReYZLWb_YRU5tv0sxM'
)
})
Expand Down Expand Up @@ -513,7 +535,7 @@ test('it correctly handle errors - callback', t => {
sign(
{ a: 1 },
{
key: (header, callback) => {
key: (_decodedJwt, callback) => {
callback(new Error('FAILED'))
},
noTimestamp: true
Expand All @@ -531,7 +553,7 @@ test('it correctly validates the key received from the callback', t => {
sign(
{ a: 1 },
{
key: (header, callback) => {
key: (_decodedJwt, callback) => {
callback(null, 123)
},
noTimestamp: true
Expand All @@ -552,7 +574,7 @@ test('it correctly handle errors - evented callback', t => {
sign(
{ a: 1 },
{
key: (header, callback) => {
key: (_decodedJwt, callback) => {
process.nextTick(() => callback(null, 'FAILED'))
},
noTimestamp: true,
Expand Down
10 changes: 5 additions & 5 deletions test/types.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-empty-function */

import { createDecoder, createSigner, createVerifier, JwtHeader, TokenError } from '..'
import { createDecoder, createSigner, createVerifier, DecodedJwt, JwtHeader, TokenError } from '..'
import { expectAssignable, expectNotAssignable } from 'tsd'

// Signing
Expand All @@ -18,15 +18,15 @@ signerAsync({ key: '1' }, (_e: Error | null, _token?: string) => {})
// Dynamic key in callback style
createSigner({
clockTimestamp: 10,
key(_header: { [key: string]: any }, cb: (e: Error | null, key: string) => void): void {
key(_decodedJwt: DecodedJwt, cb: (e: Error | null, key: string) => void): void {
cb(null, 'KEY')
}
})({ key: 1 }).then(console.log, console.log)

// Dynamic key in async style
createSigner({
clockTimestamp: 10,
async key(_header: { [key: string]: any }) {
async key(_decodedJwt: DecodedJwt) {
return 'KEY'
}
})({ key: 1 }).then(console.log, console.log)
Expand All @@ -48,15 +48,15 @@ verifierAsync(Buffer.from('456'), (_e: Error | null, _token?: string) => {})
// Dynamic key in callback style
createVerifier({
clockTimestamp: 10,
key(_header: { [key: string]: any }, cb: (e: Error | null, key: string) => void): void {
key(_decodedJwt: DecodedJwt, cb: (e: Error | null, key: string) => void): void {
cb(null, 'KEY')
}
})('123').then(console.log, console.log)

// Dynamic key in async style
createVerifier({
clockTimestamp: 10,
async key(_header: { [key: string]: any }) {
async key(_decodedJwt: DecodedJwt) {
return 'KEY'
}
})('456').then(console.log, console.log)
Expand Down
27 changes: 23 additions & 4 deletions test/verifier.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,25 @@ function verify(token, options, callback) {
return verifier(token, callback)
}

test('it gets the correct decoded jwt token as argument on the key callback', async t => {
verify('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoxfQ.57TF7smP9XDhIexBqPC-F1toZReYZLWb_YRU5tv0sxM', {
key: async decoded => {
t.strictSame(
decoded,
{
header: { typ: 'JWT', alg: 'HS256' },
payload: { a: 1 },
signature: '57TF7smP9XDhIexBqPC-F1toZReYZLWb_YRU5tv0sxM'
}
)

return Buffer.from('secret', 'utf-8')
},
noTimestamp: true,
complete: true
})
})

test('it correctly verifies a token - sync', t => {
t.strictSame(
verify('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoxfQ.57TF7smP9XDhIexBqPC-F1toZReYZLWb_YRU5tv0sxM', {
Expand Down Expand Up @@ -134,7 +153,7 @@ test('it correctly verifies a token - sync', t => {
test('it correctly verifies a token - async - key with callback', async t => {
t.strictSame(
await verify('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoxfQ.57TF7smP9XDhIexBqPC-F1toZReYZLWb_YRU5tv0sxM', {
key: (_h, callback) => setTimeout(() => callback(null, 'secret'), 10),
key: (_decodedJwt, callback) => setTimeout(() => callback(null, 'secret'), 10),
noTimestamp: true
}),
{ a: 1 }
Expand All @@ -143,7 +162,7 @@ test('it correctly verifies a token - async - key with callback', async t => {
t.strictSame(
await verify('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoxfQ.57TF7smP9XDhIexBqPC-F1toZReYZLWb_YRU5tv0sxM', {
algorithms: ['HS256'],
key: (_h, callback) => setTimeout(() => callback(null, 'secret'), 10),
key: (_decodedJwt, callback) => setTimeout(() => callback(null, 'secret'), 10),
noTimestamp: true,
complete: true
}),
Expand Down Expand Up @@ -247,7 +266,7 @@ test('it correctly handle errors - callback', t => {
verify(
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoxfQ.57TF7smP9XDhIexBqPC-F1toZReYZLWb_YRU5tv0sxM',
{
key: (header, callback) => {
key: (_decodedJwt, callback) => {
callback(new Error('FAILED'))
}
},
Expand All @@ -264,7 +283,7 @@ test('it correctly handle errors - evented callback', t => {
verify(
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoxfQ.57TF7smP9XDhIexBqPC-F1toZReYZLWb_YRU5tv0sxM',
{
key: (header, callback) => {
key: (_decodedJwt, callback) => {
process.nextTick(() => callback(null, 'FAILED'))
}
},
Expand Down

0 comments on commit 8f77fd8

Please sign in to comment.