From c767e1da2a31fb1aa32ec2f5c4af4377d22c0856 Mon Sep 17 00:00:00 2001 From: Alexandcoats Date: Wed, 8 Jun 2022 11:13:07 -0400 Subject: [PATCH 1/4] Update libs and improve lib usability --- auth-helper/Cargo.toml | 4 +- auth-helper/src/jwt.rs | 162 ++++++++++++++++------------ auth-helper/tests/jwt.rs | 225 +++++++++++++++++++-------------------- 3 files changed, 205 insertions(+), 186 deletions(-) diff --git a/auth-helper/Cargo.toml b/auth-helper/Cargo.toml index 1a69f10..4e098af 100644 --- a/auth-helper/Cargo.toml +++ b/auth-helper/Cargo.toml @@ -11,8 +11,8 @@ keywords = [ "iota", "auth" ] homepage = "https://www.iota.org" [dependencies] -jsonwebtoken = { version = "7.2.0", default-features = false } +jsonwebtoken = { version = "8.1.0", default-features = false } rand = { version = "0.8.4", default-features = false, features = [ "std" ] } -rust-argon2 = { version = "0.8.3", default-features = false } +rust-argon2 = { version = "1.0.0", default-features = false } serde = { version = "1.0.30", default-features = false, features = [ "std", "derive" ] } thiserror = { version = "1.0.30", default-features = false } diff --git a/auth-helper/src/jwt.rs b/auth-helper/src/jwt.rs index b3cb0e1..b224b47 100644 --- a/auth-helper/src/jwt.rs +++ b/auth-helper/src/jwt.rs @@ -3,10 +3,10 @@ //! A module that provides JSON Web Token utilities. -use std::time::{SystemTime, UNIX_EPOCH}; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; -pub use jsonwebtoken::TokenData; -use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation}; +pub use jsonwebtoken::{self, Validation}; +use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, TokenData}; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -27,52 +27,65 @@ pub enum Error { pub struct Claims { /// Issuer. /// Identifies the principal that issued the JWT. The processing of this claim is generally application specific. - iss: String, + pub iss: String, /// Subject. /// Identifies the principal that is the subject of the JWT. The claims in a JWT are normally statements about the /// subject. The subject value MUST either be scoped to be locally unique in the context of the issuer or be /// globally unique. The processing of this claim is generally application specific. - sub: String, + pub sub: String, /// Audience. /// Identifies the recipients that the JWT is intended for. Each principal intended to process the JWT MUST /// identify itself with a value in the audience claim. If the principal processing the claim does not identify /// itself with a value in the "aud" claim when this claim is present, then the JWT MUST be rejected. The /// interpretation of audience values is generally application specific. - aud: String, + pub aud: String, /// Expiration Time. /// Identifies the expiration time on or after which the JWT MUST NOT be accepted for processing. The processing of /// the "exp" claim requires that the current date/time MUST be before the expiration date/time listed in the "exp" /// claim. Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock /// skew. #[serde(skip_serializing_if = "Option::is_none")] - exp: Option, + pub exp: Option, /// Not Before. /// Identifies the time before which the JWT MUST NOT be accepted for processing. The processing of the "nbf" claim /// requires that the current date/time MUST be after or equal to the not-before date/time listed in the "nbf" /// claim. Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock /// skew. - nbf: u64, + pub nbf: u64, /// Issued At. /// Identifies the time at which the JWT was issued. This claim can be used to determine the age of the JWT. - iat: u64, + pub iat: u64, } impl Claims { /// Creates a new set of claims. - fn new(iss: String, sub: String, aud: String, nbf: u64) -> Self { + pub fn new(iss: impl Into, sub: impl Into, aud: impl Into) -> Self { + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .as_ref() + .map(Duration::as_secs) + .unwrap_or_default(); Self { - iss, - sub, - aud, + iss: iss.into(), + sub: sub.into(), + aud: aud.into(), exp: None, - nbf, - iat: SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("Clock may have gone backwards") - .as_secs() as u64, + nbf: now, + iat: now, } } + /// Specifies that this token will expire, and provides an expiry time (offset from issue time). + pub fn expires_after(mut self, dur: Duration) -> Result { + let dur = dur.as_secs(); + let exp = self.nbf.checked_add(dur).ok_or(Error::InvalidExpiry { + issued_at: self.nbf, + expiry: self.nbf + dur, + })?; + self.exp = Some(exp); + Ok(self) + } + /// Returns the issuer of the JWT. pub fn issuer(&self) -> &str { &self.iss @@ -104,58 +117,82 @@ impl Claims { } } -/// Builder for the [`Claims`] structure. -pub struct ClaimsBuilder { - iss: String, - sub: String, - aud: String, - exp: Option, +pub trait BuildValidation: _sealed_validation::SealedBuildValidation { + fn with_audience(self, aud: impl ToString) -> Self; + + fn with_audiences(self, auds: &[impl ToString]) -> Self; + + fn with_issuer(self, iss: impl ToString) -> Self; + + fn with_issuers(self, iss: &[impl ToString]) -> Self; + + fn with_subject(self, sub: impl ToString) -> Self; + + fn with_required_spec_claims(self, claims: &[&str]) -> Self; + + fn with_leeway(self, secs: u64) -> Self; + + fn validate_exp(self, validate: bool) -> Self; + + fn validate_nbf(self, validate: bool) -> Self; } -impl ClaimsBuilder { - /// Creates a new [`ClaimsBuilder`] with the given mandatory parameters. - pub fn new(iss: String, sub: String, aud: String) -> Self { - Self { - iss, - sub, - aud, - exp: None, - } +impl BuildValidation for Validation { + fn with_audience(mut self, aud: impl ToString) -> Self { + self.set_audience(&[aud]); + self } - /// Specifies that this token will expire, and provides an expiry time (offset from issue time). - #[must_use] - pub fn with_expiry(mut self, exp: u64) -> Self { - self.exp = Some(exp); + fn with_audiences(mut self, auds: &[impl ToString]) -> Self { + self.set_audience(auds); self } - /// Builds and returns a [`Claims`] structure using the given builder options. - pub fn build(self) -> Result { - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("Clock may have gone backwards") - .as_secs() as u64; + fn with_issuer(mut self, iss: impl ToString) -> Self { + self.set_issuer(&[iss]); + self + } - let mut claims = Claims::new(self.iss, self.sub, self.aud, now); + fn with_issuers(mut self, iss: &[impl ToString]) -> Self { + self.set_issuer(iss); + self + } - if let Some(exp) = self.exp { - let expiry = now.checked_add(exp).ok_or(Error::InvalidExpiry { - issued_at: now, - expiry: exp, - })?; + fn with_subject(mut self, sub: impl ToString) -> Self { + self.sub = Some(sub.to_string()); + self + } - claims.exp = Some(expiry); - } + fn with_required_spec_claims(mut self, claims: &[&str]) -> Self { + self.set_required_spec_claims(claims); + self + } - Ok(claims) + fn with_leeway(mut self, secs: u64) -> Self { + self.leeway = secs; + self + } + + fn validate_exp(mut self, validate: bool) -> Self { + self.validate_exp = validate; + self + } + + fn validate_nbf(mut self, validate: bool) -> Self { + self.validate_nbf = validate; + self } } +mod _sealed_validation { + pub trait SealedBuildValidation {} + impl SealedBuildValidation for jsonwebtoken::Validation {} +} + /// Represents a JSON Web Token. /// #[derive(Clone, Debug)] -pub struct JsonWebToken(String); +pub struct JsonWebToken(pub String); impl From for JsonWebToken { fn from(inner: String) -> Self { @@ -178,26 +215,11 @@ impl JsonWebToken { } /// Validates a JSON Web Token. - pub fn validate( - &self, - issuer: String, - subject: String, - audience: String, - expires: bool, - secret: &[u8], - ) -> Result, Error> { - let mut validation = Validation { - iss: Some(issuer), - sub: Some(subject), - validate_exp: expires, - ..Default::default() - }; - validation.set_audience(&[audience]); - + pub fn validate(&self, validation: impl Into, secret: &[u8]) -> Result, Error> { Ok(decode::( &self.0, &DecodingKey::from_secret(secret), - &validation, + &validation.into(), )?) } } diff --git a/auth-helper/tests/jwt.rs b/auth-helper/tests/jwt.rs index a39258f..99a68a2 100644 --- a/auth-helper/tests/jwt.rs +++ b/auth-helper/tests/jwt.rs @@ -1,28 +1,26 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use auth_helper::jwt; +use std::time::Duration; + +use auth_helper::jwt::{BuildValidation, Claims, Error, JsonWebToken, Validation}; #[test] fn jwt_valid() { - let claims = jwt::ClaimsBuilder::new( - String::from("issuer"), - String::from("subject"), - String::from("audience"), - ) - .with_expiry(1000) - .build() - .unwrap(); + let claims = Claims::new("issuer", "subject", "audience") + .expires_after(Duration::from_secs(1000)) + .unwrap(); - let jwt = jwt::JsonWebToken::new(claims, b"secret").unwrap(); + let jwt = JsonWebToken::new(claims, b"secret").unwrap(); assert!( jwt.validate( - String::from("issuer"), - String::from("subject"), - String::from("audience"), - false, - b"secret", + Validation::default() + .with_issuer("issuer") + .with_audience("audience") + .with_subject("subject") + .validate_nbf(true), + b"secret" ) .is_ok() ); @@ -30,24 +28,20 @@ fn jwt_valid() { #[test] fn jwt_to_str_from_str_valid() { - let claims = jwt::ClaimsBuilder::new( - String::from("issuer"), - String::from("subject"), - String::from("audience"), - ) - .with_expiry(1000) - .build() - .unwrap(); + let claims = Claims::new("issuer", "subject", "audience") + .expires_after(Duration::from_secs(1000)) + .unwrap(); - let jwt = jwt::JsonWebToken::from(jwt::JsonWebToken::new(claims, b"secret").unwrap().to_string()); + let jwt = JsonWebToken::from(JsonWebToken::new(claims, b"secret").unwrap().to_string()); assert!( jwt.validate( - String::from("issuer"), - String::from("subject"), - String::from("audience"), - false, - b"secret", + Validation::default() + .with_issuer("issuer") + .with_audience("audience") + .with_subject("subject") + .validate_nbf(true), + b"secret" ) .is_ok() ); @@ -55,127 +49,130 @@ fn jwt_to_str_from_str_valid() { #[test] fn jwt_invalid_issuer() { - let claims = jwt::ClaimsBuilder::new( - String::from("issuer"), - String::from("subject"), - String::from("audience"), - ) - .with_expiry(1000) - .build() - .unwrap(); + let claims = Claims::new("issuer", "subject", "audience") + .expires_after(Duration::from_secs(1000)) + .unwrap(); - let jwt = jwt::JsonWebToken::new(claims, b"secret").unwrap(); + let jwt = JsonWebToken::new(claims, b"secret").unwrap(); - assert!( + assert!(matches!( jwt.validate( - String::from("Issuer"), - String::from("subject"), - String::from("audience"), - false, + Validation::default() + .with_issuer("Issuer") + .with_audience("audience") + .with_subject("subject") + .validate_nbf(true), b"secret", - ) - .is_err() - ); + ), + Err(Error::Jwt(e)) if matches!(e.kind(), jsonwebtoken::errors::ErrorKind::InvalidIssuer) + )) } #[test] fn jwt_invalid_subject() { - let claims = jwt::ClaimsBuilder::new( - String::from("issuer"), - String::from("subject"), - String::from("audience"), - ) - .with_expiry(1000) - .build() - .unwrap(); + let claims = Claims::new("issuer", "subject", "audience") + .expires_after(Duration::from_secs(1000)) + .unwrap(); - let jwt = jwt::JsonWebToken::new(claims, b"secret").unwrap(); + let jwt = JsonWebToken::new(claims, b"secret").unwrap(); - assert!( + assert!(matches!( jwt.validate( - String::from("issuer"), - String::from("Subject"), - String::from("audience"), - false, + Validation::default() + .with_issuer("issuer") + .with_audience("audience") + .with_subject("Subject") + .validate_nbf(true), b"secret", - ) - .is_err() - ); + ), + Err(Error::Jwt(e)) if matches!(e.kind(), jsonwebtoken::errors::ErrorKind::InvalidSubject) + )) } #[test] fn jwt_invalid_audience() { - let claims = jwt::ClaimsBuilder::new( - String::from("issuer"), - String::from("subject"), - String::from("audience"), - ) - .with_expiry(1000) - .build() - .unwrap(); + let claims = Claims::new("issuer", "subject", "audience") + .expires_after(Duration::from_secs(1000)) + .unwrap(); - let jwt = jwt::JsonWebToken::new(claims, b"secret").unwrap(); + let jwt = JsonWebToken::new(claims, b"secret").unwrap(); - assert!( + assert!(matches!( jwt.validate( - String::from("issuer"), - String::from("subject"), - String::from("Audience"), - false, + Validation::default() + .with_issuer("issuer") + .with_audience("Audience") + .with_subject("subject") + .validate_nbf(true), b"secret", - ) - .is_err() - ); + ), + Err(Error::Jwt(e)) if matches!(e.kind(), jsonwebtoken::errors::ErrorKind::InvalidAudience) + )) } #[test] fn jwt_invalid_secret() { - let claims = jwt::ClaimsBuilder::new( - String::from("issuer"), - String::from("subject"), - String::from("audience"), - ) - .with_expiry(1000) - .build() - .unwrap(); + let claims = Claims::new("issuer", "subject", "audience") + .expires_after(Duration::from_secs(1000)) + .unwrap(); - let jwt = jwt::JsonWebToken::new(claims, b"secret").unwrap(); + let jwt = JsonWebToken::new(claims, b"secret").unwrap(); - assert!( + assert!(matches!( jwt.validate( - String::from("issuer"), - String::from("subject"), - String::from("audience"), - false, + Validation::default() + .with_issuer("issuer") + .with_audience("audience") + .with_subject("subject") + .validate_nbf(true), b"Secret", - ) - .is_err() - ); + ), + Err(Error::Jwt(e)) if matches!(e.kind(), jsonwebtoken::errors::ErrorKind::InvalidSignature) + )) } #[test] fn jwt_invalid_expired() { - let claims = jwt::ClaimsBuilder::new( - String::from("issuer"), - String::from("subject"), - String::from("audience"), - ) - .with_expiry(0) - .build() - .unwrap(); + let claims = Claims::new("issuer", "subject", "audience") + .expires_after(Duration::from_secs(0)) + .unwrap(); - let jwt = jwt::JsonWebToken::new(claims, b"secret").unwrap(); + let jwt = JsonWebToken::new(claims, b"secret").unwrap(); std::thread::sleep(std::time::Duration::from_secs(1)); - assert!( + assert!(matches!( jwt.validate( - String::from("issuer"), - String::from("subject"), - String::from("audience"), - true, + Validation::default() + .with_issuer("issuer") + .with_audience("audience") + .with_subject("subject") + .validate_nbf(true) + .with_leeway(0), b"secret", - ) - .is_err() - ); + ), + Err(Error::Jwt(e)) if matches!(e.kind(), jsonwebtoken::errors::ErrorKind::ExpiredSignature) + )) +} + +#[test] +fn jwt_immature_signature() { + let mut claims = Claims::new("issuer", "subject", "audience") + .expires_after(Duration::from_secs(1000)) + .unwrap(); + claims.nbf = claims.nbf + 100; + + let jwt = JsonWebToken::new(claims, b"secret").unwrap(); + + assert!(matches!( + jwt.validate( + Validation::default() + .with_issuer("issuer") + .with_audience("audience") + .with_subject("subject") + .validate_nbf(true), + b"secret", + ), + Err(Error::Jwt(e)) if matches!(e.kind(), jsonwebtoken::errors::ErrorKind::ImmatureSignature) + )) } From c2b41cfe47405a7c695dceaae31eef5708b5be6e Mon Sep 17 00:00:00 2001 From: Alexandcoats Date: Wed, 8 Jun 2022 12:28:10 -0400 Subject: [PATCH 2/4] clippy!!! :triumph: --- auth-helper/tests/jwt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth-helper/tests/jwt.rs b/auth-helper/tests/jwt.rs index 99a68a2..cf52a61 100644 --- a/auth-helper/tests/jwt.rs +++ b/auth-helper/tests/jwt.rs @@ -160,7 +160,7 @@ fn jwt_immature_signature() { let mut claims = Claims::new("issuer", "subject", "audience") .expires_after(Duration::from_secs(1000)) .unwrap(); - claims.nbf = claims.nbf + 100; + claims.nbf += 100; let jwt = JsonWebToken::new(claims, b"secret").unwrap(); From a42fc716e0038decbf1e89b63dfe0e7c70ebc265 Mon Sep 17 00:00:00 2001 From: Alexandcoats Date: Thu, 9 Jun 2022 09:55:11 -0400 Subject: [PATCH 3/4] Return an error if the system time is before the UNIX epoch. Add some more helper fns to claims. Fix issued at field in error. --- auth-helper/src/jwt.rs | 60 +++++++++++++++++++++++++++++++++------- auth-helper/tests/jwt.rs | 24 ++++++++++------ 2 files changed, 66 insertions(+), 18 deletions(-) diff --git a/auth-helper/src/jwt.rs b/auth-helper/src/jwt.rs index b224b47..e480cb5 100644 --- a/auth-helper/src/jwt.rs +++ b/auth-helper/src/jwt.rs @@ -16,6 +16,12 @@ pub enum Error { /// Provided an invalid expiry date. #[error("invalid expiry time {expiry} from issue time {issued_at}")] InvalidExpiry { issued_at: u64, expiry: u64 }, + /// Provided an invalid not before date. + #[error("invalid not before time {nbf} from issue time {issued_at}")] + InvalidNbf { issued_at: u64, nbf: u64 }, + /// The system time is before the UNIX epoch. + #[error("system time is before the UNIX epoch")] + InvalidSystemTime, /// An error occured in the [`jsonwebtoken`] crate. #[error(transparent)] Jwt(#[from] jsonwebtoken::errors::Error), @@ -59,33 +65,67 @@ pub struct Claims { impl Claims { /// Creates a new set of claims. - pub fn new(iss: impl Into, sub: impl Into, aud: impl Into) -> Self { + pub fn new(iss: impl Into, sub: impl Into, aud: impl Into) -> Result { let now = SystemTime::now() .duration_since(UNIX_EPOCH) - .as_ref() - .map(Duration::as_secs) - .unwrap_or_default(); - Self { + .map_err(|_| Error::InvalidSystemTime)? + .as_secs(); + Ok(Self { iss: iss.into(), sub: sub.into(), aud: aud.into(), exp: None, nbf: now, iat: now, + }) + } + + /// Specify that this token will expire by providing an expiry timestamp. + pub fn expires_after(mut self, exp: u64) -> Result { + if exp < self.iat { + return Err(Error::InvalidExpiry { + issued_at: self.iat, + expiry: exp, + }); } + self.exp = Some(exp); + Ok(self) } - /// Specifies that this token will expire, and provides an expiry time (offset from issue time). - pub fn expires_after(mut self, dur: Duration) -> Result { + /// Specify that this token will expire by providing a duration offset from issued time. + pub fn expires_after_duration(mut self, dur: Duration) -> Result { let dur = dur.as_secs(); - let exp = self.nbf.checked_add(dur).ok_or(Error::InvalidExpiry { - issued_at: self.nbf, - expiry: self.nbf + dur, + let exp = self.iat.checked_add(dur).ok_or(Error::InvalidExpiry { + issued_at: self.iat, + expiry: self.iat + dur, })?; self.exp = Some(exp); Ok(self) } + /// Specify that this token is valid after the given timestamp. + pub fn valid_after(mut self, nbf: u64) -> Result { + if nbf < self.iat { + return Err(Error::InvalidNbf { + issued_at: self.iat, + nbf, + }); + } + self.nbf = nbf; + Ok(self) + } + + /// Specify when this token becomes valid by providing a duration offset from issued time. + pub fn valid_after_duration(mut self, dur: Duration) -> Result { + let dur = dur.as_secs(); + let nbf = self.iat.checked_add(dur).ok_or(Error::InvalidNbf { + issued_at: self.iat, + nbf: self.iat + dur, + })?; + self.nbf = nbf; + Ok(self) + } + /// Returns the issuer of the JWT. pub fn issuer(&self) -> &str { &self.iss diff --git a/auth-helper/tests/jwt.rs b/auth-helper/tests/jwt.rs index cf52a61..a6820ff 100644 --- a/auth-helper/tests/jwt.rs +++ b/auth-helper/tests/jwt.rs @@ -8,7 +8,8 @@ use auth_helper::jwt::{BuildValidation, Claims, Error, JsonWebToken, Validation} #[test] fn jwt_valid() { let claims = Claims::new("issuer", "subject", "audience") - .expires_after(Duration::from_secs(1000)) + .unwrap() + .expires_after_duration(Duration::from_secs(1000)) .unwrap(); let jwt = JsonWebToken::new(claims, b"secret").unwrap(); @@ -29,7 +30,8 @@ fn jwt_valid() { #[test] fn jwt_to_str_from_str_valid() { let claims = Claims::new("issuer", "subject", "audience") - .expires_after(Duration::from_secs(1000)) + .unwrap() + .expires_after_duration(Duration::from_secs(1000)) .unwrap(); let jwt = JsonWebToken::from(JsonWebToken::new(claims, b"secret").unwrap().to_string()); @@ -50,7 +52,8 @@ fn jwt_to_str_from_str_valid() { #[test] fn jwt_invalid_issuer() { let claims = Claims::new("issuer", "subject", "audience") - .expires_after(Duration::from_secs(1000)) + .unwrap() + .expires_after_duration(Duration::from_secs(1000)) .unwrap(); let jwt = JsonWebToken::new(claims, b"secret").unwrap(); @@ -71,7 +74,8 @@ fn jwt_invalid_issuer() { #[test] fn jwt_invalid_subject() { let claims = Claims::new("issuer", "subject", "audience") - .expires_after(Duration::from_secs(1000)) + .unwrap() + .expires_after_duration(Duration::from_secs(1000)) .unwrap(); let jwt = JsonWebToken::new(claims, b"secret").unwrap(); @@ -92,7 +96,8 @@ fn jwt_invalid_subject() { #[test] fn jwt_invalid_audience() { let claims = Claims::new("issuer", "subject", "audience") - .expires_after(Duration::from_secs(1000)) + .unwrap() + .expires_after_duration(Duration::from_secs(1000)) .unwrap(); let jwt = JsonWebToken::new(claims, b"secret").unwrap(); @@ -113,7 +118,8 @@ fn jwt_invalid_audience() { #[test] fn jwt_invalid_secret() { let claims = Claims::new("issuer", "subject", "audience") - .expires_after(Duration::from_secs(1000)) + .unwrap() + .expires_after_duration(Duration::from_secs(1000)) .unwrap(); let jwt = JsonWebToken::new(claims, b"secret").unwrap(); @@ -134,7 +140,8 @@ fn jwt_invalid_secret() { #[test] fn jwt_invalid_expired() { let claims = Claims::new("issuer", "subject", "audience") - .expires_after(Duration::from_secs(0)) + .unwrap() + .expires_after_duration(Duration::from_secs(0)) .unwrap(); let jwt = JsonWebToken::new(claims, b"secret").unwrap(); @@ -158,7 +165,8 @@ fn jwt_invalid_expired() { #[test] fn jwt_immature_signature() { let mut claims = Claims::new("issuer", "subject", "audience") - .expires_after(Duration::from_secs(1000)) + .unwrap() + .expires_after_duration(Duration::from_secs(1000)) .unwrap(); claims.nbf += 100; From 29416c1123bfbfb6832c9e4ce982b5f8023bdc09 Mon Sep 17 00:00:00 2001 From: Alexandcoats Date: Tue, 14 Jun 2022 12:35:21 -0400 Subject: [PATCH 4/4] Bump version --- auth-helper/CHANGELOG.md | 19 +++++++++++++++++++ auth-helper/Cargo.toml | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/auth-helper/CHANGELOG.md b/auth-helper/CHANGELOG.md index a5c1094..808bbf3 100644 --- a/auth-helper/CHANGELOG.md +++ b/auth-helper/CHANGELOG.md @@ -19,6 +19,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Security --> +## 0.3.0 - 2022-06-14 + +### Added + + - `BuildValidation` extension trait for the `jsonwebtoken::Validation` type; + - `Claims` builder-lite methods; + +### Changed + + - Re-export `jsonwebtoken` lib; + - Expose `Claims` and `JsonWebToken` inner fields; + - `Claims` system time error handling; + - Better test cases; + - `JsonWebToken::validate` accepts `Validation` struct for better flexibility; + +### Removed + + - `ClaimsBuilder`; + ## 0.2.0 - 2022-01-25 ### Added diff --git a/auth-helper/Cargo.toml b/auth-helper/Cargo.toml index 4e098af..0f370cd 100644 --- a/auth-helper/Cargo.toml +++ b/auth-helper/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "auth-helper" -version = "0.2.0" +version = "0.3.0" authors = [ "IOTA Stiftung" ] edition = "2021" description = "Authorization tools"