Skip to content

JSON Error When Decoding JWT with Nested Object in sub Claim #427

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
richardriman opened this issue May 1, 2025 · 2 comments
Open

JSON Error When Decoding JWT with Nested Object in sub Claim #427

richardriman opened this issue May 1, 2025 · 2 comments

Comments

@richardriman
Copy link

richardriman commented May 1, 2025

JSON Error When Decoding JWT with Nested Object in sub Claim

When decoding a JWT with a nested object in the sub claim, jsonwebtoken fails with the error Error::Json("expected ',' or '}'", line: 1, column: 8), despite the JSON payload being valid and manually deserializable.

Steps to Reproduce

  1. Use jsonwebtoken version 9.3.0 (also tested with 8.x).
  2. Define a claim struct with a nested object in sub:
    use serde::{Serialize, Deserialize};
    
    #[derive(Debug, Serialize, Deserialize)]
    #[serde(rename_all = "camelCase")]
    struct SubClaims {
        user_id: i32,
        tenant_name: String,
        tenant_id: i32,
    }
    
    #[derive(Debug, Serialize, Deserialize)]
    struct Claims {
        sub: SubClaims,
        exp: usize,
        iss: String,
        aud: String,
    }
  3. Encode a token with HS256 (or HS512):
    use jsonwebtoken::{encode, Header, Algorithm, EncodingKey, Validation};
    
    let claims = Claims {
        sub: SubClaims {
            user_id: 105,
            tenant_name: "test".to_string(),
            tenant_id: 1,
        },
        exp: 10000000000,
        iss: "Issuer".to_string(),
        aud: "Audience".to_string(),
    };
    let secret = "SOME SECRET";
    let token = encode(
        &Header::new(Algorithm::HS512),
        &claims,
        &EncodingKey::from_secret(secret.as_ref()),
    ).unwrap();
  4. Attempt to decode:
    use jsonwebtoken::{decode, DecodingKey};
    
    let mut validation = Validation::new(Algorithm::HS512);
    validation.set_audience(&["Audience"]);
    validation.set_issuer(&["Issuer"]);
    let claims = decode::<Claims>(
        &token,
        &DecodingKey::from_secret(secret.as_ref()),
        &validation,
    );

Expected Behavior

The token should decode successfully into the Claims struct.

Actual Behavior

Decoding fails with Error::Json("expected ',' or '}'", line: 1, column: 8).

Additional Notes

  • Manual deserialization of the payload using serde_json::from_str and serde_json::from_slice works correctly:
    use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
    let parts: Vec<&str> = token.split('.').collect();
    let payload = parts[1];
    let decoded = URL_SAFE_NO_PAD.decode(payload).unwrap();
    let payload_str = String::from_utf8(decoded).unwrap();
    let claims = serde_json::from_str::<Claims>(&payload_str).unwrap(); // Works
    let claims = serde_json::from_slice::<Claims>(&decoded).unwrap(); // Works
  • Decoding works with a simple sub claim (e.g., sub: String):
    #[derive(Debug, Serialize, Deserialize)]
    struct SimpleClaims {
        sub: String,
        exp: usize,
        iss: String,
        aud: String,
    }
  • The issue persists with HS256, HS512, and older versions of jsonwebtoken (8.x).
  • The library uses URL_SAFE_NO_PAD for Base64 decoding (correct) and serde_json::from_slice for deserialization.
  • The raw bytes of the payload are valid UTF-8 and match the JSON:
    Payload: {"sub":{"userId":105,"tenantName":"info","tenantId":1},"exp":10000000000,"iss":"Issuer","aud":"Audience"}
    

Environment

  • Rust version: 1.82.0
  • jsonwebtoken: 9.3.0 (also tested with 8.x)
  • serde: 1.0.210
  • serde_json: 1.0.128
  • base64: 0.22

Possible Cause

The issue likely stems from jsonwebtoken passing an incorrect or truncated byte slice to serde_json::from_slice when deserializing a nested object in sub, or from a deserialization incompatibility with complex structures.

Request

Please investigate why jsonwebtoken::decode fails with nested objects in sub and provide a fix or clarification on whether complex sub claims are supported.

@L-Chao
Copy link

L-Chao commented May 9, 2025

currently, only string-type sub is supported.

#[derive(Deserialize)]
pub(crate) struct ClaimsForValidation<'a> {
    ...
    #[serde(borrow)]
    sub: TryParse<Cow<'a, str>>,
   ...
}

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

No branches or pull requests

2 participants