Skip to main content

Validate JOSE objects safely

Separate JWT claims, JWS signatures, JWE encryption, and JWK key data and apply a fixed validation policy.

Learning outcomes

  • Separate JWT, JWS, JWE, JWK, and JWKS roles.
  • Build a validation policy before parsing attacker-controlled headers and claims.
  • Validate cryptography, issuer, audience, time, token type, and application context.
  • Test algorithm, key-source, nesting, claim, and replay failures.

Protocol roles

JWT defines a compact claims object. JWS provides a digital signature or message authentication code over bytes. JWE provides encryption and integrity protection. JWK represents key parameters, and JWKS is a set of JWK values. These building blocks are not interchangeable.

A JWT can be carried in a JWS or JWE. A signed JWT is not encrypted. An encrypted JWT is not necessarily signed by the expected issuer unless the profile requires and validates nested protection. A decoded JSON object is not a validated security object.

RFC 8725 gives current JWT implementation guidance. It requires algorithm verification, validates all cryptographic operations, recommends explicit typing, and requires mutually exclusive validation rules when different token kinds could be confused.

Message flow

  1. The application selects a validation profile from trusted local context: expected token kind, issuers, audiences, algorithms, key sources, required claims, time policy, and replay policy.
  2. It parses the compact serialization with strict size and structure limits. It rejects duplicate or malformed JSON members according to its library and profile behavior.
  3. It validates protected header fields. The configured algorithm must match the key and use. The token cannot choose an otherwise prohibited algorithm or arbitrary key source.
  4. For JWS, the verifier gets candidate keys from the approved issuer or local key set and verifies the signature over the exact encoded input. For JWE, the recipient validates algorithms, decrypts, and verifies the authenticated result.
  5. It validates claims and application bindings: exact issuer, intended audience, expiry, not-before, acceptable issue time, subject semantics, token type, nonce or identifier, and any protocol-specific authorized-party or confirmation claim.
  6. It applies replay controls when the token or enclosing protocol requires one-time use.
  7. Only then does the application map the validated result into a local identity and make its own authorization decision.

Validation and failure cases

Create different validation functions for ID tokens, access tokens, logout tokens, identity assertions, and other token types. Each function has a fixed allowed algorithm set, issuer set, audience, key source, and required claims. Do not use a permissive general decoder and decide the type from untrusted claims after validation.

Reject:

  • alg=none or any algorithm not explicitly allowed for that token profile.
  • Symmetric-versus-asymmetric algorithm confusion and a key with incompatible type, use, or operation.
  • A remote key URL or embedded key that local policy did not authorize.
  • An invalid signature, failed authenticated decryption, or unvalidated inner object.
  • Wrong issuer, audience, authorized party, subject context, or token type.
  • Expired, premature, implausibly old, or otherwise invalid time claims under the profile.
  • Missing required claims, duplicate critical values, malformed numeric dates, or unsupported critical headers.
  • A replayed nonce, code, token identifier, or proof where the protocol requires uniqueness.

Fuzz the parser with extra segments, invalid base64url, duplicate JSON keys, very large fields, nested objects, and compression where supported. All libraries in the request path must agree on the accepted object.

Design tradeoffs and residual risk

Self-contained signed objects reduce online issuer calls and improve distribution. They remain valid until expiry unless verifiers consult status or receive revocation state. Encryption hides claims from intermediaries but adds key management and does not remove the need for issuer authentication and claim validation.

Shared validation libraries improve consistency. A broad library configuration can also make one token type acceptable in another context. Use small profile-specific entry points and test them with known invalid objects.

Residual risk includes issuer-key compromise, stale JWKS caches, token theft, unsafe claim-to-account mapping, parser differences, and valid tokens used for unauthorized objects. Cryptographic validity proves origin and integrity under a key. It does not prove business permission.

Pomerium boundary

Pomerium can send a signed identity assertion to an upstream application. The application must validate the documented signature, issuer, audience, time, and trusted key source before it uses the claims. It still owns account mapping and object authorization. Pomerium route policy does not make an application's generic JWT decoder safe.

Exercise

Take one accepted token profile and write its validation contract before using a library: exact type, serialization, issuer, audience, algorithms, JWKS source, required claims, clock skew, lifetime, and replay rule. Build a negative corpus with one mutation for each validation and failure case.

Run the corpus through the production validation entry point. Confirm that only the valid fixture reaches identity mapping. Record the library version and rerun the corpus on every security or major dependency update.

Evaluation checklist

  • Can the implementation explain whether each object is JWT, JWS, JWE, JWK, or JWKS?
  • Does trusted local context select a narrow validation profile before claims are trusted?
  • Are algorithm, key source, signature or decryption, issuer, audience, type, and time all checked?
  • Are different token kinds prevented from validating under one another's rules?
  • Does the negative corpus cover parsing, algorithm, key, claim, nesting, and replay failures?

Next learning unit

JWK and JWKS

Select trusted JSON Web Keys from an approved set, restrict algorithms and key use, and rotate without trusting token-controlled URLs.

Sources and further reading

Keep learning

Application and Service AccessStandards and Protocols

Signed Header

Pomerium's signed header is the X-Pomerium-Jwt-Assertion header.

Learn this term

Get a Personalized Demo

Schedule a Call with a Pomerium Engineer

Get a Demo