15 releases

0.0.15 Apr 4, 2024
0.0.14 Nov 7, 2023
0.0.13 Oct 14, 2023
0.0.12 May 11, 2023
0.0.7 Mar 14, 2020

#28 in Authentication

Download history 152/week @ 2024-01-03 184/week @ 2024-01-10 142/week @ 2024-01-17 1623/week @ 2024-01-24 2647/week @ 2024-01-31 470/week @ 2024-02-07 194/week @ 2024-02-14 362/week @ 2024-02-21 210/week @ 2024-02-28 185/week @ 2024-03-06 287/week @ 2024-03-13 453/week @ 2024-03-20 154/week @ 2024-03-27 2544/week @ 2024-04-03 2193/week @ 2024-04-10 1723/week @ 2024-04-17

6,683 downloads per month

MIT license

230KB
5.5K SLoC

Samael

Crates.io MIT licensed

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 aren't supported yet
  • 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

Build instructions

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.

  1. Install nix
  2. Install direnv and cachix
    # Add ~/.nix-profile/bin to your path first
    nix profile install nixpkgs/release-23.11#direnv
    nix profile install nixpkgs/release-23.11#cachix
    
  3. Run cachix use nix-community to enable a binary cache for the rust toolchain (otherwise you'll build the rust toolchain from scratch)
  4. cd into this repo and run direnv allow
  5. Install the direnv VS Code extension

Building the library

Just run nix build

Entering a dev environment

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 xmlsec

How do I use this library?

You'll need these dependencies for this example

[dependencies]
tokio = { version = "1.28.1", features = ["full"] }
samael = { version = "0.0.12", 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]
async fn main() -> 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_xml()?;

    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>| {
            if let Some(encoded_resp) = s.get("SAMLResponse") {
                let t = sp
                    .parse_base64_response(encoded_resp, Some(&["a_possible_request_id"]))
                    .unwrap();
                return format!("{:?}", 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(())
}

Dependencies

~7–11MB
~232K SLoC