1use std::sync::atomic::{AtomicU64, Ordering};
25use std::sync::{Arc, RwLock};
26
27use base64::Engine;
28
29use jsonwebtoken::{Algorithm, DecodingKey};
30
31const PLAIN_SECRET_ALGORITHMS: &[Algorithm] = &[Algorithm::HS256];
37
38const HMAC: &[Algorithm] = &[Algorithm::HS256, Algorithm::HS384, Algorithm::HS512];
39const RSA: &[Algorithm] = &[
40 Algorithm::RS256,
41 Algorithm::RS384,
42 Algorithm::RS512,
43 Algorithm::PS256,
44 Algorithm::PS384,
45 Algorithm::PS512,
46];
47
48const MIN_RSA_BITS: usize = 2048;
55
56#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
62#[non_exhaustive]
63pub enum JwtKeyError {
64 #[error("the JWT secret is configured as base64 and is not valid base64")]
66 NotBase64,
67
68 #[error("the JWT secret looks like a JSON Web Key but is not valid JSON: {0}")]
74 NotJson(String),
75
76 #[error("the JWT secret is a JSON object but neither a JWK (no \"kty\") nor a JWK Set (no \"keys\" array)")]
78 NotAKey,
79
80 #[error("{at}: {reason}")]
82 BadKey {
83 at: String,
85 reason: String,
87 },
88
89 #[error("no key in the JWT secret can verify a JWT signature ({0})")]
91 NoUsableKeys(String),
92
93 #[error("PGRST_JWT_JWKS_URL: {0}")]
96 JwksUrl(String),
97}
98
99#[derive(Clone)]
101struct Key {
102 kid: Option<String>,
104 any_kid: bool,
112 algorithms: &'static [Algorithm],
114 kty: &'static str,
116 key: DecodingKey,
117}
118
119impl Key {
120 fn admits(&self, alg: Algorithm) -> bool {
121 self.algorithms.contains(&alg)
122 }
123
124 fn named(&self, kid: Option<&str>) -> bool {
125 match kid {
126 None => true,
127 Some(_) if self.any_kid => true,
128 Some(kid) => self.kid.as_deref() == Some(kid),
130 }
131 }
132}
133
134pub struct KeySet {
140 keys: Vec<Key>,
141}
142
143impl std::fmt::Debug for KeySet {
144 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147 f.debug_list()
148 .entries(self.keys.iter().map(|k| {
149 format!(
150 "{}{} {:?}",
151 k.kty,
152 k.kid
153 .as_deref()
154 .map(|kid| format!(" kid={kid}"))
155 .unwrap_or_default(),
156 k.algorithms
157 )
158 }))
159 .finish()
160 }
161}
162
163impl KeySet {
164 pub fn from_secret(secret: &str, secret_is_base64: bool) -> Result<Self, JwtKeyError> {
170 let bytes = if secret_is_base64 {
171 base64::engine::general_purpose::STANDARD
172 .decode(secret)
173 .map_err(|_| JwtKeyError::NotBase64)?
174 } else {
175 secret.as_bytes().to_vec()
176 };
177
178 if let Ok(text) = std::str::from_utf8(&bytes) {
183 if text.trim_start().starts_with('{') {
184 return Self::from_jwk_json(text);
185 }
186 }
187
188 Ok(Self {
189 keys: vec![Key {
190 kid: None,
191 any_kid: true,
192 algorithms: PLAIN_SECRET_ALGORITHMS,
193 kty: "secret",
194 key: DecodingKey::from_secret(&bytes),
195 }],
196 })
197 }
198
199 pub fn from_jwk_json(json: &str) -> Result<Self, JwtKeyError> {
210 let value: serde_json::Value =
211 serde_json::from_str(json).map_err(|e| JwtKeyError::NotJson(e.to_string()))?;
212 let object = value.as_object().ok_or(JwtKeyError::NotAKey)?;
213
214 let entries: Vec<(String, &serde_json::Value)> = match object.get("keys") {
215 Some(serde_json::Value::Array(keys)) => keys
216 .iter()
217 .enumerate()
218 .map(|(i, k)| (format!("key {} of the JWK Set", i + 1), k))
219 .collect(),
220 Some(_) => {
221 return Err(JwtKeyError::BadKey {
222 at: "JWK Set".into(),
223 reason: "\"keys\" must be an array".into(),
224 })
225 }
226 None if object.contains_key("kty") => vec![("JWK".to_string(), &value)],
227 None => return Err(JwtKeyError::NotAKey),
228 };
229
230 let mut keys = Vec::with_capacity(entries.len());
231 let mut set_aside = Vec::new();
232 for (at, entry) in entries {
233 match parse_jwk(entry) {
234 Ok(Parsed::Key(key)) => keys.push(key),
235 Ok(Parsed::SetAside(why)) => {
236 tracing::warn!("JWT secret: {at} is not used: {why}");
237 set_aside.push(format!("{at}: {why}"));
238 }
239 Err(reason) => return Err(JwtKeyError::BadKey { at, reason }),
240 }
241 }
242
243 if keys.is_empty() {
244 return Err(JwtKeyError::NoUsableKeys(if set_aside.is_empty() {
245 "the JWK Set is empty".into()
246 } else {
247 set_aside.join("; ")
248 }));
249 }
250 Ok(Self { keys })
251 }
252
253 pub fn len(&self) -> usize {
255 self.keys.len()
256 }
257
258 pub fn is_empty(&self) -> bool {
261 self.keys.is_empty()
262 }
263
264 pub(crate) fn empty() -> Self {
269 Self { keys: Vec::new() }
270 }
271
272 pub(crate) fn union(first: &KeySet, second: &KeySet) -> Self {
279 Self {
280 keys: first.keys.iter().chain(&second.keys).cloned().collect(),
281 }
282 }
283
284 pub(crate) fn selects(&self, kid: Option<&str>, alg: Algorithm) -> bool {
290 self.candidates(kid, alg).next().is_some()
291 }
292
293 pub(crate) fn candidates<'a>(
299 &'a self,
300 kid: Option<&'a str>,
301 alg: Algorithm,
302 ) -> impl Iterator<Item = &'a DecodingKey> + 'a {
303 self.keys
304 .iter()
305 .filter(move |k| k.named(kid) && k.admits(alg))
306 .map(|k| &k.key)
307 }
308}
309
310#[derive(Clone)]
327pub struct JwtKeys {
328 current: Arc<RwLock<(Arc<KeySet>, u64)>>,
329}
330
331impl std::fmt::Debug for JwtKeys {
332 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
333 self.current().fmt(f)
334 }
335}
336
337impl JwtKeys {
338 pub fn new(set: KeySet) -> Self {
340 Self {
341 current: Arc::new(RwLock::new((Arc::new(set), next_generation()))),
342 }
343 }
344
345 pub fn from_config(
350 secret: Option<&str>,
351 secret_is_base64: bool,
352 ) -> Result<Option<Self>, JwtKeyError> {
353 secret
354 .map(|secret| KeySet::from_secret(secret, secret_is_base64).map(Self::new))
355 .transpose()
356 }
357
358 pub fn current(&self) -> Arc<KeySet> {
360 self.current
363 .read()
364 .unwrap_or_else(|e| e.into_inner())
365 .0
366 .clone()
367 }
368
369 pub fn generation(&self) -> u64 {
381 self.current.read().unwrap_or_else(|e| e.into_inner()).1
382 }
383
384 pub fn replace(&self, set: KeySet) {
393 let mut current = self.current.write().unwrap_or_else(|e| e.into_inner());
394 *current = (Arc::new(set), next_generation());
395 }
396}
397
398fn next_generation() -> u64 {
402 static NEXT: AtomicU64 = AtomicU64::new(1);
403 NEXT.fetch_add(1, Ordering::Relaxed)
404}
405
406enum Parsed {
408 Key(Key),
409 SetAside(String),
411}
412
413fn parse_jwk(value: &serde_json::Value) -> Result<Parsed, String> {
415 let jwk = value.as_object().ok_or("a JWK must be a JSON object")?;
416
417 let string = |name: &str| -> Result<Option<&str>, String> {
418 match jwk.get(name) {
419 None | Some(serde_json::Value::Null) => Ok(None),
420 Some(serde_json::Value::String(s)) => Ok(Some(s)),
421 Some(_) => Err(format!("\"{name}\" must be a string")),
422 }
423 };
424 let required = |name: &str| -> Result<&str, String> {
425 string(name)?.ok_or_else(|| format!("\"{name}\" is missing"))
426 };
427
428 let kty = required("kty")?;
429 let kid = string("kid")?.map(str::to_string);
430
431 match string("use")? {
435 None | Some("sig") => {}
436 Some(other) => return Ok(Parsed::SetAside(format!("\"use\" is \"{other}\""))),
437 }
438 if let Some(ops) = jwk.get("key_ops") {
439 let ops = ops
440 .as_array()
441 .ok_or("\"key_ops\" must be an array of strings")?;
442 if !ops.iter().any(|op| op.as_str() == Some("verify")) {
443 return Ok(Parsed::SetAside(
444 "\"key_ops\" does not include \"verify\"".into(),
445 ));
446 }
447 }
448
449 let alg = match string("alg")? {
450 None => None,
451 Some(name) => match signing_algorithm(name) {
452 Some(alg) => Some(alg),
453 None => {
456 return Ok(Parsed::SetAside(format!(
457 "\"alg\" is \"{name}\", which this server does not verify"
458 )))
459 }
460 },
461 };
462
463 let (kty, family, key): (&'static str, &'static [Algorithm], DecodingKey) = match kty {
464 "oct" => {
465 let k = base64url(required("k")?, "k")?;
466 if k.is_empty() {
467 return Err("\"k\" is empty".into());
468 }
469 ("oct", HMAC, DecodingKey::from_secret(&k))
470 }
471 "RSA" => {
472 let (n, e) = (required("n")?, required("e")?);
473 let modulus = base64url(n, "n")?;
474 let bits = significant_bits(&modulus);
475 if bits < MIN_RSA_BITS {
476 return Err(format!(
477 "the RSA modulus is {bits} bits; at least {MIN_RSA_BITS} are required"
478 ));
479 }
480 base64url(e, "e")?;
481 let key = DecodingKey::from_rsa_components(n, e).map_err(|e| e.to_string())?;
482 ("RSA", RSA, key)
483 }
484 "EC" => {
485 let (family, size): (&'static [Algorithm], usize) = match required("crv")? {
486 "P-256" => (&[Algorithm::ES256], 32),
487 "P-384" => (&[Algorithm::ES384], 48),
488 other => {
489 return Ok(Parsed::SetAside(format!(
490 "the curve \"{other}\" is not one this server verifies"
491 )))
492 }
493 };
494 let (x, y) = (required("x")?, required("y")?);
495 for (name, coordinate) in [("x", x), ("y", y)] {
496 if base64url(coordinate, name)?.len() != size {
497 return Err(format!("\"{name}\" must be {size} bytes for this curve"));
498 }
499 }
500 let key = DecodingKey::from_ec_components(x, y).map_err(|e| e.to_string())?;
501 ("EC", family, key)
502 }
503 "OKP" => {
504 match required("crv")? {
505 "Ed25519" => {}
506 other => {
507 return Ok(Parsed::SetAside(format!(
508 "the curve \"{other}\" is not one this server verifies"
509 )))
510 }
511 }
512 let x = required("x")?;
513 if base64url(x, "x")?.len() != 32 {
514 return Err("\"x\" must be 32 bytes for Ed25519".into());
515 }
516 let key = DecodingKey::from_ed_components(x).map_err(|e| e.to_string())?;
517 ("OKP", &[Algorithm::EdDSA], key)
518 }
519 other => {
520 return Ok(Parsed::SetAside(format!(
521 "the key type \"{other}\" is not one this server verifies"
522 )))
523 }
524 };
525
526 let algorithms: &'static [Algorithm] = match alg {
531 None => family,
532 Some(alg) => match family.iter().position(|a| *a == alg) {
533 Some(i) => &family[i..=i],
534 None => return Err(format!("\"alg\" is {alg:?}, which a {kty} key cannot do")),
535 },
536 };
537
538 Ok(Parsed::Key(Key {
539 kid,
540 any_kid: false,
541 algorithms,
542 kty,
543 key,
544 }))
545}
546
547fn signing_algorithm(name: &str) -> Option<Algorithm> {
553 Some(match name {
554 "HS256" => Algorithm::HS256,
555 "HS384" => Algorithm::HS384,
556 "HS512" => Algorithm::HS512,
557 "RS256" => Algorithm::RS256,
558 "RS384" => Algorithm::RS384,
559 "RS512" => Algorithm::RS512,
560 "PS256" => Algorithm::PS256,
561 "PS384" => Algorithm::PS384,
562 "PS512" => Algorithm::PS512,
563 "ES256" => Algorithm::ES256,
564 "ES384" => Algorithm::ES384,
565 "EdDSA" => Algorithm::EdDSA,
566 _ => return None,
567 })
568}
569
570fn base64url(value: &str, name: &str) -> Result<Vec<u8>, String> {
573 base64::engine::general_purpose::URL_SAFE_NO_PAD
574 .decode(value.trim_end_matches('='))
575 .map_err(|_| format!("\"{name}\" is not base64url"))
576}
577
578fn significant_bits(bytes: &[u8]) -> usize {
580 match bytes.iter().position(|b| *b != 0) {
581 None => 0,
582 Some(i) => (bytes.len() - i) * 8 - bytes[i].leading_zeros() as usize,
583 }
584}
585
586#[cfg(test)]
587mod tests {
588 use super::*;
589 use crate::{validate_token, ClaimFault, JwtConfig, JwtError};
590 use base64::engine::general_purpose::{STANDARD, URL_SAFE_NO_PAD};
591 use jsonwebtoken::{encode, EncodingKey, Header};
592 use serde_json::{json, Value};
593
594 fn fixture(name: &str) -> (EncodingKey, Value) {
598 let all: Value = serde_json::from_str(include_str!("../tests/fixtures/keys.json")).unwrap();
599 let entry = &all[name];
600 let der = STANDARD
601 .decode(entry["private_der"].as_str().unwrap())
602 .unwrap();
603 let key = match entry["jwk"]["kty"].as_str().unwrap() {
604 "RSA" => EncodingKey::from_rsa_der(&der),
605 "EC" => EncodingKey::from_ec_der(&der),
606 _ => EncodingKey::from_ed_der(&der),
607 };
608 (key, entry["jwk"].clone())
609 }
610
611 fn jwk(name: &str, extra: Value) -> Value {
613 let mut jwk = fixture(name).1;
614 jwk.as_object_mut()
615 .unwrap()
616 .extend(extra.as_object().unwrap().clone());
617 jwk
618 }
619
620 fn config_for(secret: &str) -> JwtConfig {
621 JwtConfig {
622 keys: JwtKeys::from_config(Some(secret), false).unwrap(),
623 ..JwtConfig::default()
624 }
625 }
626
627 fn jwks(keys: &[Value]) -> JwtConfig {
628 config_for(&json!({ "keys": keys }).to_string())
629 }
630
631 fn token(alg: Algorithm, kid: Option<&str>, key: &EncodingKey) -> String {
632 let mut header = Header::new(alg);
633 header.kid = kid.map(str::to_string);
634 encode(&header, &json!({"role": "web_user"}), key).unwrap()
635 }
636
637 fn signed_by(name: &str, alg: Algorithm, kid: Option<&str>) -> String {
638 token(alg, kid, &fixture(name).0)
639 }
640
641 fn accepted(token: &str, config: &JwtConfig) -> bool {
642 match validate_token(token, config) {
643 Ok(result) => {
644 assert_eq!(result.role, "web_user");
645 true
646 }
647 Err(_) => false,
648 }
649 }
650
651 fn refusal(token: &str, config: &JwtConfig) -> JwtError {
652 validate_token(token, config).expect_err("the token should have been refused")
653 }
654
655 #[test]
656 fn an_rsa_key_without_alg_admits_both_rsa_families() {
657 let config = config_for(&fixture("rsa_a").1.to_string());
658 for alg in RSA {
659 assert!(
660 accepted(&signed_by("rsa_a", *alg, None), &config),
661 "{alg:?}"
662 );
663 }
664 }
665
666 #[test]
667 fn each_curve_verifies_its_own_algorithm() {
668 for (name, alg) in [
669 ("ec_p256", Algorithm::ES256),
670 ("ec_p384", Algorithm::ES384),
671 ("ed25519", Algorithm::EdDSA),
672 ] {
673 let config = config_for(&fixture(name).1.to_string());
674 assert!(accepted(&signed_by(name, alg, None), &config), "{alg:?}");
675 }
676 }
677
678 #[test]
679 fn a_curve_admits_no_other_algorithm() {
680 let config = config_for(&fixture("ec_p384").1.to_string());
682 assert!(matches!(
683 refusal(&signed_by("ec_p256", Algorithm::ES256, None), &config),
684 JwtError::NoSuitableKey
685 ));
686 }
687
688 #[test]
689 fn a_token_signed_by_another_key_does_not_verify() {
690 let config = config_for(&fixture("rsa_a").1.to_string());
691 assert!(matches!(
692 refusal(&signed_by("rsa_b", Algorithm::RS256, None), &config),
693 JwtError::InvalidSignature
694 ));
695 }
696
697 #[test]
698 fn a_jwk_alg_narrows_what_the_key_admits() {
699 let config = config_for(&jwk("rsa_a", json!({"alg": "RS256"})).to_string());
700 assert!(accepted(
701 &signed_by("rsa_a", Algorithm::RS256, None),
702 &config
703 ));
704 assert!(matches!(
705 refusal(&signed_by("rsa_a", Algorithm::PS256, None), &config),
706 JwtError::NoSuitableKey
707 ));
708 }
709
710 #[test]
711 fn a_kid_selects_that_key_and_no_other() {
712 let config = jwks(&[
713 jwk("rsa_a", json!({"kid": "a"})),
714 jwk("rsa_b", json!({"kid": "b"})),
715 ]);
716 assert!(accepted(
717 &signed_by("rsa_b", Algorithm::RS256, Some("b")),
718 &config
719 ));
720 assert!(matches!(
722 refusal(&signed_by("rsa_b", Algorithm::RS256, Some("a")), &config),
723 JwtError::InvalidSignature
724 ));
725 assert!(matches!(
727 refusal(&signed_by("rsa_b", Algorithm::RS256, Some("c")), &config),
728 JwtError::NoSuitableKey
729 ));
730 }
731
732 #[test]
733 fn a_kid_does_not_select_a_key_without_one() {
734 let config = config_for(&fixture("rsa_a").1.to_string());
737 assert!(matches!(
738 refusal(&signed_by("rsa_a", Algorithm::RS256, Some("a")), &config),
739 JwtError::NoSuitableKey
740 ));
741 }
742
743 #[test]
744 fn without_a_kid_every_key_that_admits_the_algorithm_is_tried() {
745 let config = jwks(&[
746 jwk("rsa_a", json!({"kid": "a"})),
747 jwk("ec_p256", json!({})),
748 jwk("rsa_b", json!({"kid": "b"})),
749 ]);
750 assert!(accepted(
751 &signed_by("rsa_b", Algorithm::RS512, None),
752 &config
753 ));
754 assert!(accepted(
755 &signed_by("ec_p256", Algorithm::ES256, None),
756 &config
757 ));
758 assert!(matches!(
759 refusal(&signed_by("ed25519", Algorithm::EdDSA, None), &config),
760 JwtError::NoSuitableKey
761 ));
762 }
763
764 #[test]
765 fn an_hmac_token_made_from_a_public_key_finds_no_key() {
766 let public = fixture("rsa_a").1;
769 let config = config_for(&public.to_string());
770 let n = URL_SAFE_NO_PAD
771 .decode(public["n"].as_str().unwrap())
772 .unwrap();
773 for secret in [public.to_string().into_bytes(), n] {
774 let forged = token(Algorithm::HS256, None, &EncodingKey::from_secret(&secret));
775 assert!(matches!(refusal(&forged, &config), JwtError::NoSuitableKey));
776 }
777 }
778
779 #[test]
780 fn a_key_whose_alg_contradicts_its_type_is_a_configuration_error() {
781 let error = KeySet::from_secret(&jwk("rsa_a", json!({"alg": "HS256"})).to_string(), false)
782 .unwrap_err();
783 assert!(matches!(error, JwtKeyError::BadKey { .. }), "{error}");
784 }
785
786 #[test]
787 fn alg_none_is_refused_whatever_the_key() {
788 let unsigned = format!(
789 "{}.{}.",
790 URL_SAFE_NO_PAD.encode(json!({"alg": "none"}).to_string()),
791 URL_SAFE_NO_PAD.encode(json!({"role": "web_user"}).to_string())
792 );
793 for config in [
794 config_for("reallyreallyreallyreallyverysafe"),
795 config_for(&fixture("rsa_a").1.to_string()),
796 ] {
797 assert!(matches!(
798 refusal(&unsigned, &config),
799 JwtError::NoSuitableKey
800 ));
801 }
802 let error = KeySet::from_secret(&jwk("rsa_a", json!({"alg": "none"})).to_string(), false)
804 .unwrap_err();
805 assert!(matches!(error, JwtKeyError::NoUsableKeys(_)), "{error}");
806 }
807
808 #[test]
809 fn an_encryption_key_is_never_used_to_verify() {
810 let config = jwks(&[
811 jwk("rsa_a", json!({"kid": "a", "use": "enc"})),
812 jwk("rsa_b", json!({"kid": "b", "use": "sig"})),
813 ]);
814 assert!(matches!(
815 refusal(&signed_by("rsa_a", Algorithm::RS256, Some("a")), &config),
816 JwtError::NoSuitableKey
817 ));
818 assert!(matches!(
820 refusal(&signed_by("rsa_a", Algorithm::RS256, None), &config),
821 JwtError::InvalidSignature
822 ));
823 assert!(accepted(
824 &signed_by("rsa_b", Algorithm::RS256, None),
825 &config
826 ));
827
828 let config = jwks(&[
830 jwk("rsa_a", json!({"key_ops": ["encrypt"]})),
831 jwk("rsa_b", json!({})),
832 ]);
833 assert!(matches!(
834 refusal(&signed_by("rsa_a", Algorithm::RS256, None), &config),
835 JwtError::InvalidSignature
836 ));
837 }
838
839 #[test]
840 fn a_secret_of_nothing_but_encryption_keys_is_refused_at_startup() {
841 let only_enc = jwk("rsa_a", json!({"use": "enc"})).to_string();
842 assert!(matches!(
843 KeySet::from_secret(&only_enc, false),
844 Err(JwtKeyError::NoUsableKeys(_))
845 ));
846 assert!(matches!(
847 KeySet::from_secret(r#"{"keys": []}"#, false),
848 Err(JwtKeyError::NoUsableKeys(_))
849 ));
850 }
851
852 #[test]
853 fn keys_this_server_cannot_use_are_set_aside_not_fatal() {
854 let config = jwks(&[
857 json!({"kty": "EC", "crv": "P-521", "x": "AA", "y": "AA"}),
858 json!({"kty": "OKP", "crv": "X25519", "x": "AA"}),
859 jwk("rsa_b", json!({"alg": "RSA-OAEP"})),
860 json!({"kty": "unheard-of"}),
861 jwk("rsa_a", json!({})),
862 ]);
863 assert_eq!(config.keys.as_ref().unwrap().current().len(), 1);
864 assert!(accepted(
865 &signed_by("rsa_a", Algorithm::RS256, None),
866 &config
867 ));
868 }
869
870 #[test]
871 fn an_oct_jwk_is_an_hmac_key() {
872 let secret = b"reallyreallyreallyreallyverysafe";
873 let oct = json!({"kty": "oct", "k": URL_SAFE_NO_PAD.encode(secret)});
874 let config = config_for(&oct.to_string());
875 for alg in HMAC {
876 let signed = token(*alg, None, &EncodingKey::from_secret(secret));
877 assert!(accepted(&signed, &config), "{alg:?}");
878 }
879
880 let mut only_512 = oct.clone();
881 only_512["alg"] = json!("HS512");
882 let config = config_for(&only_512.to_string());
883 let hs256 = token(Algorithm::HS256, None, &EncodingKey::from_secret(secret));
884 assert!(matches!(refusal(&hs256, &config), JwtError::NoSuitableKey));
885 }
886
887 #[test]
888 fn a_plain_secret_is_hs256_and_ignores_kid() {
889 let secret = "reallyreallyreallyreallyverysafe";
890 let key = EncodingKey::from_secret(secret.as_bytes());
891 let config = config_for(secret);
892 assert!(accepted(&token(Algorithm::HS256, None, &key), &config));
893 assert!(accepted(
894 &token(Algorithm::HS256, Some("whatever"), &key),
895 &config
896 ));
897 assert!(matches!(
899 refusal(&token(Algorithm::HS512, None, &key), &config),
900 JwtError::NoSuitableKey
901 ));
902 let other = EncodingKey::from_secret(b"other");
903 assert!(matches!(
904 refusal(&token(Algorithm::HS256, None, &other), &config),
905 JwtError::InvalidSignature
906 ));
907 }
908
909 #[test]
910 fn a_base64_secret_may_itself_be_a_jwk() {
911 let public = STANDARD.encode(fixture("ed25519").1.to_string());
912 let config = JwtConfig {
913 keys: JwtKeys::from_config(Some(&public), true).unwrap(),
914 ..JwtConfig::default()
915 };
916 assert!(accepted(
917 &signed_by("ed25519", Algorithm::EdDSA, None),
918 &config
919 ));
920
921 let secret = STANDARD.encode("reallyreallyreallyreallyverysafe");
922 let config = JwtConfig {
923 keys: JwtKeys::from_config(Some(&secret), true).unwrap(),
924 ..JwtConfig::default()
925 };
926 let key = EncodingKey::from_secret(b"reallyreallyreallyreallyverysafe");
927 assert!(accepted(&token(Algorithm::HS256, None, &key), &config));
928
929 assert_eq!(
930 KeySet::from_secret("not base64!", true).unwrap_err(),
931 JwtKeyError::NotBase64
932 );
933 }
934
935 #[test]
936 fn a_malformed_key_is_a_configuration_error() {
937 let rsa = fixture("rsa_a").1;
938 let short_modulus =
939 json!({"kty": "RSA", "n": URL_SAFE_NO_PAD.encode([0xffu8; 128]), "e": "AQAB"});
940 let mut no_n = rsa.clone();
941 no_n.as_object_mut().unwrap().remove("n");
942 let mut bad_x = fixture("ec_p256").1;
943 bad_x["x"] = json!("AAAA");
944
945 assert!(matches!(
946 KeySet::from_secret(r#"{"kty": "RSA", "n": "#, false),
947 Err(JwtKeyError::NotJson(_))
948 ));
949 assert_eq!(
950 KeySet::from_secret(r#"{"hello": "world"}"#, false).unwrap_err(),
951 JwtKeyError::NotAKey
952 );
953 for malformed in [short_modulus, no_n, bad_x, json!({"keys": "nope"})] {
954 let error = KeySet::from_secret(&malformed.to_string(), false).unwrap_err();
955 assert!(matches!(error, JwtKeyError::BadKey { .. }), "{error}");
956 }
957 let error = KeySet::from_secret(
959 &json!({"keys": [rsa, {"kty": "RSA", "e": "AQAB"}]}).to_string(),
960 false,
961 )
962 .unwrap_err();
963 assert_eq!(error.to_string(), "key 2 of the JWK Set: \"n\" is missing");
964 }
965
966 #[test]
967 fn replacing_the_set_is_seen_by_every_holder() {
968 let set = |name: &str| KeySet::from_secret(&fixture(name).1.to_string(), false).unwrap();
969 let keys = JwtKeys::new(set("rsa_a"));
970 let config = JwtConfig {
971 keys: Some(keys.clone()),
972 ..JwtConfig::default()
973 };
974 let by_b = signed_by("rsa_b", Algorithm::RS256, None);
975 assert!(!accepted(&by_b, &config));
976
977 keys.replace(set("rsa_b"));
978 assert!(accepted(&by_b, &config));
979 }
980
981 #[test]
982 fn debug_output_shows_no_key_material() {
983 let secret = "reallyreallyreallyreallyverysafe";
984 let k = URL_SAFE_NO_PAD.encode(secret);
985 let oct = json!({"kty": "oct", "kid": "k1", "k": k});
986 for keys in [
987 KeySet::from_secret(secret, false).unwrap(),
988 KeySet::from_secret(&oct.to_string(), false).unwrap(),
989 ] {
990 let shown = format!("{keys:?}");
991 assert!(!shown.contains(secret) && !shown.contains(&k), "{shown}");
992 }
993 }
994
995 #[test]
996 fn the_issuer_is_checked_once_the_signature_verifies() {
997 let config = JwtConfig {
998 issuer: Some("https://issuer.example".into()),
999 ..config_for(&fixture("rsa_a").1.to_string())
1000 };
1001 let (key, _) = fixture("rsa_a");
1002 let with = |claims: Value| encode(&Header::new(Algorithm::RS256), &claims, &key).unwrap();
1003 assert!(validate_token(
1004 &with(json!({"role": "r", "iss": "https://issuer.example"})),
1005 &config
1006 )
1007 .is_ok());
1008 for claims in [
1009 json!({"role": "r", "iss": "https://elsewhere.example"}),
1010 json!({"role": "r", "iss": ["https://issuer.example"]}),
1011 json!({"role": "r"}),
1012 ] {
1013 assert!(matches!(
1014 validate_token(&with(claims), &config),
1015 Err(JwtError::Claim(ClaimFault::UnexpectedIssuer))
1016 ));
1017 }
1018 }
1019}