#![allow(dead_code)]
use armature_auth::saml::{IdpMetadata, SamlConfig, SamlProvider, SamlServiceProvider};
#[tokio::main]
async fn main() {
println!("🔐 Armature SAML 2.0 Example");
println!("============================\n");
println!("1. Configuring SAML Service Provider");
let config = SamlConfig::new(
"https://myapp.example.com/saml/metadata".to_string(),
"https://myapp.example.com/saml/acs".to_string(),
IdpMetadata::Xml(SAMPLE_IDP_METADATA.to_string()),
)
.with_sls_url("https://myapp.example.com/saml/sls".to_string())
.allow_unsigned(false)
.with_required_attributes(vec!["email".to_string(), "displayName".to_string()]);
println!(" Entity ID: {}", config.entity_id);
println!(" ACS URL: {}", config.acs_url);
println!(" SLS URL: {}", config.sls_url.as_ref().unwrap());
println!(" Unsigned allowed: {}", config.allow_unsigned_assertions);
println!();
println!("2. Creating SAML Provider");
let provider = SamlServiceProvider::new("okta".to_string(), config);
match provider {
Ok(provider) => {
println!(" ✓ Provider: {}", provider.name());
println!(" ✓ SAML SP created successfully\n");
println!("3. Generating SAML Authentication Request");
match provider.create_auth_request() {
Ok(auth_req) => {
println!(" ✓ SAMLRequest generated");
println!(" ✓ Redirect URL: {}", auth_req.redirect_url);
println!(
" ✓ Relay State: {}",
auth_req.relay_state.unwrap_or_default()
);
println!(
" ✓ SAML Request (first 100 chars): {}...",
&auth_req.saml_request[..100.min(auth_req.saml_request.len())]
);
println!();
}
Err(e) => println!(" ✗ Error: {}\n", e),
}
println!("4. Generating SP Metadata");
match provider.get_metadata() {
Ok(metadata) => {
println!(" ✓ Metadata XML generated");
let lines: Vec<&str> = metadata.lines().collect();
println!(" ✓ Metadata (first 5 lines):");
for (i, line) in lines.iter().take(5).enumerate() {
println!(" {}: {}", i + 1, line);
}
println!();
}
Err(e) => println!(" ✗ Error: {}\n", e),
}
println!("5. SAML 2.0 Flow:");
println!(" 1. User clicks 'Login with SSO'");
println!(" 2. SP generates AuthnRequest");
println!(" 3. User redirected to IdP");
println!(" 4. User authenticates at IdP");
println!(" 5. IdP posts SAML Response to SP ACS");
println!(" 6. SP validates assertion");
println!(" 7. User logged in");
println!();
}
Err(e) => println!(" ✗ Error creating provider: {}\n", e),
}
println!("✅ SAML 2.0 Features:");
println!(" ✓ SP configuration");
println!(" ✓ AuthnRequest generation");
println!(" ✓ Assertion validation");
println!(" ✓ Metadata generation");
println!(" ✓ Enterprise SSO support");
println!();
println!("📖 Common SAML Providers:");
println!(" - Okta");
println!(" - Microsoft Entra (Azure AD)");
println!(" - Auth0");
println!(" - OneLogin");
println!(" - Ping Identity");
println!();
println!("🔧 Setup Steps:");
println!(" 1. Register SP in IdP");
println!(" 2. Download IdP metadata");
println!(" 3. Configure SP with IdP metadata");
println!(" 4. Upload SP metadata to IdP");
println!(" 5. Test authentication");
println!();
println!("📄 For complete integration, see:");
println!(" - docs/auth-guide.md");
println!(" - docs/saml-guide.md (coming soon)");
}
const SAMPLE_IDP_METADATA: &str = r#"<?xml version="1.0"?>
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://idp.example.com">
<IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="https://idp.example.com/sso"/>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="https://idp.example.com/sso"/>
</IDPSSODescriptor>
</EntityDescriptor>"#;