mirror of
https://github.com/TECHNOFAB11/jwt-authorizer.git
synced 2025-12-11 23:50:07 +01:00
This commit is contained in:
parent
28c7eedcd5
commit
683f932468
12 changed files with 471 additions and 75 deletions
|
|
@ -5,11 +5,15 @@ JWT authorizer Layer for Axum.
|
|||
## Features
|
||||
|
||||
- JWT token verification (Bearer)
|
||||
- Algoritms: ECDSA, RSA, EdDSA, HS
|
||||
- Algoritms: ECDSA, RSA, EdDSA, HMAC
|
||||
- JWKS endpoint support
|
||||
- Configurable refresh
|
||||
- OpenId Connect Discovery
|
||||
- Validation
|
||||
- exp, nbf, iss, aud
|
||||
- Claims extraction
|
||||
- Claims checker
|
||||
- Tracing support (error logging)
|
||||
|
||||
## Usage
|
||||
|
||||
|
|
@ -17,7 +21,7 @@ See documentation of the [`jwt-authorizer`](./jwt-authorizer/docs/README.md) mod
|
|||
|
||||
## Development
|
||||
|
||||
...
|
||||
Minimum supported Rust version is 1.65.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
# Key generation
|
||||
|
||||
## RSA
|
||||
|
||||
> openssl genrsa -out rsa-private2.pem 1024
|
||||
> openssl rsa -in rsa-private2.pem -out rsa-public2.pem -pubout -outform PEM
|
||||
|
||||
## EC (ECDSA) - (algorigthm ES256 - ECDSA using SHA-256)
|
||||
|
||||
curve name: prime256v1 (secp256r1, secp384r1)
|
||||
|
|
|
|||
9
config/rsa-public2.pem
Normal file
9
config/rsa-public2.pem
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyRE6rHuNR0QbHO3H3Kt2
|
||||
pOKGVhQqGZXInOduQNxXzuKlvQTLUTv4l4sggh5/CYYi/cvI+SXVT9kPWSKXxJXB
|
||||
Xd/4LkvcPuUakBoAkfh+eiFVMh2VrUyWyj3MFl0HTVF9KwRXLAcwkREiS3npThHR
|
||||
yIxuy0ZMeZfxVL5arMhw1SRELB8HoGfG/AtH89BIE9jDBHZ9dLelK9a184zAf8Lw
|
||||
oPLxvJb3Il5nncqPcSfKDDodMFBIMc4lQzDKL5gvmiXLXB1AGLm8KBjfE8s3L5xq
|
||||
i+yUod+j8MtvIj812dkS4QMiRVN/by2h3ZY8LYVGrqZXZTcgn2ujn8uKjXLZVD5T
|
||||
dQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
|
|
@ -101,6 +101,7 @@ fn build_header(alg: Algorithm, kid: &str) -> Header {
|
|||
struct Claims {
|
||||
iss: &'static str,
|
||||
sub: &'static str,
|
||||
aud: &'static str,
|
||||
exp: usize,
|
||||
nbf: usize,
|
||||
}
|
||||
|
|
@ -110,6 +111,7 @@ pub async fn tokens() -> Json<Value> {
|
|||
let claims = Claims {
|
||||
iss: ISSUER_URI,
|
||||
sub: "b@b.com",
|
||||
aud: "aud1",
|
||||
exp: 2000000000, // May 2033
|
||||
nbf: 1516239022, // Jan 2018
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,13 +5,15 @@ JWT authoriser Layer for Axum.
|
|||
## Features
|
||||
|
||||
- JWT token verification (Bearer)
|
||||
- Algoritms: ECDSA, RSA, EdDSA, HS
|
||||
- Algoritms: ECDSA, RSA, EdDSA, HMAC
|
||||
- JWKS endpoint support
|
||||
- Configurable refresh
|
||||
- OpenId Connect Discovery
|
||||
- Validation
|
||||
- exp, nbf, iss, aud
|
||||
- Claims extraction
|
||||
- Claims checker
|
||||
- tracing support (error logging)
|
||||
- Tracing support (error logging)
|
||||
|
||||
|
||||
## Usage Example
|
||||
|
|
@ -48,6 +50,30 @@ JWT authoriser Layer for Axum.
|
|||
# };
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
Validation configuration object.
|
||||
|
||||
If no validation configuration is provided default values will be applyed.
|
||||
|
||||
docs: [`jwt-authorizer::Validation`]
|
||||
|
||||
```rust
|
||||
# use jwt_authorizer::{JwtAuthorizer, Validation};
|
||||
# use serde_json::Value;
|
||||
|
||||
let validation = Validation::new()
|
||||
.iss(&["https://issuer1", "https://issuer2"])
|
||||
.aud(&["audience1"])
|
||||
.nbf(true)
|
||||
.leeway(20);
|
||||
|
||||
let jwt_auth: JwtAuthorizer<Value> = JwtAuthorizer::from_oidc("https://accounts.google.com")
|
||||
.validation(validation);
|
||||
|
||||
```
|
||||
|
||||
|
||||
## ClaimsChecker
|
||||
|
||||
A check function (mapping deserialized claims to boolean) can be added to the authorizer.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::io::Read;
|
||||
|
||||
use jsonwebtoken::{decode, decode_header, jwk::JwkSet, DecodingKey, TokenData, Validation};
|
||||
use jsonwebtoken::{decode, decode_header, jwk::JwkSet, DecodingKey, TokenData};
|
||||
use reqwest::Url;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
|
|
@ -37,6 +37,7 @@ where
|
|||
{
|
||||
pub key_source: KeySource,
|
||||
pub claims_checker: Option<FnClaimsChecker<C>>,
|
||||
pub validation: crate::validation::Validation,
|
||||
}
|
||||
|
||||
fn read_data(path: &str) -> Result<Vec<u8>, InitError> {
|
||||
|
|
@ -64,6 +65,7 @@ where
|
|||
key_source_type: &KeySourceType,
|
||||
claims_checker: Option<FnClaimsChecker<C>>,
|
||||
refresh: Option<Refresh>,
|
||||
validation: crate::validation::Validation,
|
||||
) -> Result<Authorizer<C>, InitError> {
|
||||
Ok(match key_source_type {
|
||||
KeySourceType::RSA(path) => {
|
||||
|
|
@ -71,6 +73,7 @@ where
|
|||
Authorizer {
|
||||
key_source: KeySource::DecodingKeySource(key),
|
||||
claims_checker,
|
||||
validation,
|
||||
}
|
||||
}
|
||||
KeySourceType::EC(path) => {
|
||||
|
|
@ -78,6 +81,7 @@ where
|
|||
Authorizer {
|
||||
key_source: KeySource::DecodingKeySource(key),
|
||||
claims_checker,
|
||||
validation,
|
||||
}
|
||||
}
|
||||
KeySourceType::ED(path) => {
|
||||
|
|
@ -85,6 +89,7 @@ where
|
|||
Authorizer {
|
||||
key_source: KeySource::DecodingKeySource(key),
|
||||
claims_checker,
|
||||
validation,
|
||||
}
|
||||
}
|
||||
KeySourceType::Secret(secret) => {
|
||||
|
|
@ -92,6 +97,7 @@ where
|
|||
Authorizer {
|
||||
key_source: KeySource::DecodingKeySource(key),
|
||||
claims_checker,
|
||||
validation,
|
||||
}
|
||||
}
|
||||
KeySourceType::JwksString(jwks_str) => {
|
||||
|
|
@ -102,6 +108,7 @@ where
|
|||
Authorizer {
|
||||
key_source: KeySource::DecodingKeySource(k),
|
||||
claims_checker,
|
||||
validation,
|
||||
}
|
||||
}
|
||||
KeySourceType::Jwks(url) => {
|
||||
|
|
@ -110,6 +117,7 @@ where
|
|||
Authorizer {
|
||||
key_source: KeySource::KeyStoreSource(key_store_manager),
|
||||
claims_checker,
|
||||
validation,
|
||||
}
|
||||
}
|
||||
KeySourceType::Discovery(issuer_url) => {
|
||||
|
|
@ -120,6 +128,7 @@ where
|
|||
Authorizer {
|
||||
key_source: KeySource::KeyStoreSource(key_store_manager),
|
||||
claims_checker,
|
||||
validation,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -127,9 +136,11 @@ where
|
|||
|
||||
pub async fn check_auth(&self, token: &str) -> Result<TokenData<C>, AuthError> {
|
||||
let header = decode_header(token)?;
|
||||
let validation = Validation::new(header.alg);
|
||||
// TODO: build validation only once or cache it (store it in key_source?)
|
||||
// (problem: alg family is checked in jsonwebtoken but may change with store refresh)
|
||||
let jwt_validation = &self.validation.to_jwt_validation(header.alg);
|
||||
let decoding_key = self.key_source.get_key(header).await?;
|
||||
let token_data = decode::<C>(token, &decoding_key, &validation)?;
|
||||
let token_data = decode::<C>(token, &decoding_key, jwt_validation)?;
|
||||
|
||||
if let Some(ref checker) = self.claims_checker {
|
||||
if !checker.check(&token_data.claims) {
|
||||
|
|
@ -147,12 +158,14 @@ mod tests {
|
|||
use jsonwebtoken::{Algorithm, Header};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::validation::Validation;
|
||||
|
||||
use super::{Authorizer, KeySourceType};
|
||||
|
||||
#[tokio::test]
|
||||
async fn build_from_secret() {
|
||||
let h = Header::new(Algorithm::HS256);
|
||||
let a = Authorizer::<Value>::build(&KeySourceType::Secret("xxxxxx"), None, None)
|
||||
let a = Authorizer::<Value>::build(&KeySourceType::Secret("xxxxxx"), None, None, Validation::new())
|
||||
.await
|
||||
.unwrap();
|
||||
let k = a.key_source.get_key(h);
|
||||
|
|
@ -171,7 +184,7 @@ mod tests {
|
|||
"e": "AQAB"
|
||||
}]}
|
||||
"#;
|
||||
let a = Authorizer::<Value>::build(&KeySourceType::JwksString(jwks.to_owned()), None, None)
|
||||
let a = Authorizer::<Value>::build(&KeySourceType::JwksString(jwks.to_owned()), None, None, Validation::new())
|
||||
.await
|
||||
.unwrap();
|
||||
let k = a.key_source.get_key(Header::new(Algorithm::RS256));
|
||||
|
|
@ -180,42 +193,70 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn build_from_file() {
|
||||
let a = Authorizer::<Value>::build(&KeySourceType::RSA("../config/rsa-public1.pem".to_owned()), None, None)
|
||||
.await
|
||||
.unwrap();
|
||||
let a = Authorizer::<Value>::build(
|
||||
&KeySourceType::RSA("../config/rsa-public1.pem".to_owned()),
|
||||
None,
|
||||
None,
|
||||
Validation::new(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let k = a.key_source.get_key(Header::new(Algorithm::RS256));
|
||||
assert!(k.await.is_ok());
|
||||
|
||||
let a = Authorizer::<Value>::build(&KeySourceType::EC("../config/ecdsa-public1.pem".to_owned()), None, None)
|
||||
.await
|
||||
.unwrap();
|
||||
let a = Authorizer::<Value>::build(
|
||||
&KeySourceType::EC("../config/ecdsa-public1.pem".to_owned()),
|
||||
None,
|
||||
None,
|
||||
Validation::new(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let k = a.key_source.get_key(Header::new(Algorithm::ES256));
|
||||
assert!(k.await.is_ok());
|
||||
|
||||
let a = Authorizer::<Value>::build(&KeySourceType::ED("../config/ed25519-public1.pem".to_owned()), None, None)
|
||||
.await
|
||||
.unwrap();
|
||||
let a = Authorizer::<Value>::build(
|
||||
&KeySourceType::ED("../config/ed25519-public1.pem".to_owned()),
|
||||
None,
|
||||
None,
|
||||
Validation::new(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let k = a.key_source.get_key(Header::new(Algorithm::EdDSA));
|
||||
assert!(k.await.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn build_file_errors() {
|
||||
let a = Authorizer::<Value>::build(&KeySourceType::RSA("./config/does-not-exist.pem".to_owned()), None, None).await;
|
||||
let a = Authorizer::<Value>::build(
|
||||
&KeySourceType::RSA("./config/does-not-exist.pem".to_owned()),
|
||||
None,
|
||||
None,
|
||||
Validation::new(),
|
||||
)
|
||||
.await;
|
||||
println!("{:?}", a.as_ref().err());
|
||||
assert!(a.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn build_jwks_url_error() {
|
||||
let a = Authorizer::<Value>::build(&KeySourceType::Jwks("://xxxx".to_owned()), None, None).await;
|
||||
let a =
|
||||
Authorizer::<Value>::build(&KeySourceType::Jwks("://xxxx".to_owned()), None, None, Validation::default()).await;
|
||||
println!("{:?}", a.as_ref().err());
|
||||
assert!(a.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn build_discovery_url_error() {
|
||||
let a = Authorizer::<Value>::build(&KeySourceType::Discovery("://xxxx".to_owned()), None, None).await;
|
||||
let a = Authorizer::<Value>::build(
|
||||
&KeySourceType::Discovery("://xxxx".to_owned()),
|
||||
None,
|
||||
None,
|
||||
Validation::default(),
|
||||
)
|
||||
.await;
|
||||
println!("{:?}", a.as_ref().err());
|
||||
assert!(a.is_err());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use tower_service::Service;
|
|||
use crate::authorizer::{Authorizer, FnClaimsChecker, KeySourceType};
|
||||
use crate::error::InitError;
|
||||
use crate::jwks::key_store_manager::Refresh;
|
||||
use crate::validation::Validation;
|
||||
use crate::{AuthError, RefreshStrategy};
|
||||
|
||||
/// Authorizer Layer builder
|
||||
|
|
@ -30,6 +31,7 @@ where
|
|||
key_source_type: KeySourceType,
|
||||
refresh: Option<Refresh>,
|
||||
claims_checker: Option<FnClaimsChecker<C>>,
|
||||
validation: Option<Validation>,
|
||||
}
|
||||
|
||||
/// authorization layer builder
|
||||
|
|
@ -43,6 +45,7 @@ where
|
|||
key_source_type: KeySourceType::Discovery(issuer.to_string()),
|
||||
refresh: Default::default(),
|
||||
claims_checker: None,
|
||||
validation: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,6 +55,7 @@ where
|
|||
key_source_type: KeySourceType::Jwks(url.to_owned()),
|
||||
refresh: Default::default(),
|
||||
claims_checker: None,
|
||||
validation: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -61,6 +65,7 @@ where
|
|||
key_source_type: KeySourceType::RSA(path.to_owned()),
|
||||
refresh: Default::default(),
|
||||
claims_checker: None,
|
||||
validation: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -70,6 +75,7 @@ where
|
|||
key_source_type: KeySourceType::EC(path.to_owned()),
|
||||
refresh: Default::default(),
|
||||
claims_checker: None,
|
||||
validation: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -79,6 +85,7 @@ where
|
|||
key_source_type: KeySourceType::ED(path.to_owned()),
|
||||
refresh: Default::default(),
|
||||
claims_checker: None,
|
||||
validation: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -88,6 +95,7 @@ where
|
|||
key_source_type: KeySourceType::Secret(secret),
|
||||
refresh: Default::default(),
|
||||
claims_checker: None,
|
||||
validation: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,9 +128,16 @@ where
|
|||
self
|
||||
}
|
||||
|
||||
pub fn validation(mut self, validation: Validation) -> JwtAuthorizer<C> {
|
||||
self.validation = Some(validation);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Build axum layer
|
||||
pub async fn layer(&self) -> Result<AsyncAuthorizationLayer<C>, InitError> {
|
||||
let auth = Arc::new(Authorizer::build(&self.key_source_type, self.claims_checker.clone(), self.refresh).await?);
|
||||
pub async fn layer(self) -> Result<AsyncAuthorizationLayer<C>, InitError> {
|
||||
let val = self.validation.unwrap_or_default();
|
||||
let auth = Arc::new(Authorizer::build(&self.key_source_type, self.claims_checker, self.refresh, val).await?);
|
||||
Ok(AsyncAuthorizationLayer::new(auth))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,14 @@ use serde::de::DeserializeOwned;
|
|||
pub use self::error::AuthError;
|
||||
pub use jwks::key_store_manager::{Refresh, RefreshStrategy};
|
||||
pub use layer::JwtAuthorizer;
|
||||
pub use validation::Validation;
|
||||
|
||||
pub mod authorizer;
|
||||
pub mod error;
|
||||
pub mod jwks;
|
||||
pub mod layer;
|
||||
mod oidc;
|
||||
pub mod validation;
|
||||
|
||||
/// Claims serialized using T
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
|
|
|
|||
129
jwt-authorizer/src/validation.rs
Normal file
129
jwt-authorizer/src/validation.rs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use jsonwebtoken::Algorithm;
|
||||
|
||||
/// Defines the jwt validation parameters (with defaults simplifying configuration).
|
||||
pub struct Validation {
|
||||
/// Add some leeway (in seconds) to the `exp` and `nbf` validation to
|
||||
/// account for clock skew.
|
||||
///
|
||||
/// Defaults to `60`.
|
||||
pub leeway: u64,
|
||||
/// Whether to validate the `exp` field.
|
||||
///
|
||||
/// Defaults to `true`.
|
||||
pub validate_exp: bool,
|
||||
/// Whether to validate the `nbf` field.
|
||||
///
|
||||
/// Defaults to `false`.
|
||||
pub validate_nbf: bool,
|
||||
/// If it contains a value, the validation will check that the `aud` claim value is in the values provided.
|
||||
///
|
||||
/// Defaults to `None`.
|
||||
pub aud: Option<Vec<String>>,
|
||||
/// If it contains a value, the validation will check that the `iss` claim value is in the values provided.
|
||||
///
|
||||
/// Defaults to `None`.
|
||||
pub iss: Option<Vec<String>>,
|
||||
|
||||
/// Whether to validate the JWT signature. Very insecure to turn that off!
|
||||
///
|
||||
/// Defaults to true.
|
||||
pub validate_signature: bool,
|
||||
}
|
||||
|
||||
impl Validation {
|
||||
/// new Validation with default values
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// check that the `iss` claim is a member of the values provided
|
||||
pub fn iss<T: ToString>(mut self, items: &[T]) -> Self {
|
||||
self.iss = Some(items.iter().map(|x| x.to_string()).collect());
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// check that the `aud` claim is a member of the items provided
|
||||
pub fn aud<T: ToString>(mut self, items: &[T]) -> Self {
|
||||
self.aud = Some(items.iter().map(|x| x.to_string()).collect());
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// enables or disables exp validation
|
||||
pub fn exp(mut self, val: bool) -> Self {
|
||||
self.validate_exp = val;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// enables or disables nbf validation
|
||||
pub fn nbf(mut self, val: bool) -> Self {
|
||||
self.validate_nbf = val;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Add some leeway (in seconds) to the `exp` and `nbf` validation to
|
||||
/// account for clock skew.
|
||||
pub fn leeway(mut self, value: u64) -> Self {
|
||||
self.leeway = value;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether to validate the JWT cryptographic signature
|
||||
/// Very insecure to turn that off, only do it if you know what you're doing.
|
||||
pub fn disable_validation(mut self) -> Self {
|
||||
self.validate_signature = false;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn to_jwt_validation(&self, alg: Algorithm) -> jsonwebtoken::Validation {
|
||||
let required_claims = if self.validate_exp {
|
||||
let mut claims = HashSet::with_capacity(1);
|
||||
claims.insert("exp".to_owned());
|
||||
claims
|
||||
} else {
|
||||
HashSet::with_capacity(0)
|
||||
};
|
||||
|
||||
let aud = self.aud.clone().map(|v| HashSet::from_iter(v.into_iter()));
|
||||
let iss = self.iss.clone().map(|v| HashSet::from_iter(v.into_iter()));
|
||||
|
||||
let mut jwt_validation = jsonwebtoken::Validation::default();
|
||||
|
||||
jwt_validation.required_spec_claims = required_claims;
|
||||
jwt_validation.leeway = self.leeway;
|
||||
jwt_validation.validate_exp = self.validate_exp;
|
||||
jwt_validation.validate_nbf = self.validate_nbf;
|
||||
jwt_validation.iss = iss;
|
||||
jwt_validation.aud = aud;
|
||||
jwt_validation.sub = None;
|
||||
jwt_validation.algorithms = vec![alg];
|
||||
if !self.validate_signature {
|
||||
jwt_validation.insecure_disable_signature_validation();
|
||||
}
|
||||
|
||||
jwt_validation
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Validation {
|
||||
fn default() -> Self {
|
||||
Validation {
|
||||
leeway: 60,
|
||||
|
||||
validate_exp: true,
|
||||
validate_nbf: false,
|
||||
|
||||
iss: None,
|
||||
aud: None,
|
||||
|
||||
validate_signature: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
47
jwt-authorizer/tests/common/mod.rs
Normal file
47
jwt-authorizer/tests/common/mod.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
lazy_static! {
|
||||
pub static ref JWKS_RSA1: Value = json!({
|
||||
"keys": [{
|
||||
"kty": "RSA",
|
||||
"n": "2pQeZdxa7q093K7bj5h6-leIpxfTnuAxzXdhjfGEJHxmt2ekHyCBWWWXCBiDn2RTcEBcy6gZqOW45Uy_tw-5e-Px1xFj1PykGEkRlOpYSAeWsNaAWvvpGB9m4zQ0PgZeMDDXE5IIBrY6YAzmGQxV-fcGGLhJnXl0-5_z7tKC7RvBoT3SGwlc_AmJqpFtTpEBn_fDnyqiZbpcjXYLExFpExm41xDitRKHWIwfc3dV8_vlNntlxCPGy_THkjdXJoHv2IJmlhvmr5_h03iGMLWDKSywxOol_4Wc1BT7Hb6byMxW40GKwSJJ4p7W8eI5mqggRHc8jlwSsTN9LZ2VOvO-XiVShZRVg7JeraGAfWwaIgIJ1D8C1h5Pi0iFpp2suxpHAXHfyLMJXuVotpXbDh4NDX-A4KRMgaxcfAcui_x6gybksq6gF90-9nfQfmVMVJctZ6M-FvRr-itd1Nef5WAtwUp1qyZygAXU3cH3rarscajmurOsP6dE1OHl3grY_eZhQxk33VBK9lavqNKPg6Q_PLiq1ojbYBj3bcYifJrsNeQwxldQP83aWt5rGtgZTehKVJwa40Uy_Grae1iRnsDtdSy5sTJIJ6EiShnWAdMoGejdiI8vpkjrdU8SWH8lv1KXI54DsbyAuke2cYz02zPWc6JEotQqI0HwhzU0KHyoY4s",
|
||||
"e": "AQAB",
|
||||
"kid": "rsa01",
|
||||
"alg": "RS256",
|
||||
"use": "sig"
|
||||
}]
|
||||
});
|
||||
pub static ref JWKS_RSA2: Value = json!({
|
||||
"keys": [{
|
||||
"kty": "RSA",
|
||||
"n": "yRE6rHuNR0QbHO3H3Kt2pOKGVhQqGZXInOduQNxXzuKlvQTLUTv4l4sggh5_CYYi_cvI-SXVT9kPWSKXxJXBXd_4LkvcPuUakBoAkfh-eiFVMh2VrUyWyj3MFl0HTVF9KwRXLAcwkREiS3npThHRyIxuy0ZMeZfxVL5arMhw1SRELB8HoGfG_AtH89BIE9jDBHZ9dLelK9a184zAf8LwoPLxvJb3Il5nncqPcSfKDDodMFBIMc4lQzDKL5gvmiXLXB1AGLm8KBjfE8s3L5xqi-yUod-j8MtvIj812dkS4QMiRVN_by2h3ZY8LYVGrqZXZTcgn2ujn8uKjXLZVD5TdQ",
|
||||
"e": "AQAB",
|
||||
"kid": "rsa02",
|
||||
"alg": "RS256",
|
||||
"use": "sig"
|
||||
}]
|
||||
});
|
||||
pub static ref JWKS_EC1: Value = json!({
|
||||
"keys": [{
|
||||
"kty": "EC",
|
||||
"crv": "P-256",
|
||||
"x": "MZiwc5EVP_E3vkd2oKedr4lWVMN9vgdyBBpBIVFJjwY",
|
||||
"y": "1npLU75B6M0mb01zUAVoeYJSDOlQJmvjBdqLPjJvy3Y",
|
||||
"kid": "ec01",
|
||||
"alg": "ES256",
|
||||
"use": "sig"
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
pub const JWT_RSA1_OK: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6InJzYTAxIn0.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDEiLCJzdWIiOiJiQGIuY29tIiwiYXVkIjoiYXVkMSIsImV4cCI6MjAwMDAwMDAwMCwibmJmIjoxNTE2MjM5MDIyfQ.d29YS5U-Cfur1DDxOeiYBxlEzRFQrVovuFdyIlrMpAfLLWudtpqiMxHKfTEM0ohHrk4ahf7nWhMamuiQUOEZSYpx7ze-4f47FGU4RxFLVSZw7VUWFrNYKkmRlgFsCbpdRLb5in6YDIaqrUnr2tqF9c3vUo_15lLgNn__xDG9_49A8UvbNFGvDm_z53aYGBPdgWVmwrRU5lmHH0tYcLMyiqQKfnM4jr__klIeVGBpJ2V_2qZyHvvevEtiiV7EGWZxaA-cYzeaO-_24nVBvPYrcdib84pz-a4JWhmnUobfzvtbdKEy12abxB2TvpzikBbX5etiDx92cPP_9Kf_51BncmwC_anRIwCSEe5TEgduihYS9yucOGgjP09sjlPPvdGAE6vcl35eR2fizJo7KU6Ol8DoUSDMhuPS-KQ_bFpCDK1iPtsXw514WZQZL1qXF61yd5QZ3wvckR8s_pV0XcFHWg_TpupNC3Yn6zlYU9l8NLkWiIudJVAM2pe-MSu292FyR2ytLISrNqHtk-e4_MIoviqyswvmtHZivoFWkq_CE2V9RyLX4WXaVEJLf4FihfCMFGZVfON2B8N2PfoPMuAlE1otQerbKwwR_TYjOFJRG1HdIDqvNDQ-LeJDKKX0NzCHwoJIqC-X7m6F1QIcaupOWnXyoSndvsi6g1jAlP_fTCI";
|
||||
pub const JWT_RSA2_OK: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6InJzYTAyIn0.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDEiLCJzdWIiOiJiQGIuY29tIiwiZXhwIjoyMDAwMDAwMDAwLCJuYmYiOjE1MTYyMzkwMjJ9.tWyA4ve2CY6GruBch_qIf8f1PgCEhqmrZ1J5XBuwO_v-P-PSLe3MWpkPAMdIDE5QE19ItUcGdJblhiyPb0tJJtrDHVYER7q8X4fOjQjY_NlFK6Bd1GtZS2DCA5EPxIX8l7Jpn8fPvbyamagLwnB_waQaYBteTGnOkLmz3F3sqC8KdO9lyu5v7BknC1f56ZOvr_DiInkTiAsTWqX4nS2KYRjcz4HcxcPO7O0CFXqcOTF_e3ntmq4rQV9LHCaEnuXj2WZtnX423CMkcG0uYzsnmWAMPB6IlDKejPnAJThMjjuJhze1gGbP1U8c53UbEhfHEZgJ2N634YEXMfsojZ5VzQ";
|
||||
pub const JWT_EC1_OK: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImVjMDEifQ.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDEiLCJzdWIiOiJiQGIuY29tIiwiYXVkIjoiYXVkMSIsImV4cCI6MjAwMDAwMDAwMCwibmJmIjoxNTE2MjM5MDIyfQ.orTVTRdQnktCg1Ar_mo9IvN__5-s-q5oCZaUUWaL2I8HAkIq68GV6ACqvhrxQMB-OInX0hY9pBWGYbrFJjCwKA";
|
||||
pub const JWT_EC2_OK: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImVjMDIifQ.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDEiLCJzdWIiOiJiQGIuY29tIiwiYXVkIjoiYXVkMSIsImV4cCI6MjAwMDAwMDAwMCwibmJmIjoxNTE2MjM5MDIyfQ.QnsVM9CA11VpwDr6e_aHzxlLSXTQ7yVH5oxTR1yIWBPKnosjk1EIIBMcSjD81fZCrON2kX4TNkfSCxSCL8GI3g";
|
||||
pub const JWT_EC1_EXP_KO: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImVjMDEifQ.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDEiLCJzdWIiOiJib2IiLCJleHAiOjE1MTYyMzkwMjIsIm5iZiI6MTUxNjIzOTAyMn0.MNmY66S3NgSAbWwZP0hfC5pme3SM7B3yvFhBFLQH-cU3enP0G8bBzDOhpjmli9uKQitkIQxffwu2Au9wTUraTQ";
|
||||
pub const JWT_EC1_NBF_KO: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImVjMDEifQ.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDEiLCJzdWIiOiJib2IiLCJleHAiOjIwMDAwMDAwMDAsIm5iZiI6MjAwMDAwMDAwMH0.d5MRfwcToMxR7O7NEt3qUj-MUKKpG9BZW1w6ihyfN95ZULoMajr7mtYY2R2LS96oBYgp3OdlR4tkHmdqDpvCSA";
|
||||
pub const JWT_ED1_OK: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImVkMDEifQ.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDEiLCJzdWIiOiJiQGIuY29tIiwiYXVkIjoiYXVkMSIsImV4cCI6MjAwMDAwMDAwMCwibmJmIjoxNTE2MjM5MDIyfQ.U2eaP1EzRiLDRRPJTVjOMy4y40uAiW8MeryWJAjU-QPxU_PnuzatvrRjntTcdW7hXx0EWIezecJuXzp2UrBqAw";
|
||||
pub const JWT_ED2_OK: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImVkMDIifQ.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDEiLCJzdWIiOiJiQGIuY29tIiwiYXVkIjoiYXVkMSIsImV4cCI6MjAwMDAwMDAwMCwibmJmIjoxNTE2MjM5MDIyfQ.xFrGhImKI1irksznuU9DoLk24bbdHhurbVRoUdZSb_FNlav1Jw49eMyKfeJUPy8IdMCtnG33K9xHuCRjm5IcAA";
|
||||
|
|
@ -14,52 +14,15 @@ use hyper::Body;
|
|||
use jwt_authorizer::{JwtAuthorizer, JwtClaims, Refresh, RefreshStrategy};
|
||||
use lazy_static::lazy_static;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::Value;
|
||||
use tower::Service;
|
||||
use tower::ServiceExt;
|
||||
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||
|
||||
lazy_static! {
|
||||
static ref JWKS_RSA1: Value = json!({
|
||||
"keys": [{
|
||||
"kty": "RSA",
|
||||
"n": "2pQeZdxa7q093K7bj5h6-leIpxfTnuAxzXdhjfGEJHxmt2ekHyCBWWWXCBiDn2RTcEBcy6gZqOW45Uy_tw-5e-Px1xFj1PykGEkRlOpYSAeWsNaAWvvpGB9m4zQ0PgZeMDDXE5IIBrY6YAzmGQxV-fcGGLhJnXl0-5_z7tKC7RvBoT3SGwlc_AmJqpFtTpEBn_fDnyqiZbpcjXYLExFpExm41xDitRKHWIwfc3dV8_vlNntlxCPGy_THkjdXJoHv2IJmlhvmr5_h03iGMLWDKSywxOol_4Wc1BT7Hb6byMxW40GKwSJJ4p7W8eI5mqggRHc8jlwSsTN9LZ2VOvO-XiVShZRVg7JeraGAfWwaIgIJ1D8C1h5Pi0iFpp2suxpHAXHfyLMJXuVotpXbDh4NDX-A4KRMgaxcfAcui_x6gybksq6gF90-9nfQfmVMVJctZ6M-FvRr-itd1Nef5WAtwUp1qyZygAXU3cH3rarscajmurOsP6dE1OHl3grY_eZhQxk33VBK9lavqNKPg6Q_PLiq1ojbYBj3bcYifJrsNeQwxldQP83aWt5rGtgZTehKVJwa40Uy_Grae1iRnsDtdSy5sTJIJ6EiShnWAdMoGejdiI8vpkjrdU8SWH8lv1KXI54DsbyAuke2cYz02zPWc6JEotQqI0HwhzU0KHyoY4s",
|
||||
"e": "AQAB",
|
||||
"kid": "rsa01",
|
||||
"alg": "RS256",
|
||||
"use": "sig"
|
||||
}]
|
||||
});
|
||||
static ref JWKS_RSA2: Value = json!({
|
||||
"keys": [{
|
||||
"kty": "RSA",
|
||||
"n": "yRE6rHuNR0QbHO3H3Kt2pOKGVhQqGZXInOduQNxXzuKlvQTLUTv4l4sggh5_CYYi_cvI-SXVT9kPWSKXxJXBXd_4LkvcPuUakBoAkfh-eiFVMh2VrUyWyj3MFl0HTVF9KwRXLAcwkREiS3npThHRyIxuy0ZMeZfxVL5arMhw1SRELB8HoGfG_AtH89BIE9jDBHZ9dLelK9a184zAf8LwoPLxvJb3Il5nncqPcSfKDDodMFBIMc4lQzDKL5gvmiXLXB1AGLm8KBjfE8s3L5xqi-yUod-j8MtvIj812dkS4QMiRVN_by2h3ZY8LYVGrqZXZTcgn2ujn8uKjXLZVD5TdQ",
|
||||
"e": "AQAB",
|
||||
"kid": "rsa02",
|
||||
"alg": "RS256",
|
||||
"use": "sig"
|
||||
}]
|
||||
});
|
||||
static ref JWKS_EC1: Value = json!({
|
||||
"keys": [{
|
||||
"kty": "EC",
|
||||
"crv": "P-256",
|
||||
"x": "MZiwc5EVP_E3vkd2oKedr4lWVMN9vgdyBBpBIVFJjwY",
|
||||
"y": "1npLU75B6M0mb01zUAVoeYJSDOlQJmvjBdqLPjJvy3Y",
|
||||
"kid": "ec01",
|
||||
"alg": "ES256",
|
||||
"use": "sig"
|
||||
}]
|
||||
});
|
||||
}
|
||||
use crate::common::{JWT_RSA1_OK, JWT_RSA2_OK};
|
||||
|
||||
const JWT_RSA1_OK: &str = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6InJzYTAxIn0.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDEiLCJzdWIiOiJiQGIuY29tIiwiZXhwIjoyMDAwMDAwMDAwLCJuYmYiOjE1MTYyMzkwMjJ9.pmm8Kdk-SvycXIGpWb1R0DuP5nlB7w4QQS7trhN_OjOpbk0A8F_lC4BdClz3rol2Pgo61lcFckJgjNBj34DQGeTGOtvxdiUXNgi1aKiXH4AyPzZeZx30PgFxa1fxhuZhBAj6xIZKBSBQvVyjeVQzAScINRCBX8zfCaXSU1ZCUkJl5vbD7zT-cYIFU76we9HcIYKRXwTiAyoNn3Lixa1H3_t5sbx3om2WlIB2x-sGpoDFDjorcuJT1yQx3grTRTBzHyRBRjZ3e8wrMbiacy-m3WoEFdkssQgYi_dSQH0hvxgacvGWayK0UqD7O5UL6EzTA2feXbgA_68o5gfvSnM8CUsPut5gZr-gwVbQKPbBdCQtl_wXIMot7UNKYEiFV38x5EmUr-ShzQcditW6fciguuY1Qav502UE1UMXvt5p8-kYxw2AaaVd6iTgQBzkBrtvywMYWzIwzGNA70RvUhI2rlgcn8GEU_51Tv_NMHjp6CjDbAxQVKa0PlcRE4pd6yk_IJSR4Nska_8BQZdPbsFn--z_XHEDoRZQ1C1M6m77xVndg3zX0sNQPXfWsttCbBmaHvMKTOp0cH9rlWB9r9nTo9fn8jcfqlak2O2IAzfzsOdVfUrES6T1UWkWobs9usGgqJuIkZHbDd4tmXyPRT4wrU7hxEyE9cuvuZPAi8GYt80";
|
||||
const JWT_RSA2_OK: &str = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6InJzYTAyIn0.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDEiLCJzdWIiOiJiQGIuY29tIiwiZXhwIjoyMDAwMDAwMDAwLCJuYmYiOjE1MTYyMzkwMjJ9.tWyA4ve2CY6GruBch_qIf8f1PgCEhqmrZ1J5XBuwO_v-P-PSLe3MWpkPAMdIDE5QE19ItUcGdJblhiyPb0tJJtrDHVYER7q8X4fOjQjY_NlFK6Bd1GtZS2DCA5EPxIX8l7Jpn8fPvbyamagLwnB_waQaYBteTGnOkLmz3F3sqC8KdO9lyu5v7BknC1f56ZOvr_DiInkTiAsTWqX4nS2KYRjcz4HcxcPO7O0CFXqcOTF_e3ntmq4rQV9LHCaEnuXj2WZtnX423CMkcG0uYzsnmWAMPB6IlDKejPnAJThMjjuJhze1gGbP1U8c53UbEhfHEZgJ2N634YEXMfsojZ5VzQ";
|
||||
// const JWT_EC1_OK: &str = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImVjMDEifQ.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDEiLCJzdWIiOiJiQGIuY29tIiwiZXhwIjoyMDAwMDAwMDAwLCJuYmYiOjE1MTYyMzkwMjJ9.AsAX8XQdsQMI7NGNJOPE8LFFaKJ_nYXeKBwl2NZACbPhCiRj7FgxIw0UVcpmRVzK0BNbb9S4lFocaTLo9DsCeQ";
|
||||
// const JWT_EC2_OK: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImVjMDIifQ.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDEiLCJzdWIiOiJiQGIuY29tIiwiZXhwIjoyMDAwMDAwMDAwLCJuYmYiOjE1MTYyMzkwMjJ9.DJFNPyfuL5-ifcAxRCvneo7SdtDu0cfJyYmv2Gl4rmJOjKlzDx3GDamYa0cGLy8zcYYdpDMJ-s1WKzlGC_Hiyw";
|
||||
// const JWT_ED1_OK: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImVkMDEifQ.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDEiLCJzdWIiOiJiQGIuY29tIiwiZXhwIjoyMDAwMDAwMDAwLCJuYmYiOjE1MTYyMzkwMjJ9.5bFOZqc-lBFy4gFifQ_CTx1A3R6Nry71gdi7KH2GGvTZQC_ZI1vNbqGnWQhpR6n_jUd9ICUc0pPI5iLCB6K1Bg";
|
||||
// const JWT_ED2_OK: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImVkMDIifQ.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDEiLCJzdWIiOiJiQGIuY29tIiwiZXhwIjoyMDAwMDAwMDAwLCJuYmYiOjE1MTYyMzkwMjJ9.Yfe88E26UEJ8x13h8xv2XtBrQ7O5E5UtS9t6-hRbo_pMSxKui13X0uNleRPHaZFfzK4AO033m8gHYHxQDLkTCg";
|
||||
mod common;
|
||||
|
||||
/// Static variable to ensure that logging is only initialized once.
|
||||
pub static INITIALIZED: Once = Once::new();
|
||||
|
|
@ -99,7 +62,7 @@ fn discovery(uri: &str) -> Json<Value> {
|
|||
async fn jwks() -> Json<Value> {
|
||||
Arc::clone(&JWKS_COUNTER).fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
Json(JWKS_RSA1.clone())
|
||||
Json(common::JWKS_RSA1.clone())
|
||||
}
|
||||
|
||||
fn run_jwks_server() -> String {
|
||||
|
|
@ -166,7 +129,7 @@ async fn make_proteced_request(app: &mut Router, bearer: &str) -> Response {
|
|||
.call(
|
||||
Request::builder()
|
||||
.uri("/protected")
|
||||
.header("Authorization", bearer)
|
||||
.header("Authorization", format!("Bearer {bearer}"))
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
mod common;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use axum::{
|
||||
|
|
@ -8,11 +10,11 @@ mod tests {
|
|||
Router,
|
||||
};
|
||||
use http::{header, HeaderValue};
|
||||
use jwt_authorizer::{JwtAuthorizer, JwtClaims};
|
||||
use jwt_authorizer::{validation::Validation, JwtAuthorizer, JwtClaims};
|
||||
use serde::Deserialize;
|
||||
use tower::ServiceExt;
|
||||
|
||||
const JWT_RSA_OK: &str = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtleS1yc2EifQ.eyJzdWIiOiJiQGIuY29tIiwiZXhwIjoyMDAwMDAwMDAwfQ.K9QFjvVquRF2-Wt1QRfipOGwiYsmRs7SAwqKskHemFb9BRRZutpfV4oEoHaXMLomTUe8rH0TMjpKcweYK_H1I8D4r-mAN216oUfxCQiFWDB8T2VBI8um-efUg67i2myDZJr5VXdZH8ywj7bn9LyNS4I_xT-J3XvsngeCpuxVSRiYu4FkcUkLrPzbu2sDyBXFqYO9FOorZ8sl0Ninc93fWT2uUrEG8jRyWCa4xpoqbKbm7CN7T2tOKF7mx_xdSPTeSM-U9mUiHsMOrXi1S05IM0hvNJrBduLS6sMTFWrVhis6zqnuxDOirwZS-aN0_SgMDnZTFPsCh8dkqFde1Pv1IYjZfr5OOHjQ9QWj6UDjam6M1eWVPK6QLlxv5bU_gnlAiHm9wJX38-REwmVhIJIBzKxsgJAu1gnRBxe36OM3rkgYxpB86YvfDyOoFlqx8erdxYv38AtvJibe4HB6KLndp_QMm5XXQsbfyEXWGe8hzDwozdhGeQsJXz7PcI3KPlv19PrUM8njElFpOiyfAEXwbtp1EZTzMZ4ZNF6LLFy1fpLcosgyp05o_2YMvngltSnN3v0IPncJx50StdYsoxPN9Ac_nH8VbNlHfmPHMklD1plof0pYf5SiL8yCQP9Uiw9NrN2PeQzbveMKF1T1UNbn2tefxoxr3k6sgWiMH_g_kkk";
|
||||
use crate::common;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
struct User {
|
||||
|
|
@ -33,7 +35,7 @@ mod tests {
|
|||
.oneshot(
|
||||
Request::builder()
|
||||
.uri("/protected")
|
||||
.header("Authorization", bearer)
|
||||
.header("Authorization", format!("Bearer {bearer}"))
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
|
|
@ -63,10 +65,24 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn protected_with_jwt() {
|
||||
let response = make_proteced_request(JwtAuthorizer::from_rsa_pem("../config/rsa-public1.pem"), JWT_RSA_OK).await;
|
||||
|
||||
let response = make_proteced_request(
|
||||
JwtAuthorizer::from_ed_pem("../config/ed25519-public2.pem"),
|
||||
common::JWT_ED2_OK,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
|
||||
assert_eq!(&body[..], b"hello: b@b.com");
|
||||
|
||||
let response =
|
||||
make_proteced_request(JwtAuthorizer::from_ec_pem("../config/ecdsa-public2.pem"), common::JWT_EC2_OK).await;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
|
||||
assert_eq!(&body[..], b"hello: b@b.com");
|
||||
|
||||
let response =
|
||||
make_proteced_request(JwtAuthorizer::from_rsa_pem("../config/rsa-public2.pem"), common::JWT_RSA2_OK).await;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
|
||||
assert_eq!(&body[..], b"hello: b@b.com");
|
||||
}
|
||||
|
|
@ -82,16 +98,16 @@ mod tests {
|
|||
#[tokio::test]
|
||||
async fn protected_with_claims_check() {
|
||||
let rsp_ok = make_proteced_request(
|
||||
JwtAuthorizer::from_rsa_pem("../config/rsa-public1.pem").check(|_| true),
|
||||
JWT_RSA_OK,
|
||||
JwtAuthorizer::from_rsa_pem("../config/rsa-public2.pem").check(|_| true),
|
||||
common::JWT_RSA2_OK,
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(rsp_ok.status(), StatusCode::OK);
|
||||
|
||||
let rsp_ko = make_proteced_request(
|
||||
JwtAuthorizer::from_rsa_pem("../config/rsa-public1.pem").check(|_| false),
|
||||
JWT_RSA_OK,
|
||||
JwtAuthorizer::from_rsa_pem("../config/rsa-public2.pem").check(|_| false),
|
||||
common::JWT_RSA2_OK,
|
||||
)
|
||||
.await;
|
||||
|
||||
|
|
@ -110,7 +126,8 @@ mod tests {
|
|||
// but should be 500 when checking.
|
||||
#[tokio::test]
|
||||
async fn protected_with_bad_jwks_url() {
|
||||
let response = make_proteced_request(JwtAuthorizer::from_jwks_url("http://bad-url/xxx/yyy"), JWT_RSA_OK).await;
|
||||
let response =
|
||||
make_proteced_request(JwtAuthorizer::from_jwks_url("http://bad-url/xxx/yyy"), common::JWT_RSA1_OK).await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
|
@ -128,4 +145,140 @@ mod tests {
|
|||
|
||||
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
// --------------------
|
||||
// VALIDATION
|
||||
// ---------------------
|
||||
#[tokio::test]
|
||||
async fn validate_signature() {
|
||||
let response = make_proteced_request(
|
||||
JwtAuthorizer::from_rsa_pem("../config/rsa-public1.pem").validation(Validation::new().disable_validation()),
|
||||
common::JWT_EC2_OK,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let response = make_proteced_request(
|
||||
JwtAuthorizer::from_rsa_pem("../config/rsa-public1.pem").validation(Validation::new()),
|
||||
common::JWT_EC2_OK,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn validate_iss() {
|
||||
let response = make_proteced_request(
|
||||
JwtAuthorizer::from_ec_pem("../config/ecdsa-public1.pem").validation(Validation::new().iss(&["bad-iss"])),
|
||||
common::JWT_EC1_OK,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||
|
||||
let response = make_proteced_request(
|
||||
JwtAuthorizer::from_ec_pem("../config/ecdsa-public1.pem").validation(Validation::new()),
|
||||
common::JWT_EC1_OK,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let response = make_proteced_request(
|
||||
JwtAuthorizer::from_ec_pem("../config/ecdsa-public1.pem")
|
||||
.validation(Validation::new().iss(&["http://localhost:3001"])),
|
||||
common::JWT_EC1_OK,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn validate_aud() {
|
||||
let response = make_proteced_request(
|
||||
JwtAuthorizer::from_ed_pem("../config/ed25519-public1.pem").validation(Validation::new().aud(&["bad-aud"])),
|
||||
common::JWT_ED1_OK,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||
|
||||
let response = make_proteced_request(
|
||||
JwtAuthorizer::from_ed_pem("../config/ed25519-public1.pem").validation(Validation::new()),
|
||||
common::JWT_ED1_OK,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let response = make_proteced_request(
|
||||
JwtAuthorizer::from_ed_pem("../config/ed25519-public1.pem").validation(Validation::new().aud(&["aud1"])),
|
||||
common::JWT_ED1_OK,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn validate_exp() {
|
||||
// DEFAULT -> ENABLED
|
||||
let response = make_proteced_request(
|
||||
JwtAuthorizer::from_ec_pem("../config/ecdsa-public1.pem").validation(Validation::new()),
|
||||
common::JWT_EC1_EXP_KO,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||
|
||||
// DISABLED
|
||||
let response = make_proteced_request(
|
||||
JwtAuthorizer::from_ec_pem("../config/ecdsa-public1.pem").validation(Validation::new().exp(false)),
|
||||
common::JWT_EC1_EXP_KO,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
// ENABLED
|
||||
let response = make_proteced_request(
|
||||
JwtAuthorizer::from_ec_pem("../config/ecdsa-public1.pem").validation(Validation::new().exp(true)),
|
||||
common::JWT_EC1_EXP_KO,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||
let response = make_proteced_request(
|
||||
JwtAuthorizer::from_ec_pem("../config/ecdsa-public1.pem").validation(Validation::new().exp(true)),
|
||||
common::JWT_EC1_OK,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn validate_nbf() {
|
||||
// DEFAULT -> DISABLED
|
||||
let response = make_proteced_request(
|
||||
JwtAuthorizer::from_ec_pem("../config/ecdsa-public1.pem").validation(Validation::new()),
|
||||
common::JWT_EC1_NBF_KO,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
// DISABLED
|
||||
let response = make_proteced_request(
|
||||
JwtAuthorizer::from_ec_pem("../config/ecdsa-public1.pem").validation(Validation::new().nbf(false)),
|
||||
common::JWT_EC1_NBF_KO,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
// ENABLED
|
||||
let response = make_proteced_request(
|
||||
JwtAuthorizer::from_ec_pem("../config/ecdsa-public1.pem").validation(Validation::new().nbf(true)),
|
||||
common::JWT_EC1_NBF_KO,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||
|
||||
let response = make_proteced_request(
|
||||
JwtAuthorizer::from_ec_pem("../config/ecdsa-public1.pem").validation(Validation::new().nbf(true)),
|
||||
common::JWT_EC1_OK,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue