Skip to main content

saml_rs/api/
options.rs

1use crate::browser::{LogoutBinding, SsoRequestBinding, SsoResponseBinding};
2use crate::model::RelayStateParam;
3
4/// Explicit `ForceAuthn` value for outbound AuthnRequests.
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum ForceAuthn {
7    /// Emit `ForceAuthn="true"`.
8    Required,
9    /// Emit `ForceAuthn="false"`.
10    NotRequired,
11}
12
13impl ForceAuthn {
14    pub(super) fn as_bool(self) -> bool {
15        match self {
16            Self::Required => true,
17            Self::NotRequired => false,
18        }
19    }
20}
21
22/// Options for starting SP-initiated Web SSO.
23#[derive(Debug, Clone)]
24pub struct StartSso {
25    pub(super) binding: SsoRequestBinding,
26    pub(super) response_binding: Option<SsoResponseBinding>,
27    pub(super) relay_state: RelayStateParam,
28    pub(super) force_authn: Option<ForceAuthn>,
29    pub(super) acs_index: Option<u16>,
30}
31
32impl StartSso {
33    /// Start SSO with HTTP-Redirect AuthnRequest dispatch.
34    pub fn redirect() -> Self {
35        Self::new(SsoRequestBinding::Redirect)
36    }
37
38    /// Start SSO with HTTP-POST AuthnRequest dispatch.
39    pub fn post() -> Self {
40        Self::new(SsoRequestBinding::Post)
41    }
42
43    /// Start SSO with HTTP-POST-SimpleSign AuthnRequest dispatch.
44    pub fn simple_sign() -> Self {
45        Self::new(SsoRequestBinding::SimpleSign)
46    }
47
48    fn new(binding: SsoRequestBinding) -> Self {
49        Self {
50            binding,
51            response_binding: None,
52            relay_state: RelayStateParam::absent(),
53            force_authn: None,
54            acs_index: None,
55        }
56    }
57
58    /// Set the expected SAML Response binding.
59    pub fn response_binding(mut self, binding: SsoResponseBinding) -> Self {
60        self.response_binding = Some(binding);
61        self
62    }
63
64    /// Set exact RelayState state for the outbound request.
65    pub fn relay_state(mut self, relay_state: RelayStateParam) -> Self {
66        self.relay_state = relay_state;
67        self
68    }
69
70    /// Set the exact `ForceAuthn` value.
71    pub fn force_authn(mut self, force_authn: ForceAuthn) -> Self {
72        self.force_authn = Some(force_authn);
73        self
74    }
75
76    /// Select an AssertionConsumerServiceIndex.
77    pub fn assertion_consumer_service_index(mut self, acs_index: u16) -> Self {
78        self.acs_index = Some(acs_index);
79        self
80    }
81}
82
83/// Options for issuing SAML Responses from an IdP.
84#[derive(Debug, Clone)]
85pub struct RespondSso {
86    pub(super) binding: SsoResponseBinding,
87    pub(super) relay_state: Option<RelayStateParam>,
88    response_signing: ResponseSigning,
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92enum ResponseSigning {
93    FollowEncryptedCbcRecommendation,
94    Always,
95    AllowUnsignedEncryptedCbcForCompatibility,
96}
97
98impl RespondSso {
99    /// Respond with HTTP-POST.
100    pub fn post() -> Self {
101        Self::new(SsoResponseBinding::Post)
102    }
103
104    /// Respond with HTTP-POST-SimpleSign.
105    pub fn simple_sign() -> Self {
106        Self::new(SsoResponseBinding::SimpleSign)
107    }
108
109    fn new(binding: SsoResponseBinding) -> Self {
110        Self {
111            binding,
112            relay_state: None,
113            response_signing: ResponseSigning::FollowEncryptedCbcRecommendation,
114        }
115    }
116
117    /// Always authenticate the top-level SAML Response.
118    ///
119    /// HTTP-POST embeds an XML signature covering the Response. HTTP-POST-
120    /// SimpleSign continues to use its binding-defined detached signature.
121    pub fn sign_response(mut self) -> Self {
122        self.response_signing = ResponseSigning::Always;
123        self
124    }
125
126    /// Allow an unsigned Response around a CBC-encrypted Assertion.
127    ///
128    /// This explicitly relaxes SAML V2.0 Approved Errata 05 E93, which
129    /// recommends signing the Response so the ciphertext is integrity
130    /// protected. By default, typed IdPs sign such Responses automatically.
131    pub fn allow_unsigned_encrypted_cbc_for_compatibility(mut self) -> Self {
132        self.response_signing = ResponseSigning::AllowUnsignedEncryptedCbcForCompatibility;
133        self
134    }
135
136    /// Set exact RelayState state for the response.
137    ///
138    /// When omitted for a response to a received request, the received
139    /// RelayState is echoed. Pass [`RelayStateParam::absent`] to suppress echo.
140    pub fn relay_state(mut self, relay_state: RelayStateParam) -> Self {
141        self.relay_state = Some(relay_state);
142        self
143    }
144
145    pub(super) fn should_sign_response(
146        &self,
147        assertion_encrypted: bool,
148        data_encryption_algorithm: &str,
149    ) -> bool {
150        match self.response_signing {
151            ResponseSigning::FollowEncryptedCbcRecommendation => {
152                assertion_encrypted
153                    && crate::constants::is_xml_encryption_cbc_algorithm(data_encryption_algorithm)
154            }
155            ResponseSigning::Always => true,
156            ResponseSigning::AllowUnsignedEncryptedCbcForCompatibility => false,
157        }
158    }
159}
160
161/// Explicit signing choice for typed Single Logout requests.
162#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub enum LogoutSigning {
164    /// Use the local typed logout policy.
165    FollowLocalPolicy,
166    /// Sign this logout message.
167    Sign,
168    /// Send unsigned logout for an explicit compatibility exception.
169    DoNotSignForCompatibility,
170}
171
172/// Options for issuing a LogoutRequest.
173#[derive(Debug, Clone)]
174pub struct StartSlo {
175    pub(super) binding: LogoutBinding,
176    pub(super) relay_state: RelayStateParam,
177    pub(super) signing: LogoutSigning,
178}
179
180impl StartSlo {
181    /// Start SLO with HTTP-Redirect.
182    pub fn redirect() -> Self {
183        Self::new(LogoutBinding::Redirect)
184    }
185
186    /// Start SLO with HTTP-POST.
187    pub fn post() -> Self {
188        Self::new(LogoutBinding::Post)
189    }
190
191    /// Start SLO with HTTP-POST-SimpleSign.
192    pub fn simple_sign() -> Self {
193        Self::new(LogoutBinding::SimpleSign)
194    }
195
196    fn new(binding: LogoutBinding) -> Self {
197        Self {
198            binding,
199            relay_state: RelayStateParam::absent(),
200            signing: LogoutSigning::FollowLocalPolicy,
201        }
202    }
203
204    /// Set exact RelayState state for the logout request.
205    pub fn relay_state(mut self, relay_state: RelayStateParam) -> Self {
206        self.relay_state = relay_state;
207        self
208    }
209
210    /// Set logout request signing behavior.
211    pub fn signing(mut self, signing: LogoutSigning) -> Self {
212        self.signing = signing;
213        self
214    }
215}
216
217/// Options for issuing a LogoutResponse.
218#[derive(Debug, Clone)]
219pub struct RespondSlo {
220    pub(super) binding: LogoutBinding,
221    pub(super) relay_state: Option<RelayStateParam>,
222}
223
224impl RespondSlo {
225    /// Respond with HTTP-Redirect.
226    pub fn redirect() -> Self {
227        Self::new(LogoutBinding::Redirect)
228    }
229
230    /// Respond with HTTP-POST.
231    pub fn post() -> Self {
232        Self::new(LogoutBinding::Post)
233    }
234
235    /// Respond with HTTP-POST-SimpleSign.
236    pub fn simple_sign() -> Self {
237        Self::new(LogoutBinding::SimpleSign)
238    }
239
240    fn new(binding: LogoutBinding) -> Self {
241        Self {
242            binding,
243            relay_state: None,
244        }
245    }
246
247    /// Set exact RelayState state for the logout response.
248    ///
249    /// When omitted, the received LogoutRequest RelayState is echoed. Pass
250    /// [`RelayStateParam::absent`] to suppress echo.
251    pub fn relay_state(mut self, relay_state: RelayStateParam) -> Self {
252        self.relay_state = Some(relay_state);
253        self
254    }
255}