feat(validation): allow to restrict valid algs

This commit is contained in:
cduvray 2023-11-18 08:56:44 +01:00
parent 5f148d3631
commit 14f4a6dcc3
3 changed files with 45 additions and 3 deletions

View file

@ -228,7 +228,7 @@ where
let header = decode_header(token)?; let header = decode_header(token)?;
// TODO: (optimisation) build & store jwt_validation in key data, to avoid rebuilding it for each check // TODO: (optimisation) build & store jwt_validation in key data, to avoid rebuilding it for each check
let val_key = self.key_source.get_key(header).await?; let val_key = self.key_source.get_key(header).await?;
let jwt_validation = &self.validation.to_jwt_validation(val_key.algs.clone()); let jwt_validation = &self.validation.to_jwt_validation(&val_key.algs);
let token_data = decode::<C>(token, &val_key.key, jwt_validation)?; let token_data = decode::<C>(token, &val_key.key, jwt_validation)?;
if let Some(ref checker) = self.claims_checker { if let Some(ref checker) = self.claims_checker {

View file

@ -30,6 +30,11 @@ pub struct Validation {
/// ///
/// Defaults to true. /// Defaults to true.
pub validate_signature: bool, pub validate_signature: bool,
/// Accepted algorithms
///
/// If empty anly the algorithms matching key will be authorized
pub algs: Vec<Algorithm>,
} }
impl Validation { impl Validation {
@ -82,7 +87,17 @@ impl Validation {
self self
} }
pub(crate) fn to_jwt_validation(&self, alg: Vec<Algorithm>) -> jsonwebtoken::Validation { /// Authorized algorithms.
///
/// If no algs are supplied default algs for the key will be used
/// (example for a EC key, algs = [ES256, ES384]).
pub fn algs(mut self, algs: Vec<Algorithm>) -> Self {
self.algs = algs;
self
}
pub(crate) fn to_jwt_validation(&self, default_algs: &Vec<Algorithm>) -> jsonwebtoken::Validation {
let required_claims = if self.validate_exp { let required_claims = if self.validate_exp {
let mut claims = HashSet::with_capacity(1); let mut claims = HashSet::with_capacity(1);
claims.insert("exp".to_owned()); claims.insert("exp".to_owned());
@ -103,7 +118,11 @@ impl Validation {
jwt_validation.iss = iss; jwt_validation.iss = iss;
jwt_validation.aud = aud; jwt_validation.aud = aud;
jwt_validation.sub = None; jwt_validation.sub = None;
jwt_validation.algorithms = alg; jwt_validation.algorithms = if self.algs.is_empty() {
default_algs.clone()
} else {
self.algs.clone()
};
if !self.validate_signature { if !self.validate_signature {
jwt_validation.insecure_disable_signature_validation(); jwt_validation.insecure_disable_signature_validation();
} }
@ -124,6 +143,7 @@ impl Default for Validation {
aud: None, aud: None,
validate_signature: true, validate_signature: true,
algs: vec![],
} }
} }
} }

View file

@ -12,6 +12,7 @@ mod tests {
BoxError, Router, BoxError, Router,
}; };
use http::{header, HeaderValue}; use http::{header, HeaderValue};
use jsonwebtoken::Algorithm;
use jwt_authorizer::{ use jwt_authorizer::{
authorizer::Authorizer, authorizer::Authorizer,
layer::{AuthorizationLayer, JwtSource}, layer::{AuthorizationLayer, JwtSource},
@ -374,6 +375,27 @@ mod tests {
assert_eq!(response.status(), StatusCode::OK); assert_eq!(response.status(), StatusCode::OK);
} }
#[tokio::test]
async fn validate_algs() {
// OK
let response = make_proteced_request(
JwtAuthorizer::from_rsa_pem("../config/rsa-public1.pem")
.validation(Validation::new().algs(vec![Algorithm::RS256, Algorithm::RS384])),
common::JWT_RSA1_OK,
)
.await;
assert_eq!(response.status(), StatusCode::OK);
// NOK - Invalid Alg
let response = make_proteced_request(
JwtAuthorizer::from_rsa_pem("../config/rsa-public1.pem")
.validation(Validation::new().algs(vec![Algorithm::RS512])),
common::JWT_RSA1_OK,
)
.await;
assert_eq!(response.status(), StatusCode::OK);
}
// -------------------- // --------------------
// jwt_source // jwt_source
// --------------------- // ---------------------