Skip to main content

sso/
sso.rs

1//! End-to-end typed SSO: SP starts an `AuthnRequest`, the IdP receives it and
2//! issues a signed `Response`, and the SP finishes with a typed session.
3//!
4//! Run with: `cargo run -p saml-rs --example sso`
5//! (the `crypto-bergshamra` feature is on by default).
6
7#[cfg(any(
8    feature = "crypto-rustcrypto",
9    feature = "crypto-aws-lc",
10    feature = "crypto-fips"
11))]
12fn main() -> Result<(), Box<dyn std::error::Error>> {
13    use saml_rs::{
14        AcsEndpoint, AuthnRequest, BrowserInput, CertificatePem, Credentials, EntityId, IdpConfig,
15        IdpDescriptor, IdpValidationPolicy, MetadataTrustPolicy, NameId, PrivateKeyPem,
16        RelayStateParam, ReplayPolicy, RespondSso, Saml, SamlValidationContext, SpConfig,
17        SpDescriptor, SpValidationPolicy, SsoEndpoint, SsoResponse, StartSso, Subject,
18    };
19    use std::time::SystemTime;
20
21    let privkey = include_str!("../tests/fixtures/key/sp_privkey.pem");
22    let cert = include_str!("../tests/fixtures/key/sp_signing_cert.cer");
23    let credentials = || Credentials {
24        signing_key: Some(PrivateKeyPem::new(privkey)),
25        signing_certificate: Some(CertificatePem::new(cert)),
26        ..Credentials::default()
27    };
28    let validation =
29        || SamlValidationContext::new(SystemTime::now(), ReplayPolicy::DisabledForCompatibility);
30
31    let sp = Saml::sp(
32        SpConfig::builder(EntityId::try_new("https://sp.example.com/metadata")?)
33            .acs_endpoint(AcsEndpoint::post("https://sp.example.com/acs")?)
34            .credentials(credentials())
35            .validation(SpValidationPolicy::strict())
36            .build()?,
37    )?;
38    let idp = Saml::idp(
39        IdpConfig::builder(EntityId::try_new("https://idp.example.com/metadata")?)
40            .sso_endpoint(SsoEndpoint::post("https://idp.example.com/sso")?)
41            .credentials(credentials())
42            .validation(IdpValidationPolicy::strict())
43            .build()?,
44    )?;
45
46    let sp_descriptor = SpDescriptor::from_metadata_xml_for(
47        EntityId::try_new("https://sp.example.com/metadata")?,
48        sp.metadata_xml(),
49        MetadataTrustPolicy::UnsignedForCompatibility,
50    )?;
51    let idp_descriptor = IdpDescriptor::from_metadata_xml_for(
52        EntityId::try_new("https://idp.example.com/metadata")?,
53        idp.metadata_xml(),
54        MetadataTrustPolicy::UnsignedForCompatibility,
55    )?;
56
57    let relay_state = RelayStateParam::try_from_option(Some("demo-state".to_string()))?;
58    let started = sp.start_sso(&idp_descriptor, StartSso::post().relay_state(relay_state))?;
59    println!(
60        "SP  -> AuthnRequest id = {}",
61        started.pending.request_id().as_str()
62    );
63
64    let request = idp.receive_sso(
65        &sp_descriptor,
66        BrowserInput::<AuthnRequest>::post(started.outbound.post_form()?.fields().to_vec()),
67        validation(),
68    )?;
69    println!(
70        "IdP <- request issuer  = {}",
71        request.message().issuer().as_str()
72    );
73
74    let response = idp.respond_sso(
75        &sp_descriptor,
76        &request,
77        Subject::new(NameId::new("alice@example.com", None), Vec::new()),
78        RespondSso::post(),
79    )?;
80
81    let session = sp.finish_sso(
82        &idp_descriptor,
83        &started.pending,
84        BrowserInput::<SsoResponse>::post(response.post_form()?.fields().to_vec()),
85        validation(),
86    );
87    let session = session?;
88    println!("SP  <- authenticated   = {}", session.name_id().value());
89    Ok(())
90}
91
92#[cfg(not(any(
93    feature = "crypto-rustcrypto",
94    feature = "crypto-aws-lc",
95    feature = "crypto-fips"
96)))]
97fn main() {
98    eprintln!("Enable a crypto provider feature (`crypto-bergshamra` is on by default).");
99}