This is a SAML2 library for rust.
This is a work in progress. Pull Requests are welcome.
Current Features:
- Serializing and Deserializing SAML messages
- IDP-initiated SSO
- SP-initiated SSO Redirect-POST binding
- Helpers for validating SAML assertions
- Encrypted assertions only support:
- key info:
http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1phttp://www.w3.org/2001/04/xmlenc#rsa-1_5
- value info:
http://www.w3.org/2001/04/xmlenc#aes128-cbchttp://www.w3.org/2009/xmlenc11#aes128-gcm
- key info:
- Encrypted assertions only support:
- Verify SAMLRequest (AuthnRequest) message signatures
- Create signed SAMLResponse (Response) messages
The "xmlsec" feature flag adds basic support for verifying and signing SAML messages. We're using a modified copy of rust-xmlsec library (bindings to xmlsec1 library).
If you want to use the "xmlsec" feature, you'll need to install the following C libs:
- libiconv
- libtool
- libxml2
- libxslt
- libclang
- openssl
- pkg-config
- xmlsec1
We use nix to faciliate reproducible builds of samael.
It will ensure you have the required libraries installed in a way that won't cause any issues with the rest of your system.
If you want to take advantage of this, you'll need to put in a little bit of work.
- Install nix
- Install direnv and cachix
# Add ~/.nix-profile/bin to your path first nix profile install nixpkgs#direnv nix profile install nixpkgs#cachix - Run
cachix use nix-communityto enable a binary cache for the rust toolchain (otherwise you'll build the rust toolchain from scratch) cdinto this repo and rundirenv allowandnix-direnv-reload- Install the direnv VS Code extension
Just run nix build
If you followed the above instructions, just cd-ing into the directory will setup a reproducible dev environment,
but if you don't want to install direnv, then just run nix develop.
From their you can build as normal:
cargo build --features xmlsec
cargo test --features xmlsecYou'll need these dependencies for this example
[dependencies]
tokio = { version = "1.28.1", features = ["full"] }
samael = { version = "0.0.20", features = ["xmlsec"] }
warp = "0.3.5"reqwest = "0.11.18"openssl = "0.10.52"openssl-probe = "0.1.5"Here is some sample code using this library:
use samael::metadata::{ContactPerson,ContactType,EntityDescriptor};use samael::service_provider::ServiceProviderBuilder;use std::collections::HashMap;use std::fs;use warp::Filter;#[tokio::main]asyncfnmain() -> Result<(),Box<dyn std::error::Error>>{
openssl_probe::init_ssl_cert_env_vars();let resp = reqwest::get("https://samltest.id/saml/idp").await?
.text().await?;let idp_metadata:EntityDescriptor = samael::metadata::de::from_str(&resp)?;let pub_key = openssl::x509::X509::from_pem(&fs::read("./publickey.cer")?)?;let private_key = openssl::rsa::Rsa::private_key_from_pem(&fs::read("./privatekey.pem")?)?;let sp = ServiceProviderBuilder::default().entity_id("".to_string()).key(private_key).certificate(pub_key).allow_idp_initiated(true).contact_person(ContactPerson{sur_name:Some("Bob".to_string()),contact_type:Some(ContactType::Technical.value().to_string()),
..ContactPerson::default()}).idp_metadata(idp_metadata).acs_url("http://localhost:8080/saml/acs".to_string()).slo_url("http://localhost:8080/saml/slo".to_string()).build()?;let metadata = sp.metadata()?.to_string()?;let metadata_route = warp::get().and(warp::path("metadata")).map(move || metadata.clone());let acs_route = warp::post().and(warp::path("acs")).and(warp::body::form()).map(move |s:HashMap<String,String>| {ifletSome(encoded_resp) = s.get("SAMLResponse"){let t = sp
.parse_base64_response(encoded_resp,Some(&["a_possible_request_id"])).unwrap();returnformat!("{:?}", t);}format!("")});let saml_routes = warp::path("saml").and(acs_route.or(metadata_route));
warp::serve(saml_routes).run(([127,0,0,1],8080)).await;Ok(())}