SAML Authentication in Rails

Siva Gollapalli
3 min readJul 15, 2022

Before diving into the authentication part and its implementation let’s clarify the basics of SAML. What is SAML? As per google here is the definition:

“SAML is an acronym used to describe the Security Assertion Markup Language (SAML). Its primary role in online security is that it enables you to access multiple web applications using one set of login credentials.”

It is an open standard for authentication. Using this we can establish authentication across multiple applications using a single service. The service which provides authentication is called an Identity provider and an application that provides service to users is called a Service provider. Both identity and service providers will exchange information in XML format. Before exchanging information let’s configure how to exchange.

For easy understanding, I have coded sample Identity provider and service provider. To provide Identity I am using saml_idp gem and to generate SAML request I am using saml-ruby gem. To establish communication first we need to generate certificate and private key to validate incoming SAML requests and generate SAML responses respectively. To generate a certificate/private key please do the following

openssl req -nodes -x509 -newkey rsa:2048 -keyout private-key.pem -out cert.pem -sha256 -days 365

This will generate an x509 certificate and a private key of length 2048 using the RSA encryption algorithm. This certificate is valid for up to 365 days.

certificate and private key generation using OpenSSL

Next, generate a certificate fingerprint for a certificate:

SamlIdp::Fingerprint.certificate_digest(x509_cert, :sha512)

After that configure the certificate and private as per saml_idp. For more understanding please check here.

Now generate SAML request using init action.

class SamlController < ApplicationController
skip_before_action :verify_authenticity_token, only: [:consume]
def init
request = OneLogin::RubySaml::Authrequest.new
redirect_to(request.create(saml_settings, email: "sivagollapalli88@gmail.com"))
end
def consume
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], settings: saml_settings)
# We validate the SAML Response and check if the user already exists in the system
if response.is_valid?
# authorize_success, log the user
session[:userid] = response.nameid
session[:attributes] = response.attributes
render plain: "Successfully authenticated...."
else
authorize_failure # This method shows an error message
# List of errors is available in response.errors array
end
end
def saml_settings
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
settings = idp_metadata_parser.parse_remote("http://localhost:3000/saml/metadata")
settings.assertion_consumer_service_url = "http://localhost:4000/saml/consume"
settings.sp_entity_id = "http://localhost:4000"
settings.private_key = File.read("#{Rails.root}/private-key.pem")
#settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
# Optional for most SAML IdPs
#settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
settings
end
end

Before generating a request it will pull metadata from the identity provider. Using that metadata will generate a SAML request. Upon request Identity provider validates the request using in build method validate_saml_request . Upon validation authentication Idp generates a response using the following configuration:

config.name_id.formats = {
email_address: -> (principal) { principal.email },
transient: -> (principal) { principal.id },
persistent: -> (p) { p.id },
}

For safe transmission, we can encode and encrypt the response as follows

@saml_response = encode_response user, encryption: {
cert: saml_request.service_provider.cert,
block_encryption: 'aes256-cbc',
key_transport: 'rsa-oaep-mgf1p'
}

Once a response is generated we submit that respective provider. In our case it is consume action. Once we got the response we will create a session in our service provider application and move ahead.

Hope this article is helpful.

Let me your suggestions or comments.

--

--