crystal-saml
= 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].
crystal-saml
- 0
- 0
- 0
- 1
- 0
- 1 day ago
- April 13, 2026
MIT License
Mon, 13 Apr 2026 17:12:55 GMT