use crate::{types::{Jwt, Algorithm}, jwk::{Key, Jwks}, time::{Leeway, validate_claim_times}, Error, Result};
#[derive(Debug, Clone)]
pub struct VerifyOptions {
pub validate_times: bool,
pub leeway: Leeway,
pub expected_iss: Option<String>,
pub expected_aud: Option<String>,
pub expected_alg: Option<Algorithm>,
pub now_ts: Option<i64>,
}
impl Default for VerifyOptions {
fn default() -> Self {
Self {
validate_times: true,
leeway: Leeway { seconds: 0 },
expected_iss: None,
expected_aud: None,
expected_alg: None,
now_ts: None,
}
}
}
impl VerifyOptions {
pub fn validate_times(mut self, v: bool) -> Self { self.validate_times = v; self }
pub fn leeway(mut self, s: i64) -> Self { self.leeway = Leeway { seconds: s }; self }
pub fn expect_iss(mut self, iss: impl Into<String>) -> Self { self.expected_iss = Some(iss.into()); self }
pub fn expect_aud(mut self, aud: impl Into<String>) -> Self { self.expected_aud = Some(aud.into()); self }
pub fn expect_alg(mut self, alg: Algorithm) -> Self { self.expected_alg = Some(alg); self }
pub fn now_ts(mut self, ts: i64) -> Self { self.now_ts = Some(ts); self }
}
fn to_jalg(a: Algorithm) -> jsonwebtoken::Algorithm {
use Algorithm::*;
match a {
HS256 => jsonwebtoken::Algorithm::HS256,
HS384 => jsonwebtoken::Algorithm::HS384,
HS512 => jsonwebtoken::Algorithm::HS512,
RS256 => jsonwebtoken::Algorithm::RS256,
RS384 => jsonwebtoken::Algorithm::RS384,
RS512 => jsonwebtoken::Algorithm::RS512,
ES256 => jsonwebtoken::Algorithm::ES256,
ES384 => jsonwebtoken::Algorithm::ES384,
ES512 => jsonwebtoken::Algorithm::ES384,
EdDSA => jsonwebtoken::Algorithm::EdDSA,
}
}
pub fn verify_with_key(jwt: &Jwt, key: &Key, opts: &VerifyOptions) -> Result<()> {
use jsonwebtoken::{Validation, DecodingKey, decode};
if let Some(expected_alg) = opts.expected_alg {
if jwt.header.alg != expected_alg {
return Err(Error::Claims(format!("algorithm mismatch: expected {:?}, got {:?}", expected_alg, jwt.header.alg)));
}
}
match jwt.header.alg {
Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => { #[cfg(not(feature="hs"))] return Err(Error::DisabledAlg("HS")); }
Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 => { #[cfg(not(feature="rs"))] return Err(Error::DisabledAlg("RS")); }
Algorithm::ES256 | Algorithm::ES384 | Algorithm::ES512 => { #[cfg(not(feature="es"))] return Err(Error::DisabledAlg("ES")); }
Algorithm::EdDSA => { #[cfg(not(feature="eddsa"))] return Err(Error::DisabledAlg("EdDSA")); }
}
let mut v = Validation::new(to_jalg(jwt.header.alg));
v.validate_exp = false;
v.insecure_disable_signature_validation();
if let Some(ref iss) = opts.expected_iss { v.set_issuer(&[iss.clone()]); }
if let Some(ref aud) = opts.expected_aud { v.set_audience(&[aud.clone()]); }
let dkey = match key {
Key::Hs(s) => DecodingKey::from_secret(s),
Key::RsaPublicPem(p) => DecodingKey::from_rsa_pem(p).map_err(|e| Error::Key(e.to_string()))?,
Key::EcPublicPem(p) => DecodingKey::from_ec_pem(p).map_err(|e| Error::Key(e.to_string()))?,
Key::EdPublicPem(p) => DecodingKey::from_ed_pem(p).map_err(|e| Error::Key(e.to_string()))?,
_ => return Err(Error::Key("public or shared secret required to verify".into())),
};
let token_str = format!("{}.{}.{}", jwt.raw_header_b64, jwt.raw_payload_b64, jwt.signature_b64);
let data = decode::<serde_json::Value>(&token_str, &dkey, &v).map_err(|e| Error::Signature(e.to_string()))?;
if opts.validate_times {
let now = opts.now_ts.unwrap_or_else(|| time::OffsetDateTime::now_utc().unix_timestamp());
let map = data.claims.as_object().cloned().unwrap_or_default();
validate_claim_times(&map, true, opts.leeway, now)?;
}
Ok(())
}
impl Jwt {
pub fn verify(&self, key: &Key, opts: VerifyOptions) -> Result<()> {
verify_with_key(self, key, &opts)
}
pub fn verify_with_jwks(&self, jwks: &Jwks, opts: VerifyOptions) -> Result<()> {
let kid = self.header.kid.clone().ok_or(crate::Error::MissingKid)?;
let key = jwks.select_for(&kid, self.header.alg)?;
verify_with_key(self, &key, &opts)
}
}