1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//! SAML 2.0 service-provider login + Assertion Consumer Service (#381).
//!
//! This module is gated behind the **non-default** `auth-saml` Cargo feature, which pulls
//! in [`samael`](https://crates.io/crates/samael) and its `xmlsec` backend (libxml2 +
//! openssl + the xmlsec1 C library). The default build stays lean and free of the C
//! XML/crypto stack.
//!
//! # What this slice ships
//!
//! - SP-initiated SSO: [`saml_login`] builds a signed-or-unsigned `AuthnRequest` and redirects the
//! browser to the IdP (HTTP-Redirect binding).
//! - Assertion Consumer Service: [`saml_acs`] receives the IdP's `SAMLResponse` (HTTP-POST
//! binding), verifies it, resolves a local user, and creates a session.
//!
//! The broader #381 umbrella (multi-IdP discovery, per-tenant SAML config storage, SCIM)
//! stays open; this is the smallest shippable, security-complete SP login + ACS slice.
//!
//! # Security model (all fail-closed)
//!
//! [`verify_saml_response`] is the heart. It owns, in order:
//!
//! 1. **XXE defense** — the raw response is rejected outright if it carries a `DOCTYPE` or entity
//! declaration (SAML never legitimately needs a DTD), so no entity expansion or external-entity
//! fetch can occur — rejected before the XML ever reaches a parser.
//! 2. **Signature + assertion validation** — delegated to `samael`, which *reduces* the document to
//! only the bytes covered by a verified signature and parses **that** (XML-Signature-Wrapping
//! defense by construction — the asserted element is taken by reference to what was signed,
//! never re-queried from the original DOM), then enforces audience, `Recipient`/`Destination`,
//! `NotBefore`/`NotOnOrAfter`, issuer, and `InResponseTo`. A configured **signature-algorithm
//! allow-list** blocks algorithm substitution.
//! 3. **Replay protection** — the assertion `ID` is recorded single-use in a [`SamlReplayCache`]; a
//! second presentation of the same assertion is rejected.
//!
//! # Account linking (tenant-bounded trust, #368/#381)
//!
//! A successfully verified assertion maps to a local user via the existing
//! [`crate::account_linking::AccountStore::link_or_create_user`] keyed on
//! `("saml:<idp>", NameID)`. Whether the asserted email is allowed to *merge* across
//! providers is governed by [`effective_saml_email_verified`] — opt-in per IdP, default
//! off, and **never** by registering the IdP into the global
//! [`crate::account_linking::TrustedEmailProviders`] set. See that function's docs for the
//! tenant-bounding rule that prevents a cross-tenant nOAuth merge.
pub use ;
pub use ;
pub use ;
pub use SamlReplayCache;
pub use ;
/// Errors raised by the SAML SP login + ACS flow.