Skip to content

Fix JWT header validation for tokens with kid field but missing typ field#19

Open
jroark-envoy wants to merge 1 commit intokataras:mainfrom
jroark-envoy:fix-header-validation-issue-13
Open

Fix JWT header validation for tokens with kid field but missing typ field#19
jroark-envoy wants to merge 1 commit intokataras:mainfrom
jroark-envoy:fix-header-validation-issue-13

Conversation

@jroark-envoy
Copy link

Fix JWT header validation for tokens with kid field but missing typ field

Fixes #13

This PR resolves the JWT validation issue reported in #13 where tokens with additional header fields (like kid) fail validation when the optional typ field is missing.

Problem Description

The current compareHeader function in token.go incorrectly assumes that any JWT header without the typ field will be exactly 15 bytes long. This assumption breaks when headers contain additional fields like:

  • kid (Key ID) - commonly used by services like Cloudflare Zero Trust
  • iss (Issuer)
  • jku (JWK Set URL)
  • Any other optional header fields

Example failing header:

{"kid":"test-key-id","alg":"ES384"}

This is 34 bytes, not 15, so it bypasses the "no typ field" check and fails validation.

Root Cause

In token.go lines 216-224:

if n := len(headerDecoded); n < 25 {
    if n == 15 { // header without "typ": "JWT".
        expectedHeader := createHeaderWithoutTyp(alg)
        if bytes.Equal(expectedHeader, headerDecoded) {
            return nil, nil, nil, nil
        }
    }
    return nil, nil, nil, ErrTokenAlg
}

The hardcoded n == 15 check only handles the specific case of {"alg":"XXX"} but not headers with additional fields.

Solution

This fix:

  1. Maintains Performance: Preserves the existing fast-path byte comparison for standard JWT formats
  2. Adds Robust Fallback: When byte comparison fails, falls back to proper JSON parsing
  3. Validates Correctly: Ensures algorithm matches and allows typ to be missing or "JWT"
  4. Supports Extensions: Handles headers with any additional fields per JWT specification
  5. Backward Compatible: All existing functionality continues to work unchanged

Changes Made

  • Added encoding/json import
  • Enhanced compareHeader function with JSON parsing fallback
  • Updated comment to clarify the 15-byte assumption
  • Added comprehensive validation logic for non-standard header formats

Testing

The fix has been validated with:

Performance Impact

  • Zero impact for standard JWT formats (fast path unchanged)
  • Minimal overhead only when encountering non-standard headers that require JSON parsing
  • Overall improvement in compatibility without sacrificing performance

Related Issues

@jroark-envoy jroark-envoy requested a review from kataras as a code owner June 17, 2025 23:45
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.

"Unexpected token algorithm" error when validating CloudFlare's JWT

1 participant