| 353 | |
| 354 | #[test] |
| 355 | fn rs256_roundtrip() { |
| 356 | use rsa::pkcs1v15::SigningKey; |
| 357 | use rsa::signature::{SignatureEncoding, Signer}; |
| 358 | |
| 359 | // Generate a test RSA key pair. |
| 360 | let mut rng = rsa::rand_core::OsRng; |
| 361 | let private_key = rsa::RsaPrivateKey::new(&mut rng, TEST_RSA_BITS).unwrap(); |
| 362 | let public_key = rsa::RsaPublicKey::from(&private_key); |
| 363 | |
| 364 | // Export public key as DER (PKCS#8). |
| 365 | let pub_der = { |
| 366 | use rsa::pkcs8::EncodePublicKey; |
| 367 | public_key.to_public_key_der().unwrap().as_ref().to_vec() |
| 368 | }; |
| 369 | |
| 370 | // Build JWT manually. |
| 371 | let header = base64_url_encode(br#"{"alg":"RS256","typ":"JWT"}"#); |
| 372 | let payload_json = |
| 373 | r#"{"sub":"bob","tenant_id":2,"roles":["admin"],"exp":9999999999,"user_id":99}"#; |
| 374 | let payload = base64_url_encode(payload_json.as_bytes()); |
| 375 | let signing_input = format!("{header}.{payload}"); |
| 376 | |
| 377 | // Sign with RSA PKCS#1 v1.5. |
| 378 | let signing_key = SigningKey::<sha2::Sha256>::new(private_key); |
| 379 | let sig: rsa::pkcs1v15::Signature = signing_key.sign(signing_input.as_bytes()); |
| 380 | let sig_b64 = base64_url_encode(&sig.to_bytes()); |
| 381 | |
| 382 | let token = format!("{signing_input}.{sig_b64}"); |
| 383 | |
| 384 | // Validate. |
| 385 | let config = JwtConfig { |
| 386 | rsa_public_key_der: pub_der, |
| 387 | ..Default::default() |
| 388 | }; |
| 389 | let validator = JwtValidator::new(config); |
| 390 | let identity = validator.validate(&token).unwrap(); |
| 391 | assert_eq!(identity.username, "bob"); |
| 392 | assert_eq!(identity.tenant_id, TenantId::new(2)); |
| 393 | assert_eq!(identity.user_id, 99); |
| 394 | } |
| 395 | |
| 396 | #[test] |
| 397 | fn rs256_wrong_key_rejected() { |