SAML 2.0 authentication provider for MultiAuth. This shard provides a Crystal implementation of SAML authentication, ported from Ruby's omniauth-saml.
- SAML 2.0 Authentication (SP-initiated SSO)
- Support for signed requests and assertions
- Configurable attribute mapping
- Compatible with standard SAML Identity Providers
- Built on crystal-saml
-
Add the dependency to your
shard.yml
:dependencies: multi_auth_saml: github: spider-gazelle/multi_auth_saml
-
Run
shards install
require "multi_auth"
require "multi_auth_saml"
# Register a SAML provider using factory-style initialization
MultiAuth.config("saml") do |redirect_uri, _db_id|
MultiAuth::Provider::SAML.new(
# update this if database driven
provider_name: "saml",
# Callback URL where SAML response will be sent
redirect_uri: redirect_uri,
# Identity Provider settings
idp_sso_url: "https://idp.example.com/sso",
idp_cert_fingerprint: "AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD",
# Service Provider settings
sp_entity_id: "https://myapp.com/saml/metadata"
)
end
MultiAuth.config("saml") do |redirect_uri, db_id|
MultiAuth::Provider::SAML.new(
# where the config comes from the database
provider_name: "saml-#{db_id}",
redirect_uri: redirect_uri,
# IdP Configuration
idp_sso_url: "https://idp.example.com/sso",
idp_cert: File.read("idp_certificate.pem"), # Or use idp_cert_fingerprint
idp_entity_id: "https://idp.example.com/metadata",
idp_slo_url: "https://idp.example.com/slo",
# SP Configuration
sp_entity_id: "https://myapp.com/saml/metadata",
sp_certificate: File.read("sp_certificate.pem"), # For signing requests
sp_private_key: File.read("sp_private_key.pem"),
# Attribute Configuration
uid_attribute: "email", # Which attribute to use as unique user ID
attribute_statements: Hash(Symbol, Array(String)){
:email => ["mail", "emailAddress"],
:name => ["displayName", "cn"],
:first_name => ["givenName"],
:last_name => ["sn", "surname"],
},
# Security Settings
authn_requests_signed: true,
want_assertions_signed: true,
digest_method: Saml::XMLSecurity::SHA256,
signature_method: Saml::XMLSecurity::RSA_SHA256,
# Optional Behavior
name_identifier_format: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
force_authn: false,
passive: false
)
end
Works like any other multi_auth provider. For details see the Generic OAuth2 documentation
# Generate authorization URL (SAML AuthnRequest)
provider = MultiAuth.make("saml", redirect_uri)
auth_url = provider.authorize_uri(state: "optional-relay-state")
# Redirect user to auth_url...
# Handle callback (SAML Response)
params = {"SAMLResponse" => request.params["SAMLResponse"]}
user = provider.user(params)
# Access user information
puts user.uid # Unique user identifier
puts user.email # User's email
puts user.name # User's full name
puts user.first_name # User's first name
puts user.last_name # User's last name
# If you've used AD-FS or registered your app in Entra, add Graph permissions and consent.
# RFC 7522: OAuth 2.0 token endpoint for SAML assertion exchange
token = user.access_token
Generate SP metadata XML to share with your Identity Provider:
provider = MultiAuth.make("saml", redirect_uri)
metadata_xml = provider.metadata
# Serve metadata at a route like GET /auth/saml/metadata
# The IdP will use this to configure trust with your SP
The metadata includes:
- Entity ID
- Assertion Consumer Service URL
- NameID formats supported
- Security settings (signing requirements)
- SP certificate (if configured)
redirect_uri
- The callback URL where SAML response will be sentidp_sso_url
- Identity Provider's Single Sign-On URLsp_entity_id
- Service Provider's entity ID (usually your app's URL)
idp_cert
- PEM-encoded certificate from IdPidp_cert_fingerprint
- Certificate fingerprint (SHA1/SHA256)
idp_entity_id
- IdP's entity IDidp_slo_url
- IdP's Single Logout URL (for SLO support)sp_certificate
- SP certificate for signing requestssp_private_key
- SP private key for signinguid_attribute
- Attribute to use as unique user ID (defaults to NameID)attribute_statements
- Hash mapping user fields to SAML attributesauthn_requests_signed
- Sign authentication requests (default: false)want_assertions_signed
- Require signed assertions (default: false)want_signature_validated
- All signatures are validated (default: false)name_identifier_format
- NameID format to requestforce_authn
- Force re-authentication (default: false)passive
- Passive authentication (default: false)
# Install dependencies
shards install
# Run tests
crystal spec
# Run specs with coverage
crystal spec --error-trace
- Fork it (https://github.com/spider-gazelle/multi_auth_saml/fork)
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request
- Stephen von Takach - creator and maintainer