Skip to content

fix: accept non-string values in Header extras#496

Closed
kp-timo-beyel wants to merge 1 commit into
Keats:masterfrom
kp-timo-beyel:fix/header-extras-non-string-values
Closed

fix: accept non-string values in Header extras#496
kp-timo-beyel wants to merge 1 commit into
Keats:masterfrom
kp-timo-beyel:fix/header-extras-non-string-values

Conversation

@kp-timo-beyel
Copy link
Copy Markdown

Summary

  • Change Header.extras from HashMap<String, String> to HashMap<String, serde_json::Value>
  • decode_header / decode now accept JWT headers with non-string custom fields (e.g. "uid": 180444)

Problem

When a JWT header contains a non-standard field with a non-string value such as:

{"typ": "JWT", "alg": "RS256", "kid": "...", "uid": 180444}

decode_header fails with a deserialization error because serde cannot coerce the integer 180444 into a String for the #[serde(flatten)] pub extras: HashMap<String, String> field.

JOSE headers can legitimately carry arbitrary JSON values in custom fields, so the extras map should accept any JSON value.

Fix

Change the type of Header.extras from HashMap<String, String> to HashMap<String, serde_json::Value>.

Breaking change

Consumers that read from Header.extras directly now receive serde_json::Value instead of String. For string extras, .as_str().unwrap() (or pattern matching) is needed:

// Before
let v: &String = header.extras.get("custom").unwrap();

// After  
let v: &str = header.extras.get("custom").unwrap().as_str().unwrap();

Test plan

  • Existing tests updated and passing
  • New test decode_token_with_non_string_extra_header verifies integer header values are accepted
  • Example and benchmark updated

…ing, serde_json::Value>

The `extras` field on `Header` used `HashMap<String, String>`, which
caused `decode_header` and `decode` to fail when the JWT header contained
non-string values (e.g. `"uid": 180444`). Since JOSE headers can
legitimately carry arbitrary JSON values in custom fields, this changes
the type to `HashMap<String, serde_json::Value>` so that integers,
booleans, arrays, and objects are accepted.

This is a breaking change for consumers that read from `Header.extras`
directly — they now receive `serde_json::Value` instead of `String`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
kageiit added a commit to gitarcode/clerk-rs that referenced this pull request May 12, 2026
Clerk recently started adding `cat` (string) and `oiat` (integer) fields to JWT
headers. jsonwebtoken v10's Header.extras uses HashMap<String, String> which
rejects non-string values, causing all JWT validation to fail with deserialization
errors.

Fix: use gitarcode/jsonwebtoken fork with HashMap<String, serde_json::Value> for
extras (upstream PR Keats/jsonwebtoken#496). Also cherry-picks upstream clerk-rs
PR DarrenBaldwin07#282 (jsonwebtoken 10) and PR DarrenBaldwin07#280 (nbf optional).
kageiit added a commit to gitarcode/clerk-rs that referenced this pull request May 12, 2026
Clerk recently started adding `cat` (string) and `oiat` (integer) fields to JWT
headers. jsonwebtoken v10's Header.extras uses HashMap<String, String> which
rejects non-string values, causing all JWT validation to fail with deserialization
errors.

Fix: use gitarcode/jsonwebtoken fork with HashMap<String, serde_json::Value> for
extras (upstream PR Keats/jsonwebtoken#496). Also cherry-picks upstream clerk-rs
PR DarrenBaldwin07#282 (jsonwebtoken 10) and PR DarrenBaldwin07#280 (nbf optional).

Co-authored-by: Gautam Korlam <gautam@gitar.ai>
@arckoor
Copy link
Copy Markdown
Collaborator

arckoor commented May 17, 2026

Done in #512

@arckoor arckoor closed this May 17, 2026
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.

2 participants