use tokio_test::*; use serde_json::json; use serde_json::value::Value; use jsonwebtokens as jwt; use jwt::raw::TokenSlices; use jwt::{raw, Algorithm, AlgorithmID, TokenData, Verifier}; mod common; use common::get_time; const URL_SAFE_ENGINE: base64::engine::fast_portable::FastPortable = base64::engine::fast_portable::FastPortable::from( &base64::alphabet::URL_SAFE, base64::engine::fast_portable::NO_PAD, ); #[test] fn sign_hs256() { let alg = Algorithm::new_hmac(AlgorithmID::HS256, "secret").unwrap(); let result = alg.sign("hello world").unwrap(); let expected = "c0zGLzKEFWj0VxWuufTXiRMk5tlI5MbGDAYhzaxIYjo"; assert_eq!(result, expected); } #[test] fn verify_hs256() { let alg = Algorithm::new_hmac(AlgorithmID::HS256, "secret").unwrap(); let sig = "c0zGLzKEFWj0VxWuufTXiRMk5tlI5MbGDAYhzaxIYjo"; assert_ok!(alg.verify(None, "hello world", sig)); } #[test] fn verify_hs256_signature_only() { let alg = Algorithm::new_hmac(AlgorithmID::HS256, "secret").unwrap(); let header = json!({ "alg": "HS256" }); let claims = json!({ "aud": "test" }); let token_str = jwt::encode(&header, &claims, &alg).unwrap(); let TokenSlices { message, signature, header, .. } = raw::split_token(&token_str).unwrap(); let header = raw::decode_json_token_slice(header).unwrap(); assert_ok!(raw::verify_signature_only( &header, message, signature, &alg )); } #[test] #[should_panic(expected = "InvalidSignature")] fn hmac_256_bad_secret() { let alg = Algorithm::new_hmac(AlgorithmID::HS256, "secret").unwrap(); let header = json!({ "alg": "HS256" }); let claims = json!({ "aud": "test" }); let token_str = jwt::encode(&header, &claims, &alg).unwrap(); let alg = Algorithm::new_hmac(AlgorithmID::HS256, "wrong-secret").unwrap(); let validator = Verifier::create().build().unwrap(); let _claims: Value = validator.verify(token_str, &alg).unwrap(); } #[test] #[should_panic(expected = "AlgorithmMismatch")] fn missing_alg() { let alg = Algorithm::new_hmac(AlgorithmID::HS256, "secret").unwrap(); let header = json!({}); let claims = json!({ "aud": "test" }); let token_str = jwt::encode(&header, &claims, &alg).unwrap(); let validator = Verifier::create().build().unwrap(); let _claims: Value = validator.verify(token_str, &alg).unwrap(); } #[test] fn round_trip_claims() { let alg = Algorithm::new_hmac(AlgorithmID::HS256, "secret").unwrap(); let my_claims = json!({ "sub": "b@b.com", "company": "ACME", "exp": get_time() + 10000, }); let header = json!({"alg": "HS256"}); let token = jwt::encode(&header, &my_claims, &alg).unwrap(); let verifier = Verifier::create().build().unwrap(); let claims: Value = verifier.verify(token, &alg).unwrap(); assert_eq!(my_claims, claims); } #[test] fn round_trip_claims_and_custom_header() { let alg = Algorithm::new_hmac(AlgorithmID::HS256, "secret").unwrap(); let my_claims = json!({ "sub": "b@b.com", "company": "ACME", "exp": get_time() + 10000, }); let header = json!({"alg": "HS256", "my_hdr": "my_hdr_val"}); let token = jwt::encode(&header, &my_claims, &alg).unwrap(); let verifier = Verifier::create().build().unwrap(); // We have to use the lower-level for_time API if we want to see the header let TokenData { header, claims, .. } = verifier.verify_for_time(token, &alg, get_time()).unwrap(); assert_eq!(my_claims, claims); assert_eq!(header.get("my_hdr").unwrap(), "my_hdr_val"); } #[test] fn round_trip_claims_and_kid() { let mut alg = Algorithm::new_hmac(AlgorithmID::HS256, "secret").unwrap(); alg.set_kid("kid1234"); let my_claims = json!({ "sub": "b@b.com", "company": "ACME", "exp": get_time() + 10000, }); let header = json!({ "alg": alg.name(), "kid": alg.kid(), "my_hdr": "my_hdr_val" }); let token = jwt::encode(&header, &my_claims, &alg).unwrap(); let verifier = Verifier::create().build().unwrap(); // We have to use the lower-level for_time API if we want to see the header let TokenData { header, claims, .. } = verifier.verify_for_time(token, &alg, get_time()).unwrap(); assert_eq!(my_claims, claims); assert_eq!(header.get("kid").unwrap(), "kid1234"); assert_eq!(header.get("my_hdr").unwrap(), "my_hdr_val"); } #[test] #[should_panic(expected = "MalformedToken")] fn round_trip_claims_and_wrong_kid() { let mut alg = Algorithm::new_hmac(AlgorithmID::HS256, "secret").unwrap(); alg.set_kid("kid1234"); let my_claims = json!({ "sub": "b@b.com", "company": "ACME", "exp": get_time() + 10000, }); let header = json!({ "alg": alg.name(), "kid": "kid4321", "my_hdr": "my_hdr_val" }); let token = jwt::encode(&header, &my_claims, &alg).unwrap(); let verifier = Verifier::create().build().unwrap(); // We have to use the lower-level for_time API if we want to see the header let TokenData { header, claims, .. } = verifier.verify_for_time(token, &alg, get_time()).unwrap(); assert_eq!(my_claims, claims); assert_eq!(header.get("kid").unwrap(), "kid1234"); assert_eq!(header.get("my_hdr").unwrap(), "my_hdr_val"); } #[test] fn decode_token() { let alg = Algorithm::new_hmac(AlgorithmID::HS256, "secret").unwrap(); let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.9r56oF7ZliOBlOAyiOFperTGxBtPykRQiWNFxhDCW98"; let verifier = Verifier::create().build().unwrap(); let claims: Value = verifier.verify(token, &alg).unwrap(); println!("{claims:?}"); } #[test] #[should_panic(expected = "MalformedToken")] fn decode_token_missing_parts() { let alg = Algorithm::new_hmac(AlgorithmID::HS256, "secret").unwrap(); let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"; let verifier = Verifier::create().build().unwrap(); let _claims: Value = verifier.verify(token, &alg).unwrap(); } #[test] #[should_panic(expected = "InvalidSignature")] fn decode_token_invalid_signature() { let alg = Algorithm::new_hmac(AlgorithmID::HS256, "secret").unwrap(); let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUifQ.wrong"; let verifier = Verifier::create().build().unwrap(); let _claims: Value = verifier.verify(token, &alg).unwrap(); } #[test] fn decode_token_with_bytes_secret() { let secret_b64 = base64::encode_engine(b"\x01\x02\x03", &URL_SAFE_ENGINE); let alg = Algorithm::new_hmac_b64(AlgorithmID::HS256, secret_b64).unwrap(); let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.Hm0yvKH25TavFPz7J_coST9lZFYH1hQo0tvhvImmaks"; let verifier = Verifier::create().build().unwrap(); let _claims: Value = verifier.verify(token, &alg).unwrap(); } #[test] fn only_decode_token_header() { let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjb21wYW55IjoiMTIzNDU2Nzg5MCIsInN1YiI6IkpvaG4gRG9lIn0.S"; let header = raw::decode_header_only(token).unwrap(); assert_eq!(header.get("alg").expect("missing alg"), "HS256"); assert_eq!(header.get("typ").expect("missing typ"), "JWT"); } #[test] fn only_decode_token_header_with_slice_api() { let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjb21wYW55IjoiMTIzNDU2Nzg5MCIsInN1YiI6IkpvaG4gRG9lIn0.S"; let TokenSlices { header, .. } = raw::split_token(token).unwrap(); let header = raw::decode_json_token_slice(header).unwrap(); assert_eq!(header.get("alg").expect("missing alg"), "HS256"); assert_eq!(header.get("typ").expect("missing typ"), "JWT"); } #[test] fn only_decode_token() { let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.9r56oF7ZliOBlOAyiOFperTGxBtPykRQiWNFxhDCW98"; let TokenData { header, claims, .. } = raw::decode_only(token).unwrap(); assert_eq!(header.get("alg").expect("missing alg"), "HS256"); assert_eq!(header.get("typ").expect("missing typ"), "JWT"); assert_eq!(claims.get("sub").expect("no sub"), "b@b.com"); assert_eq!(claims.get("company").expect("no company"), "ACME"); assert_eq!(claims.get("exp").expect("no exp"), 2532524891u64); } #[test] #[should_panic(expected = "MalformedToken")] fn split_token_missing_parts() { let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"; let _token_slices = raw::split_token(token).unwrap(); } #[test] fn only_decode_token_invalid_signature() { let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.wrong"; let _token_data = raw::decode_only(token).unwrap(); } #[test] fn only_decode_token_wrong_algorithm() { let token = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.fLxey-hxAKX5rNHHIx1_Ch0KmrbiuoakDVbsJjLWrx8fbjKjrPuWMYEJzTU3SBnYgnZokC-wqSdqckXUOunC-g"; let _token_data = raw::decode_only(token).unwrap(); }