webauthn_rp

WebAuthn RP library.
git clone https://git.philomathiclife.com/repos/webauthn_rp
Log | Files | Refs | README

tests.rs (9294B)


      1 use super::{
      2     super::{super::PublicKeyCredentialHint, ExtensionReq},
      3     ClientCredentialRequestOptions, CredentialMediationRequirement, CredentialUiMode,
      4     ExtensionOwned, FIVE_MINUTES, Hints, NonZeroU32, PublicKeyCredentialRequestOptionsOwned,
      5     UserVerificationRequirement,
      6 };
      7 use serde_json::Error;
      8 #[expect(
      9     clippy::panic_in_result_fn,
     10     clippy::unwrap_used,
     11     reason = "OK in tests"
     12 )]
     13 #[expect(clippy::cognitive_complexity, reason = "a lot to test")]
     14 #[test]
     15 fn client_options() -> Result<(), Error> {
     16     let mut err =
     17         serde_json::from_str::<ClientCredentialRequestOptions>(r#"{"bob":true}"#).unwrap_err();
     18     assert_eq!(
     19         err.to_string().get(..71),
     20         Some("unknown field `bob`, expected one of `mediation`, `uiMode`, `publicKey`")
     21     );
     22     err = serde_json::from_str::<ClientCredentialRequestOptions>(
     23         r#"{"mediation":"required","mediation":"required"}"#,
     24     )
     25     .unwrap_err();
     26     assert_eq!(
     27         err.to_string().get(..27),
     28         Some("duplicate field `mediation`")
     29     );
     30     let mut options = serde_json::from_str::<ClientCredentialRequestOptions>("{}")?;
     31     assert!(matches!(
     32         options.mediation,
     33         CredentialMediationRequirement::Required
     34     ));
     35     assert!(options.ui_mode.is_none());
     36     assert!(options.public_key.rp_id.is_none());
     37     assert_eq!(options.public_key.timeout, FIVE_MINUTES);
     38     assert!(matches!(
     39         options.public_key.user_verification,
     40         UserVerificationRequirement::Preferred
     41     ));
     42     assert_eq!(options.public_key.hints, Hints::EMPTY);
     43     assert!(options.public_key.extensions.prf.is_none());
     44     options = serde_json::from_str::<ClientCredentialRequestOptions>(
     45         r#"{"mediation":null,"uiMode":null,"publicKey":null}"#,
     46     )?;
     47     assert!(matches!(
     48         options.mediation,
     49         CredentialMediationRequirement::Required
     50     ));
     51     assert!(options.ui_mode.is_none());
     52     assert!(options.public_key.rp_id.is_none());
     53     assert_eq!(options.public_key.timeout, FIVE_MINUTES);
     54     assert!(matches!(
     55         options.public_key.user_verification,
     56         UserVerificationRequirement::Preferred
     57     ));
     58     assert_eq!(options.public_key.hints, Hints::EMPTY);
     59     assert!(options.public_key.extensions.prf.is_none());
     60     options = serde_json::from_str::<ClientCredentialRequestOptions>(r#"{"publicKey":{}}"#)?;
     61     assert!(options.public_key.rp_id.is_none());
     62     assert_eq!(options.public_key.timeout, FIVE_MINUTES);
     63     assert!(matches!(
     64         options.public_key.user_verification,
     65         UserVerificationRequirement::Preferred
     66     ));
     67     assert_eq!(options.public_key.hints, Hints::EMPTY);
     68     assert!(options.public_key.extensions.prf.is_none());
     69     options = serde_json::from_str::<ClientCredentialRequestOptions>(
     70         r#"{"mediation":"conditional","uiMode":"immediate","publicKey":{"rpId":"example.com","timeout":300000,"allowCredentials":[],"userVerification":"required","extensions":{"prf":{"eval":{"first":"","second":""}}},"hints":["security-key"],"challenge":null}}"#,
     71     )?;
     72     assert!(matches!(
     73         options.mediation,
     74         CredentialMediationRequirement::Conditional
     75     ));
     76     assert_eq!(options.ui_mode, Some(CredentialUiMode::Immediate));
     77     assert!(
     78         options
     79             .public_key
     80             .rp_id
     81             .is_some_and(|val| val.as_ref() == "example.com")
     82     );
     83     assert_eq!(options.public_key.timeout, FIVE_MINUTES);
     84     assert!(matches!(
     85         options.public_key.user_verification,
     86         UserVerificationRequirement::Required
     87     ));
     88     assert!(
     89         options
     90             .public_key
     91             .extensions
     92             .prf
     93             .is_some_and(|prf| prf.first.is_empty()
     94                 && prf.second.is_some_and(|p| p.is_empty())
     95                 && matches!(prf.ext_req, ExtensionReq::Allow))
     96     );
     97     Ok(())
     98 }
     99 #[expect(
    100     clippy::panic_in_result_fn,
    101     clippy::unwrap_used,
    102     reason = "OK in tests"
    103 )]
    104 #[expect(clippy::cognitive_complexity, reason = "a lot to test")]
    105 #[test]
    106 fn key_options() -> Result<(), Error> {
    107     let mut err = serde_json::from_str::<PublicKeyCredentialRequestOptionsOwned>(r#"{"bob":true}"#)
    108         .unwrap_err();
    109     assert_eq!(
    110         err.to_string().get(..130),
    111         Some(
    112             "unknown field `bob`, expected one of `rpId`, `userVerification`, `challenge`, `timeout`, `allowCredentials`, `hints`, `extensions`"
    113         )
    114     );
    115     err = serde_json::from_str::<PublicKeyCredentialRequestOptionsOwned>(
    116         r#"{"rpId":"example.com","rpId":"example.com"}"#,
    117     )
    118     .unwrap_err();
    119     assert_eq!(err.to_string().get(..22), Some("duplicate field `rpId`"));
    120     err = serde_json::from_str::<PublicKeyCredentialRequestOptionsOwned>(
    121         r#"{"challenge":"AAAAAAAAAAAAAAAAAAAAAA"}"#,
    122     )
    123     .unwrap_err();
    124     assert_eq!(
    125         err.to_string().get(..41),
    126         Some("invalid type: Option value, expected null")
    127     );
    128     err = serde_json::from_str::<PublicKeyCredentialRequestOptionsOwned>(
    129         r#"{"allowCredentials":[{"type":"public-key","transports":["usb"],"id":"AAAAAAAAAAAAAAAAAAAAAA"}]}"#,
    130     )
    131     .unwrap_err();
    132     assert_eq!(err.to_string().get(..19), Some("trailing characters"));
    133     err = serde_json::from_str::<PublicKeyCredentialRequestOptionsOwned>(r#"{"timeout":0}"#)
    134         .unwrap_err();
    135     assert_eq!(
    136         err.to_string().get(..50),
    137         Some("invalid value: integer `0`, expected a nonzero u32")
    138     );
    139     err =
    140         serde_json::from_str::<PublicKeyCredentialRequestOptionsOwned>(r#"{"timeout":4294967296}"#)
    141             .unwrap_err();
    142     assert_eq!(
    143         err.to_string().get(..59),
    144         Some("invalid value: integer `4294967296`, expected a nonzero u32")
    145     );
    146     let mut key = serde_json::from_str::<PublicKeyCredentialRequestOptionsOwned>("{}")?;
    147     assert!(key.rp_id.is_none());
    148     assert_eq!(key.timeout, FIVE_MINUTES);
    149     assert!(matches!(
    150         key.user_verification,
    151         UserVerificationRequirement::Preferred
    152     ));
    153     assert!(key.extensions.prf.is_none());
    154     assert_eq!(key.hints, Hints::EMPTY);
    155     key = serde_json::from_str::<PublicKeyCredentialRequestOptionsOwned>(
    156         r#"{"rpId":null,"timeout":null,"allowCredentials":null,"userVerification":null,"extensions":null,"hints":null,"challenge":null}"#,
    157     )?;
    158     assert!(key.rp_id.is_none());
    159     assert_eq!(key.timeout, FIVE_MINUTES);
    160     assert!(matches!(
    161         key.user_verification,
    162         UserVerificationRequirement::Preferred
    163     ));
    164     assert!(key.extensions.prf.is_none());
    165     assert_eq!(key.hints, Hints::EMPTY);
    166     key = serde_json::from_str::<PublicKeyCredentialRequestOptionsOwned>(
    167         r#"{"allowCredentials":[],"extensions":{},"hints":[]}"#,
    168     )?;
    169     assert!(matches!(
    170         key.user_verification,
    171         UserVerificationRequirement::Preferred
    172     ));
    173     assert_eq!(key.hints, Hints::EMPTY);
    174     assert!(key.extensions.prf.is_none());
    175     key = serde_json::from_str::<PublicKeyCredentialRequestOptionsOwned>(
    176         r#"{"extensions":{"prf":null}}"#,
    177     )?;
    178     assert!(key.extensions.prf.is_none());
    179     key = serde_json::from_str::<PublicKeyCredentialRequestOptionsOwned>(
    180         r#"{"rpId":"example.com","timeout":300000,"allowCredentials":[],"userVerification":"required","extensions":{"prf":{"eval":{"first":"","second":""}}},"hints":["security-key"],"challenge":null}"#,
    181     )?;
    182     assert!(key.rp_id.is_some_and(|val| val.as_ref() == "example.com"));
    183     assert_eq!(key.timeout, FIVE_MINUTES);
    184     assert!(matches!(
    185         key.user_verification,
    186         UserVerificationRequirement::Required
    187     ));
    188     assert_eq!(
    189         key.hints,
    190         Hints::EMPTY.add(PublicKeyCredentialHint::SecurityKey)
    191     );
    192     assert!(key.extensions.prf.is_some_and(|prf| prf.first.is_empty()
    193         && prf.second.is_some_and(|p| p.is_empty())
    194         && matches!(prf.ext_req, ExtensionReq::Allow)));
    195     key = serde_json::from_str::<PublicKeyCredentialRequestOptionsOwned>(
    196         r#"{"timeout":4294967295}"#,
    197     )?;
    198     assert_eq!(key.timeout, NonZeroU32::MAX);
    199     Ok(())
    200 }
    201 #[expect(
    202     clippy::panic_in_result_fn,
    203     clippy::unwrap_used,
    204     reason = "OK in tests"
    205 )]
    206 #[test]
    207 fn extension() -> Result<(), Error> {
    208     let mut err = serde_json::from_str::<ExtensionOwned>(r#"{"bob":true}"#).unwrap_err();
    209     assert_eq!(
    210         err.to_string().get(..35),
    211         Some("unknown field `bob`, expected `prf`")
    212     );
    213     err = serde_json::from_str::<ExtensionOwned>(
    214         r#"{"prf":{"eval":{"first":"","second":""}},"prf":{"eval":{"first":"","second":""}}}"#,
    215     )
    216     .unwrap_err();
    217     assert_eq!(err.to_string().get(..21), Some("duplicate field `prf`"));
    218     err = serde_json::from_str::<ExtensionOwned>(r#"{"prf":{"eval":{"first":null}}}"#).unwrap_err();
    219     assert_eq!(
    220         err.to_string().get(..51),
    221         Some("invalid type: null, expected base64url-encoded data")
    222     );
    223     let mut ext =
    224         serde_json::from_str::<ExtensionOwned>(r#"{"prf":{"eval":{"first":"","second":""}}}"#)?;
    225     assert!(ext.prf.is_some_and(|prf| prf.first.is_empty()
    226         && prf.second.is_some_and(|v| v.is_empty())
    227         && matches!(prf.ext_req, ExtensionReq::Allow)));
    228     ext = serde_json::from_str::<ExtensionOwned>(r#"{"prf":null}"#)?;
    229     assert!(ext.prf.is_none());
    230     ext = serde_json::from_str::<ExtensionOwned>("{}")?;
    231     assert!(ext.prf.is_none());
    232     Ok(())
    233 }