GHSA-jhm7-29pj-4xvf

Suggest an improvement
Source
https://github.com/advisories/GHSA-jhm7-29pj-4xvf
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/04/GHSA-jhm7-29pj-4xvf/GHSA-jhm7-29pj-4xvf.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-jhm7-29pj-4xvf
Aliases
Published
2026-04-16T21:09:50Z
Modified
2026-05-05T16:11:45Z
Severity
  • 5.9 (Medium) CVSS_V3 - CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N CVSS Calculator
Summary
@node-oauth/oauth2-server: PKCE code_verifier ABNF not enforced in token exchange allows brute-force redemption of intercepted authorization codes
Details

Summary

The token exchange path accepts RFC7636-invalid code_verifier values (including one-character strings) for S256 PKCE flows.
Because short/weak verifiers are accepted and failed verifier attempts do not consume the authorization code, an attacker who intercepts an authorization code can brute-force code_verifier guesses online until token issuance succeeds.

Root cause

  1. lib/pkce/pkce.js (getHashForCodeChallenge) only checks that verifier is a non-empty string before hashing for S256; it does not enforce RFC7636 ABNF (43..128 unreserved chars).
  2. lib/grant-types/authorization-code-grant-type.js compares hash(code_verifier) to stored codeChallenge without validating verifier format/length.
  3. In AuthorizationCodeGrantType.handle, authorization code revocation happens after verifier validation. Invalid guesses fail before revoke, so the same code can be retried repeatedly.

Steps to Reproduce

Setup

  • PKCE authorization code exists with:
    • codeChallengeMethod = "S256"
    • codeChallenge = BASE64URL(SHA256("z")) (verifier is one character, RFC-invalid)
  • Attacker has intercepted the authorization code value.

Reproduction

  1. Send repeated token requests with guessed code_verifier values:
POST /token HTTP/1.1
Host: oauth.example
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
client_id=client1&
client_secret=s3cret&
code=stolen-auth-code&
redirect_uri=https://client.example/callback&
code_verifier=<guess>
  1. Observe invalid guesses return invalid_grant.
  2. Continue guessing (a..z).
  3. When code_verifier=z, token issuance succeeds and returns bearer tokens.

Confirmed PoC output

BRUTE_FORCE_SUCCESS { tries: 26, guess: 'z', status: 200, tokenIssued: true }

Impact

An intercepted authorization code can be redeemed by brute-forcing low-entropy verifiers that the server should have rejected under RFC7636.
This weakens PKCE’s protection goal and allows token theft when clients generate short/predictable verifiers.

Recommended Fix

  1. Enforce pkce.codeChallengeMatchesABNF(request.body.code_verifier) in authorization code token exchange before hashing/comparison.
  2. Reject verifier values outside RFC7636 charset/length (43..128 unreserved).
  3. Invalidate authorization codes on failed verifier attempts (or add strict retry limits) to prevent online guessing.
Database specific
{
    "cwe_ids":  [
        "CWE-1289",
        "CWE-307"
    ],
    "github_reviewed":  true,
    "github_reviewed_at":  "2026-04-16T21:09:50Z",
    "nvd_published_at":  "2026-04-23T19:17:29Z",
    "severity":  "MODERATE"
}
References

Affected packages

npm / @node-oauth/oauth2-server

Package

Name
@node-oauth/oauth2-server
View open source insights on deps.dev
Purl
pkg:npm/%40node-oauth/oauth2-server

Affected ranges

Type
SEMVER
Events
Introduced
0 Unknown introduced version / All previous versions are affected
Fixed
5.3.0

Database specific

last_known_affected_version_range
"<= 5.2.1"
source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/04/GHSA-jhm7-29pj-4xvf/GHSA-jhm7-29pj-4xvf.json"