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