Skip to content

Commit

Permalink
feat(jwt): Support custom secret keys for signing JWTs (#3546)
Browse files Browse the repository at this point in the history
* Support custom secret keys for signing JWTs

- Expanded key type check to include 'secret' for JWT signing. This aligns importPrivateKey method with importPublicKey method for handling "secret" type keys.

- Added a test case to sign, verify, and decode using a custom secret, ensuring the correct flow and error handling with mismatched keys.

* update comment for rejected private key imports
  • Loading branch information
kaandok authored Oct 23, 2024
1 parent 7735f2f commit 0a99bd3
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 2 deletions.
6 changes: 4 additions & 2 deletions src/utils/jwt/jws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@ async function importPrivateKey(key: SignatureKey, alg: KeyImporterAlgorithm): P
throw new Error('`crypto.subtle.importKey` is undefined. JWT auth middleware requires it.')
}
if (isCryptoKey(key)) {
if (key.type !== 'private') {
throw new Error(`unexpected non private key: CryptoKey.type is ${key.type}`)
if (key.type !== 'private' && key.type !== 'secret') {
throw new Error(
`unexpected key type: CryptoKey.type is ${key.type}, expected private or secret`
)
}
return key
}
Expand Down
42 changes: 42 additions & 0 deletions src/utils/jwt/jwt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,48 @@ describe('JWT', () => {
expect(err instanceof JwtTokenSignatureMismatched).toBe(true)
})

it('sign & verify & decode with a custom secret', async () => {
const payload = { message: 'hello world' }
const algorithm = {
name: 'HMAC',
hash: {
name: 'SHA-256',
},
}
const secret = await crypto.subtle.importKey(
'raw',
Buffer.from('cefb73234d5fae4bf27662900732b52943e8d53e871fe0f353da95de4599c21d', 'hex'),
algorithm,
false,
['sign', 'verify']
)
const tok = await JWT.sign(payload, secret)
const expected =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXNzYWdlIjoiaGVsbG8gd29ybGQifQ.qunGhchNXH_unqWXN6hB0Elhzr5SykSXVhklLti1aFI'
expect(tok).toEqual(expected)

const verifiedPayload = await JWT.verify(tok, secret)
expect(verifiedPayload).not.toBeUndefined()
expect(verifiedPayload).toEqual(payload)

const invalidSecret = await crypto.subtle.importKey(
'raw',
Buffer.from('cefb73234d5fae4bf27662900732b52943e8d53e871fe0f353da95de41111111', 'hex'),
algorithm,
false,
['sign', 'verify']
)
let err = null
let authorized
try {
authorized = await JWT.verify(tok, invalidSecret)
} catch (e) {
err = e
}
expect(authorized).toBeUndefined()
expect(err instanceof JwtTokenSignatureMismatched).toBe(true)
})

const rsTestCases = [
{
alg: AlgorithmTypes.RS256,
Expand Down

0 comments on commit 0a99bd3

Please sign in to comment.