Skip to content

fix: skip public key detection for HSxxx algorithms#576

Open
hf wants to merge 7 commits intonearform:masterfrom
hf:hf/fix-public-like-secret-key
Open

fix: skip public key detection for HSxxx algorithms#576
hf wants to merge 7 commits intonearform:masterfrom
hf:hf/fix-public-like-secret-key

Conversation

@hf
Copy link
Copy Markdown

@hf hf commented Dec 17, 2025

We use fast-jwt at Supabase and are very grateful for this library.

We ran into a problem recently where some HS256 keys used to sign JWTs look quite a lot like a public/private key. The library ignores the algorithm: 'HS256' indication and errors out with:

new TokenError(TokenError.codes.invalidKey, 'Public keys are not supported for signing.')

Instead, it should not try to parse the string if the developer has indicated the string or buffer is a HMAC secret only, regardless of what it contains.

Fixes #577

Comment thread src/crypto.js Outdated
@ilteoood
Copy link
Copy Markdown
Contributor

Additionally, it would be great to have a test for this.

@simoneb
Copy link
Copy Markdown
Member

simoneb commented Dec 18, 2025

@hf can you please add tests?

hf and others added 2 commits December 18, 2025 13:36
Co-authored-by: Matteo Pietro Dazzi <matteopietro.dazzi@gmail.com>
@hf
Copy link
Copy Markdown
Author

hf commented Dec 19, 2025

Added a test. Tried to fix this for the verifier also, but decided not to. I'm new to this codebase and it's hard to find my way around it.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes an issue where HMAC secrets (used with HS256/HS384/HS512 algorithms) that contain patterns resembling PEM-encoded public keys would incorrectly trigger a "Public keys are not supported for signing" error. The fix adds an early return in the private key detection logic to skip public key validation when an HS algorithm is explicitly provided.

Key changes:

  • Modified performDetectPrivateKeyAlgorithm to bypass public key detection when the provided algorithm starts with 'H'
  • Added test to verify public key patterns in HMAC secrets are accepted when HS256 is specified
  • Updated existing HS algorithm tests to use a shared key constant from privateKeys.HS

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/crypto.js Added early return in performDetectPrivateKeyAlgorithm to skip public key validation for algorithms starting with 'H', preventing false rejection of HMAC secrets
test/crypto.spec.js Added test case verifying HMAC secrets with public key patterns are accepted, and standardized key usage in HS algorithm round-trip tests

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread test/crypto.spec.js Outdated
Comment thread src/crypto.js Outdated
@simoneb
Copy link
Copy Markdown
Member

simoneb commented Jan 15, 2026

@SociableSteve can you take a look?

@SociableSteve
Copy link
Copy Markdown
Contributor

SociableSteve commented Jan 15, 2026

@hf Many thanks for this and apologies for the delay getting to this over the holiday period. If you can update to cover HS384 and HS512 tests for completeness, and restrict the check to the defined algorithms in case of future clashes (pretty much as CoPilot has suggested) then we will get this merged in.

@SociableSteve SociableSteve self-requested a review January 15, 2026 16:42
hf and others added 4 commits January 19, 2026 10:29
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@hf
Copy link
Copy Markdown
Author

hf commented Jan 21, 2026

Sorry about the delay, it's now ready.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/crypto.js
Comment thread test/crypto.spec.js
Comment on lines +146 to +150
test('detectPrivateKeyAlgorithm - public keys should be accepted if HS256, HS384, HS512 is used', t => {
;['HS256', 'HS384', 'HS512'].forEach(algorithm => {
t.assert.equal(detectPrivateKeyAlgorithm(`-----BEGIN PUBLIC KEY-----\nUSED IN ${algorithm}`, algorithm), algorithm)
})
})
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test doesn't adequately test the caching behavior with the new changes. Each iteration uses a different key string (by including the algorithm in the key), so it doesn't test whether the same key can be correctly used with different HS algorithms.

Consider adding a test that calls detectPrivateKeyAlgorithm with the same key but different algorithms to ensure the function returns the correct algorithm for each call. For example:

  • Call with a key and HS256
  • Call with the same key and HS384
  • Verify both calls return the correct algorithm respectively

Copilot uses AI. Check for mistakes.
Comment thread test/crypto.spec.js
const hsAlgorithm = `HS${bits}`
const key = privateKeys.HS

// HS256, HS512, HS512
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment has a typo: "HS256, HS512, HS512" should be "HS256, HS384, HS512" to correctly reflect the three hash algorithms being tested.

Suggested change
// HS256, HS512, HS512
// HS256, HS384, HS512

Copilot uses AI. Check for mistakes.
@SociableSteve
Copy link
Copy Markdown
Contributor

It looks like this fix works for signing tokens but not for verifying them. Simple strings work in both cases, but a token formatted like the following doesn't work when creating a verifier:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo
4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u
-----END PUBLIC KEY-----

The code used to test was:

const { createSigner, createVerifier } = require('fast-jwt')

// This is a valid HMAC secret - it just happens to look like a PEM public key.
// This can occur when secrets are base64-encoded or generated from key material.
const secret = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo
4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u
-----END PUBLIC KEY-----`

// Signing works (fixed by PR #576)
const signer = createSigner({ algorithm: 'HS256', key: secret })
const token = signer({ userId: 123, role: 'admin' })
console.log('Token created:', token)

// Verification fails - even though we explicitly specify HS256
const verifier = createVerifier({ algorithms: ['HS256'], key: secret })
const payload = verifier(token)
console.log('Payload:', payload)

Verification uses the public key methods for validation of the key.

function: https://github.com/hf/fast-jwt/blob/bafb8f42aaeeb30fcb66960224e4722a34d5dba0/src/verifier.js#L367
next call: https://github.com/hf/fast-jwt/blob/bafb8f42aaeeb30fcb66960224e4722a34d5dba0/src/verifier.js#L405

function: https://github.com/hf/fast-jwt/blob/bafb8f42aaeeb30fcb66960224e4722a34d5dba0/src/crypto.js#L209
next call: https://github.com/hf/fast-jwt/blob/bafb8f42aaeeb30fcb66960224e4722a34d5dba0/src/crypto.js#L229

function: https://github.com/hf/fast-jwt/blob/bafb8f42aaeeb30fcb66960224e4722a34d5dba0/src/crypto.js#L131

This needs the same fix applying that you have in the private key checker to ensure that generated tokens can be verified. There is an additional complexity that the algorithms is an array instead of a single entry since the library can be used to verify against multiple different signing keys in a single call.

Copy link
Copy Markdown
Contributor

@SociableSteve SociableSteve left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per previous comment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: fast-jwt ignores algorithm indication and disallows use of HMAC keys that look like a public/private PEM key

5 participants