crystal-saml

Crystal port of ruby-saml. SAML v2 toolkit for adding SSO to Crystal applications.

= Crystal SAML :toc: macro :toclevels: 3

SAML v2 toolkit for Crystal. Add Single Sign-On (SSO) to your Crystal applications.

Port of the https://github.com/SAML-Toolkits/ruby-saml[ruby-saml] gem (v1.18.1). Works with any Crystal web framework (Kemal, Lucky, Marten, etc.).

toc::[]

== Installation

Add the dependency to your shard.yml:

[source,yaml]

dependencies: crystal-saml: github: aloli-crystal/crystal-saml version: ~> 1.18.1

Run shards install.

== Quick Start

[source,crystal]

require "kemal" require "crystal-saml"

settings = CrystalSaml::Settings.new settings.idp_sso_service_url = "https://idp.example.com/sso" settings.idp_cert = File.read("idp.pem") settings.sp_entity_id = "https://myapp.com" settings.assertion_consumer_service_url = "https://myapp.com/saml/callback"

get "/saml/login" do |env| request = CrystalSaml::AuthRequest.new(settings) env.redirect request.redirect_url end

post "/saml/callback" do |env| response = CrystalSaml::Response.new(settings, env.params.body["SAMLResponse"]) if response.valid? user_email = response.name_id attributes = response.attributes env.session.string("user", user_email) env.redirect "/" else halt env, status_code: 401, response: "SAML Error: #{response.errors.join(", ")}" end end

get "/saml/metadata" do |env| env.response.content_type = "application/xml" settings.sp_metadata end

== Configuration

=== Identity Provider (IdP) Settings

[cols="1,3"] |=== | Property | Description

| idp_entity_id | The EntityID of the IdP | idp_sso_service_url | IdP login URL (SSO endpoint) | idp_slo_service_url | IdP logout URL (SLO endpoint, optional) | idp_cert | IdP X.509 certificate in PEM format | idp_cert_fingerprint | Alternative to idp_cert: certificate fingerprint | idp_cert_fingerprint_algorithm | Fingerprint algorithm: sha1, sha256 (default: sha256) |===

=== Service Provider (SP) Settings

[cols="1,3"] |=== | Property | Description

| sp_entity_id | Your application's EntityID (usually your app URL) | assertion_consumer_service_url | Your SAML callback URL (receives the SAML Response) | single_logout_service_url | Your logout callback URL (optional) | name_identifier_format | NameID format (default: email) |===

=== Security Settings

[cols="1,3"] |=== | Property | Description

| security_authn_requests_signed | Sign AuthnRequests (default: false) | security_want_assertions_signed | Require signed assertions (default: true) | security_signature_method | Signature algorithm (default: RSA-SHA256) | certificate | SP certificate for signing (PEM format) | private_key | SP private key for signing (PEM format) |===

== Features

=== AuthnRequest (Login)

Build a SAML authentication request and redirect the user to the IdP:

[source,crystal]

request = CrystalSaml::AuthRequest.new(settings)

HTTP-Redirect binding (GET)

redirect_url = request.redirect_url(relay_state: "/dashboard")

HTTP-POST binding

html_form = request.post_form(relay_state: "/dashboard")

=== Response Parsing (Callback)

Parse and validate the SAML Response from the IdP:

[source,crystal]

response = CrystalSaml::Response.new(settings, params["SAMLResponse"])

if response.valid? name_id = response.name_id # User identifier (email) attrs = response.attributes # Hash(String, Array(String)) session = response.session_index # Session identifier end

=== Single Logout (SLO)

Initiate logout:

[source,crystal]

logout = CrystalSaml::LogoutRequest.new(settings, user_name_id, user_session_index) redirect_url = logout.redirect_url

Handle logout response:

[source,crystal]

logout_response = CrystalSaml::LogoutResponse.new(settings, params["SAMLResponse"]) if logout_response.valid?

User is logged out

end

=== SP Metadata

Generate metadata XML for IdP registration:

[source,crystal]

metadata_xml = settings.sp_metadata

=== CLI Tool

[source,bash]

Generate SP metadata

crystal-saml metadata --config settings.yml

Decode a Base64 SAML response (debugging)

crystal-saml decode "PHNhbWxwOl..."

== Compatibility

This library is framework-agnostic. It works with:

  • Kemal -- lightweight web framework
  • Lucky -- full-featured web framework
  • Marten -- Django-inspired web framework
  • Any Crystal HTTP server -- uses only Crystal stdlib

No external dependencies required.

== Upstream

This is a port of https://github.com/SAML-Toolkits/ruby-saml[ruby-saml] v1.18.1. The API has been adapted to Crystal conventions while maintaining feature parity for core functionality.

== License

MIT License. See link:LICENSE[LICENSE].

Repository

crystal-saml

Owner
Statistic
  • 0
  • 0
  • 0
  • 1
  • 0
  • 1 day ago
  • April 13, 2026
License

MIT License

Links
Synced at

Mon, 13 Apr 2026 17:12:55 GMT

Languages