#[macro_use]
pub mod bb_common;
use std::fs::File;
use std::io::Read;
use bb_common::*;
use bbjwt::keystore::{EcCurve, KeyAlgorithm};
use bbjwt::{KeyStore, default_validations, validate_jwt};
const ISS: &str = "https://kc.basebox.health/realms/testing";
fn load_asset(name: &str) -> String {
let pfn = path_to_asset_file(name);
let mut file = File::open(&pfn).unwrap_or_else(|_| panic!("Failed to open asset {}", &pfn));
let mut data = String::new();
file.read_to_string(&mut data).unwrap();
data
}
async fn validate_jwt_with_keystores(
jwt_name: &str,
keystore_good: &KeyStore,
keystore_bad: &KeyStore,
) {
let jwt = load_asset(jwt_name);
let jwt_decoded = validate_jwt(&jwt, &default_validations(ISS, None, None), keystore_good)
.await
.expect("Valid JWT did not validate");
assert_eq!(jwt_decoded.claims["nonce"].as_str().unwrap(), "UZ1BSZFvy7jKkj1o9p3r7w");
assert_eq!(jwt_decoded.claims["sub"].as_str().unwrap(), "13529346-91b6-4268-aae1-f5ad8f44cf4d");
assert_eq!(
jwt_decoded.claims["iss"].as_str().unwrap(),
"https://kc.basebox.health/realms/testing"
);
let ret = validate_jwt(&jwt, &default_validations(ISS, None, None), keystore_bad).await;
let err_msg = ret.unwrap_err().to_string();
assert!(
err_msg.contains("Invalid signature"),
"Invalid key check did not fail with signature error."
);
}
#[tokio::test]
#[should_panic(expected = "Unsupported alg")]
async fn unsupported_alg_jwt() {
let ks = KeyStore::new().await.unwrap();
ks.add_rsa_pem_key(&load_asset("rsa.pub.key"), Some("key-1"), KeyAlgorithm::RS256)
.await
.expect("Failed to add RSA key");
assert_eq!(ks.keys_len().await, 1);
let jwt = load_asset("id_token_unsupported_alg.txt");
validate_jwt(&jwt, &default_validations(ISS, None, None), &ks).await.unwrap();
}
#[tokio::test]
async fn rsa256_valid_jwt() {
let ks_good = KeyStore::new().await.unwrap();
ks_good
.add_rsa_pem_key(&load_asset("rsa.pub.key"), Some("key-1"), KeyAlgorithm::RS256)
.await
.expect("Failed to add RSA384 key");
let ks_bad = KeyStore::new().await.unwrap();
ks_bad
.add_rsa_pem_key(&load_asset("rsa.wrong.pub.key"), Some("key-1"), KeyAlgorithm::RS256)
.await
.expect("Failed to add RSA384 key");
validate_jwt_with_keystores("id_token_rsa256.txt", &ks_good, &ks_bad).await;
}
#[tokio::test]
async fn rsa256_invalid_claims_expired_jwt() {
let ks = KeyStore::new().await.unwrap();
ks.add_rsa_pem_key(&load_asset("rsa.pub.key"), Some("key-1"), KeyAlgorithm::RS256)
.await
.expect("Failed to add RSA key");
assert_eq!(ks.keys_len().await, 1);
let jwt = load_asset("id_token_rsa256_expired.txt");
let ret = validate_jwt(
&jwt,
&default_validations("https://kc.basebox.health/realms/WRONG", None, None),
&ks,
)
.await;
if let Err(err) = ret {
let msg = err.to_string();
assert!(msg.contains("iss"), "iss error not reported");
assert!(msg.contains("expired"), "expiration not reported");
} else {
panic!("Invalid JWT validated ok!");
}
}
#[tokio::test]
#[should_panic(expected = "expired")]
async fn rsa256_valid_claims_expired_jwt() {
let ks = KeyStore::new().await.unwrap();
ks.add_rsa_pem_key(&load_asset("rsa.pub.key"), Some("key-1"), KeyAlgorithm::RS256)
.await
.expect("Failed to add RSA key");
assert_eq!(ks.keys_len().await, 1);
let jwt = load_asset("id_token_rsa256_expired.txt");
validate_jwt(&jwt, &default_validations(ISS, None, None), &ks).await.unwrap();
}
#[tokio::test]
async fn rsa384_valid_jwt() {
let ks_good = KeyStore::new().await.unwrap();
ks_good
.add_rsa_pem_key(&load_asset("rsa.pub.key"), Some("key-1"), KeyAlgorithm::RS384)
.await
.expect("Failed to add RSA384 key");
let ks_bad = KeyStore::new().await.unwrap();
ks_bad
.add_rsa_pem_key(&load_asset("rsa.wrong.pub.key"), Some("key-1"), KeyAlgorithm::RS384)
.await
.expect("Failed to add RSA384 key");
validate_jwt_with_keystores("id_token_rsa384.txt", &ks_good, &ks_bad).await;
}
#[tokio::test]
async fn rsa512_valid_jwt() {
let ks = KeyStore::new().await.unwrap();
ks.add_rsa_pem_key(&load_asset("rsa.pub.key"), Some("key-1"), KeyAlgorithm::RS512)
.await
.expect("Failed to add RSA key");
assert_eq!(ks.keys_len().await, 1);
let jwt = load_asset("id_token_rsa512.txt");
validate_jwt(&jwt, &default_validations(ISS, None, None), &ks)
.await
.expect("Valid JWT did not validate");
}
#[tokio::test]
async fn es256_valid_jwt() {
let ks_good = KeyStore::new().await.unwrap();
ks_good
.add_ec_pem_key(&load_asset("ec256.pub.key"), Some("key-1"), EcCurve::P256, KeyAlgorithm::ES256)
.await
.expect("Failed to add ec256 key");
let ks_bad = KeyStore::new().await.unwrap();
ks_bad
.add_ec_pem_key(
&load_asset("ec256.wrong.pub.key"),
Some("key-1"),
EcCurve::P256,
KeyAlgorithm::ES256,
)
.await
.expect("Failed to add EC256 key");
validate_jwt_with_keystores("id_token_es256.txt", &ks_good, &ks_bad).await;
}
#[tokio::test]
#[should_panic(expected = "expired")]
async fn es256_expired_jwt() {
let ks = KeyStore::new().await.unwrap();
ks.add_ec_pem_key(
&load_asset("ec256.pub.key"),
Some("key-1"),
EcCurve::P256,
KeyAlgorithm::ES256,
)
.await
.expect("Failed to add EC key");
assert_eq!(ks.keys_len().await, 1);
let jwt = load_asset("id_token_es256_expired.txt");
validate_jwt(&jwt, &default_validations(ISS, None, None), &ks).await.unwrap();
}
#[tokio::test]
#[should_panic(expected = "SignatureInvalid")]
async fn es256_signature_invalid_jwt() {
let ks = KeyStore::new().await.unwrap();
ks.add_ec_pem_key(
&load_asset("ec256.pub.key"),
Some("key-1"),
EcCurve::P256,
KeyAlgorithm::ES256,
)
.await
.expect("Failed to add EC key");
assert_eq!(ks.keys_len().await, 1);
let jwt = load_asset("id_token_es256_signature_invalid.txt");
validate_jwt(&jwt, &default_validations(ISS, None, None), &ks).await.unwrap();
}
#[tokio::test]
async fn es384_valid_jwt() {
let ks_good = KeyStore::new().await.unwrap();
ks_good
.add_ec_pem_key(&load_asset("ec384.pub.key"), Some("key-1"), EcCurve::P384, KeyAlgorithm::ES384)
.await
.expect("Failed to add ec384 key");
let ks_bad = KeyStore::new().await.unwrap();
ks_bad
.add_ec_pem_key(
&load_asset("ec384.wrong.pub.key"),
Some("key-1"),
EcCurve::P384,
KeyAlgorithm::ES384,
)
.await
.expect("Failed to add EC384 key");
validate_jwt_with_keystores("id_token_es384.txt", &ks_good, &ks_bad).await;
}
#[tokio::test]
async fn ed25519_valid_jwt() {
let ks_good = KeyStore::new().await.unwrap();
ks_good
.add_ec_pem_key(
&load_asset("ed25519.pub.key"),
Some("key-1"),
EcCurve::Ed25519,
KeyAlgorithm::EdDSA,
)
.await
.expect("Failed to add Ed25519 key");
let ks_bad = KeyStore::new().await.unwrap();
ks_bad
.add_ec_pem_key(
&load_asset("ed25519.wrong.pub.key"),
Some("key-1"),
EcCurve::Ed25519,
KeyAlgorithm::EdDSA,
)
.await
.expect("Failed to add Ed25519 key");
validate_jwt_with_keystores("id_token_ed25519.txt", &ks_good, &ks_bad).await;
}
#[tokio::test]
async fn load_ed25519_jwk() {
let mut ks = KeyStore::new().await.unwrap();
let jwk_json = load_asset("ed25519.pub.jwk.json");
ks.add_key(&jwk_json).await.unwrap();
assert_eq!(ks.keys_len().await, 1);
ks.key_by_id(Some("key-1")).await.unwrap();
assert!(ks.key_by_id(Some("No-key-with-that-name")).await.is_err());
}
#[tokio::test]
async fn load_ec_jwk() {
let mut ks = KeyStore::new().await.unwrap();
let jwk_json = load_asset("ec256.pub.jwk.json");
ks.add_key(&jwk_json).await.unwrap();
assert_eq!(ks.keys_len().await, 1);
ks.key_by_id(Some("ec2561")).await.unwrap();
assert!(ks.key_by_id(Some("No-key-with-that-name")).await.is_err());
}