mirror of
https://github.com/TECHNOFAB11/jwt-authorizer.git
synced 2025-12-11 23:50:07 +01:00
feat: integration tests
This commit is contained in:
parent
a58b367c90
commit
ae44a8e961
6 changed files with 342 additions and 19 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -726,6 +726,7 @@ dependencies = [
|
||||||
"http",
|
"http",
|
||||||
"hyper",
|
"hyper",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
|
"lazy_static",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,17 @@ GET http://localhost:3000/public
|
||||||
### Protected RSA
|
### Protected RSA
|
||||||
GET http://localhost:3000/api/protected
|
GET http://localhost:3000/api/protected
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtleS1yc2EifQ.eyJzdWIiOiJiQGIuY29tIiwiZXhwIjoyMDAwMDAwMDAwfQ.K9QFjvVquRF2-Wt1QRfipOGwiYsmRs7SAwqKskHemFb9BRRZutpfV4oEoHaXMLomTUe8rH0TMjpKcweYK_H1I8D4r-mAN216oUfxCQiFWDB8T2VBI8um-efUg67i2myDZJr5VXdZH8ywj7bn9LyNS4I_xT-J3XvsngeCpuxVSRiYu4FkcUkLrPzbu2sDyBXFqYO9FOorZ8sl0Ninc93fWT2uUrEG8jRyWCa4xpoqbKbm7CN7T2tOKF7mx_xdSPTeSM-U9mUiHsMOrXi1S05IM0hvNJrBduLS6sMTFWrVhis6zqnuxDOirwZS-aN0_SgMDnZTFPsCh8dkqFde1Pv1IYjZfr5OOHjQ9QWj6UDjam6M1eWVPK6QLlxv5bU_gnlAiHm9wJX38-REwmVhIJIBzKxsgJAu1gnRBxe36OM3rkgYxpB86YvfDyOoFlqx8erdxYv38AtvJibe4HB6KLndp_QMm5XXQsbfyEXWGe8hzDwozdhGeQsJXz7PcI3KPlv19PrUM8njElFpOiyfAEXwbtp1EZTzMZ4ZNF6LLFy1fpLcosgyp05o_2YMvngltSnN3v0IPncJx50StdYsoxPN9Ac_nH8VbNlHfmPHMklD1plof0pYf5SiL8yCQP9Uiw9NrN2PeQzbveMKF1T1UNbn2tefxoxr3k6sgWiMH_g_kkk
|
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6InJzYTAxIn0.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDEiLCJzdWIiOiJiQGIuY29tIiwiZXhwIjoyMDAwMDAwMDAwLCJuYmYiOjE1MTYyMzkwMjJ9.pmm8Kdk-SvycXIGpWb1R0DuP5nlB7w4QQS7trhN_OjOpbk0A8F_lC4BdClz3rol2Pgo61lcFckJgjNBj34DQGeTGOtvxdiUXNgi1aKiXH4AyPzZeZx30PgFxa1fxhuZhBAj6xIZKBSBQvVyjeVQzAScINRCBX8zfCaXSU1ZCUkJl5vbD7zT-cYIFU76we9HcIYKRXwTiAyoNn3Lixa1H3_t5sbx3om2WlIB2x-sGpoDFDjorcuJT1yQx3grTRTBzHyRBRjZ3e8wrMbiacy-m3WoEFdkssQgYi_dSQH0hvxgacvGWayK0UqD7O5UL6EzTA2feXbgA_68o5gfvSnM8CUsPut5gZr-gwVbQKPbBdCQtl_wXIMot7UNKYEiFV38x5EmUr-ShzQcditW6fciguuY1Qav502UE1UMXvt5p8-kYxw2AaaVd6iTgQBzkBrtvywMYWzIwzGNA70RvUhI2rlgcn8GEU_51Tv_NMHjp6CjDbAxQVKa0PlcRE4pd6yk_IJSR4Nska_8BQZdPbsFn--z_XHEDoRZQ1C1M6m77xVndg3zX0sNQPXfWsttCbBmaHvMKTOp0cH9rlWB9r9nTo9fn8jcfqlak2O2IAzfzsOdVfUrES6T1UWkWobs9usGgqJuIkZHbDd4tmXyPRT4wrU7hxEyE9cuvuZPAi8GYt80
|
||||||
|
|
||||||
### Protected EC
|
### Protected EC
|
||||||
GET http://localhost:3000/api/protected
|
GET http://localhost:3000/api/protected
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImtleS1lYyJ9.eyJzdWIiOiJiQGIuY29tIiwiZXhwIjoyMDAwMDAwMDAwfQ.r0qaeYJWhjEybhNPgrrFwLBTeLMYtL4IJqOxZyxH9m-JYWqzV0R0WyYQIkf_tQ1UmzqHc9_xzUZnzSjTeEwDHw
|
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImVjMDEifQ.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDEiLCJzdWIiOiJiQGIuY29tIiwiZXhwIjoyMDAwMDAwMDAwLCJuYmYiOjE1MTYyMzkwMjJ9.YMQHWpGLJ3P59SvPX-RIW3uT5rfzShzcP1TNcaXr0VnsxCXYO0og0c3_O30no0D_ct0hOUJINY5tBsok-66Gzw
|
||||||
|
|
||||||
### Protected Ed
|
### Protected Ed
|
||||||
GET http://localhost:3000/api/protected
|
GET http://localhost:3000/api/protected
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImtleS1lZCJ9.eyJzdWIiOiJiQGIuY29tIiwiZXhwIjoyMDAwMDAwMDAwfQ.XAx9msioheXEH1XUEIWMHGBg25JOpBHqcgL_ou_S3fwVht2TbKRiDZ4G6ZyEtn57hCbOy250zTD_g0EbaMGwAg
|
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImVkMDEifQ.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDEiLCJzdWIiOiJiQGIuY29tIiwiZXhwIjoyMDAwMDAwMDAwLCJuYmYiOjE1MTYyMzkwMjJ9.5bFOZqc-lBFy4gFifQ_CTx1A3R6Nry71gdi7KH2GGvTZQC_ZI1vNbqGnWQhpR6n_jUd9ICUc0pPI5iLCB6K1Bg
|
||||||
|
|
||||||
### 401 (no token)
|
### 401 (no token)
|
||||||
GET http://localhost:3000/api/protected
|
GET http://localhost:3000/api/protected
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ struct User {
|
||||||
async fn main() -> Result<(), InitError> {
|
async fn main() -> Result<(), InitError> {
|
||||||
tracing_subscriber::registry()
|
tracing_subscriber::registry()
|
||||||
.with(tracing_subscriber::EnvFilter::new(
|
.with(tracing_subscriber::EnvFilter::new(
|
||||||
std::env::var("RUST_LOG").unwrap_or_else(|_| "info,axum_poc=debug,tower_http=debug".into()),
|
std::env::var("RUST_LOG").unwrap_or_else(|_| "info,jwt_authorizer=debug,tower_http=debug".into()),
|
||||||
))
|
))
|
||||||
.with(tracing_subscriber::fmt::layer())
|
.with(tracing_subscriber::fmt::layer())
|
||||||
.init();
|
.init();
|
||||||
|
|
|
||||||
|
|
@ -37,42 +37,42 @@ async fn jwks() -> Json<Value> {
|
||||||
|
|
||||||
let keypair = RsaKeyPair::from_pem(include_bytes!("../../../config/jwtRS256.key")).unwrap();
|
let keypair = RsaKeyPair::from_pem(include_bytes!("../../../config/jwtRS256.key")).unwrap();
|
||||||
let mut pk = keypair.to_jwk_public_key();
|
let mut pk = keypair.to_jwk_public_key();
|
||||||
pk.set_key_id("key-rsa");
|
pk.set_key_id("rsa01");
|
||||||
pk.set_algorithm("RS256");
|
pk.set_algorithm("RS256");
|
||||||
pk.set_key_use("sig");
|
pk.set_key_use("sig");
|
||||||
kset.keys.push(pk);
|
kset.keys.push(pk);
|
||||||
|
|
||||||
let keypair = RsaKeyPair::from_pem(include_bytes!("../../../config/private_rsa_key_pkcs8.pem")).unwrap();
|
let keypair = RsaKeyPair::from_pem(include_bytes!("../../../config/private_rsa_key_pkcs8.pem")).unwrap();
|
||||||
let mut pk = keypair.to_jwk_public_key();
|
let mut pk = keypair.to_jwk_public_key();
|
||||||
pk.set_key_id("rsa01");
|
pk.set_key_id("rsa02");
|
||||||
pk.set_algorithm("RS256");
|
pk.set_algorithm("RS256");
|
||||||
pk.set_key_use("sig");
|
pk.set_key_use("sig");
|
||||||
kset.keys.push(pk);
|
kset.keys.push(pk);
|
||||||
|
|
||||||
let keypair = EcKeyPair::from_pem(include_bytes!("../../../config/ec256-private.pem"), Some(EcCurve::P256)).unwrap();
|
let keypair = EcKeyPair::from_pem(include_bytes!("../../../config/ec256-private.pem"), Some(EcCurve::P256)).unwrap();
|
||||||
let mut pk = keypair.to_jwk_public_key();
|
let mut pk = keypair.to_jwk_public_key();
|
||||||
pk.set_key_id("key-ec");
|
pk.set_key_id("ec01");
|
||||||
pk.set_algorithm("ES256");
|
pk.set_algorithm("ES256");
|
||||||
pk.set_key_use("sig");
|
pk.set_key_use("sig");
|
||||||
kset.keys.push(pk);
|
kset.keys.push(pk);
|
||||||
|
|
||||||
let keypair = EcKeyPair::from_pem(include_bytes!("../../../config/private_ecdsa_key.pem"), Some(EcCurve::P256)).unwrap();
|
let keypair = EcKeyPair::from_pem(include_bytes!("../../../config/private_ecdsa_key.pem"), Some(EcCurve::P256)).unwrap();
|
||||||
let mut pk = keypair.to_jwk_public_key();
|
let mut pk = keypair.to_jwk_public_key();
|
||||||
pk.set_key_id("ec01");
|
pk.set_key_id("ec02");
|
||||||
pk.set_algorithm("ES256");
|
pk.set_algorithm("ES256");
|
||||||
pk.set_key_use("sig");
|
pk.set_key_use("sig");
|
||||||
kset.keys.push(pk);
|
kset.keys.push(pk);
|
||||||
|
|
||||||
let keypair = EdKeyPair::from_pem(include_bytes!("../../../config/ed25519-private.pem")).unwrap();
|
let keypair = EdKeyPair::from_pem(include_bytes!("../../../config/ed25519-private.pem")).unwrap();
|
||||||
let mut pk = keypair.to_jwk_public_key();
|
let mut pk = keypair.to_jwk_public_key();
|
||||||
pk.set_key_id("key-ed");
|
pk.set_key_id("ed01");
|
||||||
pk.set_algorithm("EdDSA");
|
pk.set_algorithm("EdDSA");
|
||||||
pk.set_key_use("sig");
|
pk.set_key_use("sig");
|
||||||
kset.keys.push(pk);
|
kset.keys.push(pk);
|
||||||
|
|
||||||
let keypair = EdKeyPair::from_pem(include_bytes!("../../../config/private_ed25519_key.pem")).unwrap();
|
let keypair = EdKeyPair::from_pem(include_bytes!("../../../config/private_ed25519_key.pem")).unwrap();
|
||||||
let mut pk = keypair.to_jwk_public_key();
|
let mut pk = keypair.to_jwk_public_key();
|
||||||
pk.set_key_id("ed01");
|
pk.set_key_id("ed02");
|
||||||
pk.set_algorithm("EdDSA");
|
pk.set_algorithm("EdDSA");
|
||||||
pk.set_key_use("sig");
|
pk.set_key_use("sig");
|
||||||
kset.keys.push(pk);
|
kset.keys.push(pk);
|
||||||
|
|
@ -114,18 +114,27 @@ pub async fn tokens() -> Json<Value> {
|
||||||
nbf: 1516239022, // Jan 2018
|
nbf: 1516239022, // Jan 2018
|
||||||
};
|
};
|
||||||
|
|
||||||
let rsa_key = EncodingKey::from_rsa_pem(include_bytes!("../../../config/jwtRS256.key")).unwrap();
|
let rsa1_key = EncodingKey::from_rsa_pem(include_bytes!("../../../config/jwtRS256.key")).unwrap();
|
||||||
let ec_key = EncodingKey::from_ec_pem(include_bytes!("../../../config/ec256-private.pem")).unwrap();
|
let rsa2_key = EncodingKey::from_rsa_pem(include_bytes!("../../../config/private_rsa_key_pkcs8.pem")).unwrap();
|
||||||
let ed_key = EncodingKey::from_ed_pem(include_bytes!("../../../config/ed25519-private.pem")).unwrap();
|
let ec1_key = EncodingKey::from_ec_pem(include_bytes!("../../../config/ec256-private.pem")).unwrap();
|
||||||
|
let ec2_key = EncodingKey::from_ec_pem(include_bytes!("../../../config/private_ecdsa_key.pem")).unwrap();
|
||||||
|
let ed1_key = EncodingKey::from_ed_pem(include_bytes!("../../../config/ed25519-private.pem")).unwrap();
|
||||||
|
let ed2_key = EncodingKey::from_ed_pem(include_bytes!("../../../config/ed25519-private.pem")).unwrap();
|
||||||
|
|
||||||
let rsa_token = encode(&build_header(Algorithm::RS256, "key-rsa"), &claims, &rsa_key).unwrap();
|
let rsa1_token = encode(&build_header(Algorithm::RS256, "rsa01"), &claims, &rsa1_key).unwrap();
|
||||||
let ec_token = encode(&build_header(Algorithm::ES256, "key-ec"), &claims, &ec_key).unwrap();
|
let rsa2_token = encode(&build_header(Algorithm::RS256, "rsa02"), &claims, &rsa2_key).unwrap();
|
||||||
let ed_token = encode(&build_header(Algorithm::EdDSA, "key-ed"), &claims, &ed_key).unwrap();
|
let ec1_token = encode(&build_header(Algorithm::ES256, "ec01"), &claims, &ec1_key).unwrap();
|
||||||
|
let ec2_token = encode(&build_header(Algorithm::ES256, "ec02"), &claims, &ec2_key).unwrap();
|
||||||
|
let ed1_token = encode(&build_header(Algorithm::EdDSA, "ed01"), &claims, &ed1_key).unwrap();
|
||||||
|
let ed2_token = encode(&build_header(Algorithm::EdDSA, "ed02"), &claims, &ed2_key).unwrap();
|
||||||
|
|
||||||
Json(json!({
|
Json(json!({
|
||||||
"rsa": rsa_token,
|
"rsa01": rsa1_token,
|
||||||
"ec": ec_token,
|
"rsa02": rsa2_token,
|
||||||
"ed": ed_token
|
"ec01": ec1_token,
|
||||||
|
"ec02": ec2_token,
|
||||||
|
"ed01": ed1_token,
|
||||||
|
"ed02": ed2_token,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,5 +30,6 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hyper = { version = "0.14", features = ["full"] }
|
hyper = { version = "0.14", features = ["full"] }
|
||||||
|
lazy_static = "1.4.0"
|
||||||
tower = { version = "0.4", features = ["util"] }
|
tower = { version = "0.4", features = ["util"] }
|
||||||
wiremock = "0.5"
|
wiremock = "0.5"
|
||||||
|
|
|
||||||
312
jwt-authorizer/tests/integration_tests.rs
Normal file
312
jwt-authorizer/tests/integration_tests.rs
Normal file
|
|
@ -0,0 +1,312 @@
|
||||||
|
use std::{
|
||||||
|
net::{SocketAddr, TcpListener},
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicI16, Ordering},
|
||||||
|
Arc, Once,
|
||||||
|
},
|
||||||
|
thread,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use axum::{response::Response, routing::get, Json, Router};
|
||||||
|
use http::{Request, StatusCode};
|
||||||
|
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 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"
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
|
/// Static variable to ensure that logging is only initialized once.
|
||||||
|
pub static INITIALIZED: Once = Once::new();
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
struct User {
|
||||||
|
sub: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref DISCOVERY_COUNTER: Arc<AtomicI16> = Arc::new(AtomicI16::new(0));
|
||||||
|
static ref JWKS_COUNTER: Arc<AtomicI16> = Arc::new(AtomicI16::new(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Stats {}
|
||||||
|
|
||||||
|
impl Stats {
|
||||||
|
fn reset() {
|
||||||
|
Arc::clone(&DISCOVERY_COUNTER).store(0, Ordering::Relaxed);
|
||||||
|
Arc::clone(&JWKS_COUNTER).store(0, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
fn jwks_counter() -> i16 {
|
||||||
|
Arc::clone(&JWKS_COUNTER).load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
fn discovery_counter() -> i16 {
|
||||||
|
Arc::clone(&DISCOVERY_COUNTER).load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn discovery(uri: &str) -> Json<Value> {
|
||||||
|
Arc::clone(&DISCOVERY_COUNTER).fetch_add(1, Ordering::Relaxed);
|
||||||
|
let d = serde_json::json!({ "jwks_uri": format!("{}/jwks", uri) });
|
||||||
|
|
||||||
|
Json(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn jwks() -> Json<Value> {
|
||||||
|
Arc::clone(&JWKS_COUNTER).fetch_add(1, Ordering::Relaxed);
|
||||||
|
|
||||||
|
Json(JWKS_RSA1.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_jwks_server() -> String {
|
||||||
|
let listener = TcpListener::bind("0.0.0.0:0".parse::<SocketAddr>().unwrap()).unwrap();
|
||||||
|
let addr = listener.local_addr().unwrap();
|
||||||
|
let url = format!("http://{}:{}", addr.ip(), addr.port());
|
||||||
|
|
||||||
|
let url2 = url.clone();
|
||||||
|
|
||||||
|
let app = Router::new()
|
||||||
|
.route("/.well-known/openid-configuration", get(|| async move { discovery(&url2) }))
|
||||||
|
.route("/jwks", get(jwks));
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
axum::Server::from_tcp(listener)
|
||||||
|
.unwrap()
|
||||||
|
.serve(app.into_make_service())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
url
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn app(jwt_auth: JwtAuthorizer<User>) -> Router {
|
||||||
|
async fn public_handler() -> &'static str {
|
||||||
|
"public"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn protected_handler() -> &'static str {
|
||||||
|
"protected"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn protected_with_user(JwtClaims(user): JwtClaims<User>) -> Json<User> {
|
||||||
|
Json(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
let pub_route: Router = Router::new().route("/public", get(public_handler));
|
||||||
|
let protected_route: Router = Router::new()
|
||||||
|
.route("/protected", get(protected_handler))
|
||||||
|
.route("/protected-with-user", get(protected_with_user))
|
||||||
|
.layer(jwt_auth.layer().await.unwrap());
|
||||||
|
|
||||||
|
Router::new().merge(pub_route).merge(protected_route)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_test() {
|
||||||
|
INITIALIZED.call_once(|| {
|
||||||
|
tracing_subscriber::registry()
|
||||||
|
.with(tracing_subscriber::EnvFilter::new(
|
||||||
|
std::env::var("RUST_LOG").unwrap_or_else(|_| "info,jwt-authorizer=debug,tower_http=debug".into()),
|
||||||
|
))
|
||||||
|
.with(tracing_subscriber::fmt::layer())
|
||||||
|
.init();
|
||||||
|
});
|
||||||
|
// reset counters
|
||||||
|
Stats::reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn make_proteced_request(app: &mut Router, bearer: &str) -> Response {
|
||||||
|
app.ready()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.call(
|
||||||
|
Request::builder()
|
||||||
|
.uri("/protected")
|
||||||
|
.header("Authorization", bearer)
|
||||||
|
.body(Body::empty())
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn make_public_request(app: &mut Router) -> Response {
|
||||||
|
app.ready()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.call(Request::builder().uri("/public").body(Body::empty()).unwrap())
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn sequential_tests() {
|
||||||
|
// these tests must be executed sequentially
|
||||||
|
scenario1().await;
|
||||||
|
scenario2().await;
|
||||||
|
scenario3().await;
|
||||||
|
scenario4().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn scenario1() {
|
||||||
|
init_test();
|
||||||
|
let url = run_jwks_server();
|
||||||
|
let auth: JwtAuthorizer<User> = JwtAuthorizer::from_oidc(&url);
|
||||||
|
let mut app = app(auth).await;
|
||||||
|
assert_eq!(1, Stats::discovery_counter());
|
||||||
|
assert_eq!(0, Stats::jwks_counter());
|
||||||
|
// NO LOADING when public request
|
||||||
|
let r = make_public_request(&mut app).await;
|
||||||
|
assert_eq!(StatusCode::OK, r.status());
|
||||||
|
assert_eq!(0, Stats::jwks_counter(), "sc1: public -> no loading");
|
||||||
|
// LOADING - first jwt check
|
||||||
|
let r = make_proteced_request(&mut app, JWT_RSA1_OK).await;
|
||||||
|
assert_eq!(StatusCode::OK, r.status());
|
||||||
|
assert_eq!(1, Stats::jwks_counter(), "sc1: 1st check -> loading");
|
||||||
|
// NO RELOADING same kid with OK
|
||||||
|
let r = make_proteced_request(&mut app, JWT_RSA1_OK).await;
|
||||||
|
assert_eq!(StatusCode::OK, r.status());
|
||||||
|
assert_eq!(1, Stats::jwks_counter(), "sc1: 2st check -> no loading");
|
||||||
|
// NO RELEOADING, invalid kid, 401
|
||||||
|
let r = make_proteced_request(&mut app, JWT_RSA2_OK).await;
|
||||||
|
assert_eq!(StatusCode::UNAUTHORIZED, r.status());
|
||||||
|
assert_eq!(1, Stats::jwks_counter(), "sc1: 3st check (invalid kid) -> no loading");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// SCENARIO2
|
||||||
|
///
|
||||||
|
/// Refresh strategy: INTERVAL
|
||||||
|
async fn scenario2() {
|
||||||
|
init_test();
|
||||||
|
let url = run_jwks_server();
|
||||||
|
let refresh = Refresh {
|
||||||
|
minimal_refresh_interval: Duration::from_millis(20),
|
||||||
|
refresh_interval: Duration::from_millis(40),
|
||||||
|
retry_interval: Duration::from_millis(0),
|
||||||
|
strategy: RefreshStrategy::Interval,
|
||||||
|
};
|
||||||
|
let auth: JwtAuthorizer<User> = JwtAuthorizer::from_oidc(&url).refresh(refresh);
|
||||||
|
let mut app = app(auth).await;
|
||||||
|
assert_eq!(1, Stats::discovery_counter());
|
||||||
|
assert_eq!(0, Stats::jwks_counter());
|
||||||
|
let r = make_proteced_request(&mut app, JWT_RSA1_OK).await;
|
||||||
|
assert_eq!(StatusCode::OK, r.status());
|
||||||
|
assert_eq!(1, Stats::jwks_counter());
|
||||||
|
// NO RELOADING same kid
|
||||||
|
let r = make_proteced_request(&mut app, JWT_RSA1_OK).await;
|
||||||
|
assert_eq!(StatusCode::OK, r.status());
|
||||||
|
assert_eq!(1, Stats::jwks_counter());
|
||||||
|
// RELEOADING, same kid, refresh_interval elapsed
|
||||||
|
thread::sleep(Duration::from_millis(41));
|
||||||
|
let r = make_proteced_request(&mut app, JWT_RSA1_OK).await;
|
||||||
|
assert_eq!(StatusCode::OK, r.status());
|
||||||
|
assert_eq!(2, Stats::jwks_counter());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// SCENARIO3
|
||||||
|
///
|
||||||
|
/// Refresh strategy: KeyNotFound
|
||||||
|
async fn scenario3() {
|
||||||
|
init_test();
|
||||||
|
let url = run_jwks_server();
|
||||||
|
let refresh = Refresh {
|
||||||
|
strategy: RefreshStrategy::KeyNotFound,
|
||||||
|
minimal_refresh_interval: Duration::from_millis(20),
|
||||||
|
retry_interval: Duration::from_millis(0),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let auth: JwtAuthorizer<User> = JwtAuthorizer::from_oidc(&url).refresh(refresh);
|
||||||
|
let mut app = app(auth).await;
|
||||||
|
assert_eq!(1, Stats::discovery_counter());
|
||||||
|
assert_eq!(0, Stats::jwks_counter());
|
||||||
|
// RELOADING getting keys first time
|
||||||
|
let r = make_proteced_request(&mut app, JWT_RSA1_OK).await;
|
||||||
|
assert_eq!(StatusCode::OK, r.status());
|
||||||
|
assert_eq!(1, Stats::jwks_counter());
|
||||||
|
thread::sleep(Duration::from_millis(21));
|
||||||
|
// NO RELOADING refresh interval elapsed, kid OK
|
||||||
|
let r = make_proteced_request(&mut app, JWT_RSA1_OK).await;
|
||||||
|
assert_eq!(StatusCode::OK, r.status());
|
||||||
|
assert_eq!(1, Stats::jwks_counter());
|
||||||
|
// RELEOADING, unknown kid, refresh_interval elapsed
|
||||||
|
thread::sleep(Duration::from_millis(41));
|
||||||
|
let r = make_proteced_request(&mut app, JWT_RSA2_OK).await;
|
||||||
|
assert_eq!(StatusCode::UNAUTHORIZED, r.status());
|
||||||
|
assert_eq!(2, Stats::jwks_counter());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// SCENARIO4
|
||||||
|
///
|
||||||
|
/// Refresh strategy: NoRefresh
|
||||||
|
async fn scenario4() {
|
||||||
|
init_test();
|
||||||
|
let url = run_jwks_server();
|
||||||
|
let refresh = Refresh {
|
||||||
|
strategy: RefreshStrategy::NoRefresh,
|
||||||
|
minimal_refresh_interval: Duration::from_millis(0),
|
||||||
|
retry_interval: Duration::from_millis(0),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let auth: JwtAuthorizer<User> = JwtAuthorizer::from_oidc(&url).refresh(refresh);
|
||||||
|
let mut app = app(auth).await;
|
||||||
|
assert_eq!(1, Stats::discovery_counter());
|
||||||
|
assert_eq!(0, Stats::jwks_counter());
|
||||||
|
// RELOADING getting keys first time
|
||||||
|
let r = make_proteced_request(&mut app, JWT_RSA1_OK).await;
|
||||||
|
assert_eq!(StatusCode::OK, r.status());
|
||||||
|
assert_eq!(1, Stats::jwks_counter());
|
||||||
|
thread::sleep(Duration::from_millis(21));
|
||||||
|
// NO RELOADING kid OK
|
||||||
|
let r = make_proteced_request(&mut app, JWT_RSA1_OK).await;
|
||||||
|
assert_eq!(StatusCode::OK, r.status());
|
||||||
|
assert_eq!(1, Stats::jwks_counter());
|
||||||
|
// NO RELEOADING, unknown kid
|
||||||
|
thread::sleep(Duration::from_millis(41));
|
||||||
|
let r = make_proteced_request(&mut app, JWT_RSA2_OK).await;
|
||||||
|
assert_eq!(StatusCode::UNAUTHORIZED, r.status());
|
||||||
|
assert_eq!(1, Stats::jwks_counter());
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue