From 70ce996275f6eaffc1a9a4e73e8946900db2a1d1 Mon Sep 17 00:00:00 2001 From: cduvray Date: Thu, 6 Jul 2023 07:47:22 +0200 Subject: [PATCH] fix(claims): aud can a string or an array of strings fixes #26 --- jwt-authorizer/src/claims.rs | 57 +++++++++++++----------------------- jwt-authorizer/src/lib.rs | 2 +- 2 files changed, 21 insertions(+), 38 deletions(-) diff --git a/jwt-authorizer/src/claims.rs b/jwt-authorizer/src/claims.rs index d46542b..bc827c8 100644 --- a/jwt-authorizer/src/claims.rs +++ b/jwt-authorizer/src/claims.rs @@ -1,7 +1,6 @@ use chrono::{DateTime, TimeZone, Utc}; -use std::fmt; -use serde::{de, Deserialize, Deserializer}; +use serde::Deserialize; /// Seconds since the epoch #[derive(Deserialize, Clone, PartialEq, Eq, Debug)] @@ -13,8 +12,12 @@ impl From for DateTime { } } -#[derive(PartialEq, Debug, Clone)] -pub struct StringList(Vec); +#[derive(PartialEq, Debug, Clone, Deserialize)] +#[serde(untagged)] +pub enum OneOrArray { + One(T), + Array(Vec), +} /// Claims mentioned in the JWT specifications. /// @@ -23,39 +26,13 @@ pub struct StringList(Vec); pub struct RegisteredClaims { pub iss: Option, pub sub: Option, - pub aud: Option, + pub aud: Option>, pub exp: Option, pub nbf: Option, pub iat: Option, pub jti: Option, } -impl<'de> Deserialize<'de> for StringList { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct StringListVisitor; - impl<'de> de::Visitor<'de> for StringListVisitor { - type Value = StringList; - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(formatter, "a space seperated strings") - } - - fn visit_str(self, v: &str) -> Result - where - E: de::Error, - { - let auds: Vec = v.split(' ').map(|s| s.to_owned()).collect(); - Ok(StringList(auds)) - } - } - - deserializer.deserialize_string(StringListVisitor) - } -} - #[cfg(test)] mod tests { @@ -63,17 +40,20 @@ mod tests { use serde::Deserialize; use serde_json::json; - use crate::claims::{NumericDate, RegisteredClaims, StringList}; + use crate::claims::{NumericDate, OneOrArray, RegisteredClaims}; #[derive(Deserialize)] struct TestStruct { - v: StringList, + v: OneOrArray, } #[test] fn rfc_claims_aud() { - let a: TestStruct = serde_json::from_str(r#"{"v":"a b"}"#).unwrap(); - assert_eq!(a.v, StringList(vec!["a".to_owned(), "b".to_owned()])); + let a: TestStruct = serde_json::from_str(r#"{"v":"a"}"#).unwrap(); + assert_eq!(a.v, OneOrArray::One("a".to_owned())); + + let a: TestStruct = serde_json::from_str(r#"{"v":["a", "b"]}"#).unwrap(); + assert_eq!(a.v, OneOrArray::Array(vec!["a".to_owned(), "b".to_owned()])); } #[test] @@ -87,7 +67,7 @@ mod tests { fn rfc_claims() { let jwt_json = json!({ "iss": "http://localhost:3001", - "aud": "aud1 aud2", + "aud": ["aud1", "aud2"], "sub": "bob", "exp": 1516240122, "iat": 1516239022, @@ -96,7 +76,10 @@ mod tests { let claims: RegisteredClaims = serde_json::from_value(jwt_json).expect("Failed RfcClaims deserialisation"); assert_eq!(claims.iss.unwrap(), "http://localhost:3001"); - assert_eq!(claims.aud.unwrap(), StringList(vec!["aud1".to_owned(), "aud2".to_owned()])); + assert_eq!( + claims.aud.unwrap(), + OneOrArray::Array(vec!["aud1".to_owned(), "aud2".to_owned()]) + ); assert_eq!(claims.exp.unwrap(), NumericDate(1516240122)); let dt: DateTime = claims.iat.unwrap().into(); diff --git a/jwt-authorizer/src/lib.rs b/jwt-authorizer/src/lib.rs index f9d2def..abc1254 100644 --- a/jwt-authorizer/src/lib.rs +++ b/jwt-authorizer/src/lib.rs @@ -6,7 +6,7 @@ use jsonwebtoken::TokenData; use serde::de::DeserializeOwned; pub use self::error::AuthError; -pub use claims::{NumericDate, RegisteredClaims, StringList}; +pub use claims::{NumericDate, OneOrArray, RegisteredClaims}; pub use jwks::key_store_manager::{Refresh, RefreshStrategy}; pub use layer::JwtAuthorizer; pub use validation::Validation;