webauthn_kit/config.rs
1//! Relying-party configuration.
2
3use crate::attestation::AttestationPolicy;
4use crate::policy::{AttestationConveyance, CredentialPolicy, ResidentKeyPolicy};
5
6/// `WebAuthn` relying party configuration.
7///
8/// This is the kit-local replacement for any application-specific auth config
9/// struct: it carries everything the protocol layer needs and nothing else.
10/// Application concerns (feature flags, storage, session handling) are
11/// deliberately excluded.
12///
13/// # Security notes
14///
15/// - `rp_id` must be the registrable domain suffix shared by all origins that
16/// may perform `WebAuthn` ceremonies. It is hashed (SHA-256) and compared
17/// against the `rpIdHash` embedded in authenticator data; a wrong value
18/// fails every verification.
19/// - Every entry in `rp_origins` is an exact-match allow-list entry. Origins
20/// are compared byte-for-byte against `clientDataJSON.origin`; scheme,
21/// host, and port must all match. List only origins you fully trust.
22/// - `allowed_algorithms` controls which COSE algorithms are *advertised* to
23/// authenticators in registration options. Runtime verification implements
24/// ES256 (-7), ES384 (-35), EdDSA (-8), and RS256 (-257); anything else is
25/// rejected with [`crate::WebauthnError::UnsupportedAlgorithm`] regardless
26/// of this setting. For server-side *enforcement* of the advertised set
27/// (rejecting credentials with other algorithms), set
28/// `credential_policy.allowed_algorithms` — advertising alone is not
29/// enforcement.
30/// - `attestation` governs attestation statement verification during
31/// registration; see [`AttestationPolicy`] for the trust implications of
32/// empty `trust_anchors` and `allow_unknown_formats`.
33/// - `credential_policy` is enforced server-side by
34/// [`crate::verify_registration`] / [`crate::verify_authentication`]; the
35/// `resident_key` / `attestation_conveyance` / advertised-algorithm
36/// settings are *preferences conveyed to the client* and cannot be relied
37/// on for enforcement.
38#[derive(Debug, Clone)]
39pub struct WebauthnConfig {
40 /// Relying party ID (effective domain, e.g. `"example.com"`).
41 ///
42 /// Must NOT include scheme or port. See struct-level security notes.
43 pub rp_id: String,
44 /// Human-readable relying party name (advertised in registration options).
45 pub rp_name: String,
46 /// Allowed origins for `WebAuthn` ceremonies (exact match, e.g.
47 /// `"https://example.com"`).
48 pub rp_origins: Vec<String>,
49 /// COSE algorithm identifiers advertised for registration.
50 ///
51 /// Defaults to `[-7 (ES256), -35 (ES384), -257 (RS256)]`.
52 pub allowed_algorithms: Vec<i32>,
53 /// Challenge freshness window in seconds (default 300 = 5 minutes).
54 ///
55 /// Challenges older than this are rejected on consumption.
56 pub challenge_timeout_secs: u64,
57 /// Attestation verification policy applied by
58 /// [`crate::verify_registration`] (default: strict — unknown formats
59 /// rejected, no trust anchors configured).
60 pub attestation: AttestationPolicy,
61 /// Server-side credential policy enforced during both ceremonies
62 /// (default: permissive — UV reported, syncable credentials allowed, no
63 /// algorithm allowlist). See [`CredentialPolicy`].
64 pub credential_policy: CredentialPolicy,
65 /// Discoverable-credential preference conveyed in registration options
66 /// (default: [`ResidentKeyPolicy::Preferred`]).
67 pub resident_key: ResidentKeyPolicy,
68 /// Attestation conveyance preference conveyed in registration options
69 /// (default: [`AttestationConveyance::None`]).
70 pub attestation_conveyance: AttestationConveyance,
71}
72
73impl Default for WebauthnConfig {
74 /// Sensible development defaults (`localhost` RP).
75 ///
76 /// Do not use `Default::default()` in production; set `rp_id` and
77 /// `rp_origins` to your real domain and HTTPS origin.
78 fn default() -> Self {
79 Self {
80 rp_id: "localhost".to_string(),
81 rp_name: "webauthn-kit".to_string(),
82 rp_origins: vec!["http://localhost:8080".to_string()],
83 allowed_algorithms: vec![-7, -35, -257],
84 challenge_timeout_secs: 300,
85 attestation: AttestationPolicy::default(),
86 credential_policy: CredentialPolicy::default(),
87 resident_key: ResidentKeyPolicy::default(),
88 attestation_conveyance: AttestationConveyance::default(),
89 }
90 }
91}
92
93// Tests exercise failure paths and invariants directly; unwrap/expect,
94// slicing, and panicking asserts are acceptable here — violations
95// surface as test failures, not production panics.
96#[allow(
97 clippy::unwrap_used,
98 clippy::expect_used,
99 clippy::indexing_slicing,
100 clippy::panic
101)]
102#[cfg(test)]
103mod tests {
104 use super::*;
105 use crate::policy::UserVerificationPolicy;
106
107 #[test]
108 fn test_config_default() {
109 let config = WebauthnConfig::default();
110 assert_eq!(config.rp_id, "localhost");
111 assert_eq!(config.rp_name, "webauthn-kit");
112 assert_eq!(config.rp_origins, vec!["http://localhost:8080"]);
113 assert_eq!(config.allowed_algorithms, vec![-7, -35, -257]);
114 assert_eq!(config.challenge_timeout_secs, 300);
115 assert_eq!(config.credential_policy, CredentialPolicy::default());
116 assert_eq!(
117 config.credential_policy.user_verification,
118 UserVerificationPolicy::Preferred
119 );
120 assert_eq!(config.resident_key, ResidentKeyPolicy::Preferred);
121 assert_eq!(config.attestation_conveyance, AttestationConveyance::None);
122 }
123
124 #[test]
125 fn test_config_custom_algorithms() {
126 let config = WebauthnConfig {
127 allowed_algorithms: vec![-7],
128 ..WebauthnConfig::default()
129 };
130 assert_eq!(config.allowed_algorithms.len(), 1);
131 assert_eq!(config.allowed_algorithms[0], -7);
132 }
133}