TokenValidator

Struct TokenValidator 

Source
pub struct TokenValidator { /* private fields */ }
Expand description

JWT token validator with builder pattern

This validator collects all validation configuration upfront, then executes the validation steps in the correct order when run() is called.

§Validation Flow

  1. Parse token (done before creating validator)
  2. Check basic integrity (header format, algorithm)
  3. Validate issuer (prevent SSRF)
  4. Verify signature (cryptographic verification)
  5. Validate claims (exp, nbf, iat, aud, etc.)

§Example

use jwtiny::*;

let token = ParsedToken::from_string("eyJ...")?;

let validated = TokenValidator::new(token)
    .ensure_issuer(|iss| {
        if iss == "https://trusted.com" {
            Ok(())
        } else {
            Err(Error::IssuerNotTrusted(iss.to_string()))
        }
    })
    .verify_signature(
        SignatureVerification::with_secret_hs256(b"my-secret")
    )
    .validate_token(
        ValidationConfig::default()
            .require_audience("my-api")
    )
    .run()?;

println!("Subject: {:?}", validated.subject());

Implementations§

Source§

impl TokenValidator

Source

pub fn new(parsed: ParsedToken) -> Self

Create a new validator from a parsed token

Note: TokenValidator is not Clone by design. Each validator is bound to a specific token to prevent accidental reuse with the wrong token data. However, validation configurations (SignatureVerification, ValidationConfig) are Clone and can be reused across multiple validators.

§Example
let token = ParsedToken::from_string("eyJ...")?;
let validator = TokenValidator::new(token);
§Validating Multiple Tokens

To validate multiple tokens with the same configuration:

// Prepare reusable configuration
let signature = SignatureVerification::with_secret_hs256(b"secret");
let validation = ValidationConfig::default().require_audience("api");
let issuer_check = |iss: &str| Ok(iss == "https://trusted.com");

// Validate each token with the same config
for token_str in tokens {
    let parsed = ParsedToken::from_string(&token_str)?;
    let token = TokenValidator::new(parsed)
        .ensure_issuer(issuer_check)
        .verify_signature(signature.clone())
        .validate_token(validation.clone())
        .run()?;
    // Use token...
}
Source

pub fn ensure_issuer<F>(self, validator: F) -> Self
where F: Fn(&str) -> Result<()> + Send + Sync + 'static,

Ensure the issuer is trusted

Validates the iss claim before proceeding with signature verification. Critical for preventing SSRF attacks when using JWKS fetching: an attacker can craft a token with an arbitrary iss claim, causing your application to fetch keys from attacker-controlled URLs.

The validator function receives the issuer string and should return:

  • Ok(()) if the issuer is trusted
  • Err(...) if the issuer is not trusted
§Note

The validator function must be Fn (not FnOnce) and should not mutate captured state. This allows the validator to be used multiple times if needed.

§Example
validator.ensure_issuer(|iss| {
    if iss == "https://auth.example.com" {
        Ok(())
    } else {
        Err(Error::IssuerNotTrusted(iss.to_string()))
    }
})
Source

pub fn danger_skip_issuer_validation(self) -> Self

⚠️ DANGER: Skip issuer validation (use with extreme caution!)

This bypasses issuer validation, which is a critical security check. Only use this if:

  • You’re providing the signing key directly (not fetching from JWKS)
  • You’re validating the issuer through other means
  • You fully understand the security implications
§Security Warning

NEVER skip issuer validation when using JWKS! This enables SSRF attacks where attackers can make your application fetch keys from arbitrary URLs. For JWKS-based validation, you MUST use ensure_issuer().

The method name includes “danger” to make it clear this is a security-critical bypass that should only be used in specific, well-understood scenarios.

Source

pub fn verify_signature(self, verification: SignatureVerification) -> Self

Configure signature verification

This specifies how the token signature should be verified. Options:

  • Symmetric key (HMAC algorithms)
  • Asymmetric public key (RSA/ECDSA algorithms)
  • HTTP client for JWKS fetching (future feature)
§Example
// With symmetric key (HMAC) - algorithm-specific constructor
validator.verify_signature(
    SignatureVerification::with_secret_hs256(b"my-secret")
)

// With public key (RSA) - must specify policy
validator.verify_signature(
    SignatureVerification::with_key(
        Key::rsa_public(der_bytes),
        AlgorithmPolicy::rs256_only()
    )
)

// Or use convenience constructor for RSA
validator.verify_signature(
    SignatureVerification::with_rsa_rs256(der_bytes)
)
Source

pub fn validate_token(self, config: ValidationConfig) -> Self

Configure claims validation

This specifies how the token claims should be validated. You can:

  • Validate time-based claims (exp, nbf, iat)
  • Validate audience (aud)
  • Set clock skew tolerance
  • Add custom validation logic
§Example
validator.validate_token(
    ValidationConfig::default()
        .require_audience("my-api")
        .max_age(3600)
        .clock_skew(60)
)
Source

pub fn danger_skip_claims_validation(self) -> Self

⚠️ DANGER: Skip claims validation (use with extreme caution!)

This bypasses claims validation, including critical checks like:

  • Token expiration (exp)
  • Not-before time (nbf)
  • Audience (aud)
§Security Warning

Skipping claims validation means you accept expired or not-yet-valid tokens. Only use this if you’re performing custom validation on the claims yourself and you fully understand the security implications.

The method name includes “danger” to make it clear this is a security-critical bypass that should only be used in specific, well-understood scenarios.

Source

pub fn run(self) -> Result<Token>

Run the validation pipeline

This executes all validation steps in the correct order:

  1. Check basic integrity (algorithm validation)
  2. Validate issuer (if configured)
  3. Verify signature (if configured)
  4. Validate claims (if configured)

Returns a fully validated Token that is safe to use.

§Errors

Returns an error if any validation step fails:

  • Error::UnsupportedAlgorithm - Invalid or unsupported algorithm
  • Error::IssuerNotTrusted - Issuer validation failed
  • Error::SignatureInvalid - Signature verification failed
  • Error::ClaimValidationFailed - Claims validation failed
  • Error::MissingKey - No key provided for signature verification
§Example
let token = validator.run()?;
println!("Token validated! Subject: {:?}", token.subject());

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.