Skip to content

Rails_Integration

zogoo edited this page Feb 2, 2026 · 4 revisions

How to integrate with a Rails app

Add gem into your Gemfile

  gem 'saml_idp'

Add required routes for your route file

    get '/saml/metadata' => 'saml_idp#show'
    get '/saml/auth' => 'saml_idp#new'
    post '/saml/auth' => 'saml_idp#create'
    match '/saml/logout' => 'saml_idp#logout', via: [:get, :post, :delete]

Create your new controller for your SAML IdP feature

The following example only shows the required methods you need to define in your controller. If you are a Devise user, please properly use Devise methods for the authentication of your users.

Note: For Devise user, please be aware that the SAML request could be more than 4Kb if you are using cookie for your session storage. You might want to use Redis for your session storage or override Devise function store_location_for to store SAML request to different places.

  class SamlIdpController < ApplicationController
    include SamlIdp::Controller
    
    protect_from_forgery

    before_action :validate_saml_request, only: [:new, :create, :logout]

    def new
      render template: "saml_idp/idp/new"
    end

    def show
      render xml: SamlIdp.metadata.signed
    end

    def create
      unless params[:email].blank? && params[:password].blank?
        person = idp_authenticate(params[:email], params[:password])
        if person.nil?
          @saml_idp_fail_msg = "Incorrect email or password."
        else
          @saml_response = idp_make_saml_response(person)
          render :template => "saml_idp/idp/saml_post", :layout => false
          return
        end
      end
      render :template => "saml_idp/idp/new"
    end

    def logout
      idp_logout
      @saml_response = idp_make_saml_response(nil)
      render :template => "saml_idp/idp/saml_post", :layout => false
    end

    def idp_logout
      user = User.by_email(saml_request.name_id)
      user.logout
    end
    private :idp_logout

    def idp_authenticate(email, password)
      user = User.by_email(email).first
      user && user.valid_password?(password) ? user : nil
    end
    protected :idp_authenticate

    def idp_make_saml_response(person)
      # NOTE encryption is optional
      encode_response person, encryption: {
        cert: saml_request.service_provider.cert,
        block_encryption: 'aes256-cbc',
        key_transport: 'rsa-oaep-mgf1p'
      }
    end
    protected :idp_make_saml_response
  end

Create your view for GET request (SAML SP-initiated request)

The following sample views are used as a SAML GET request and mimic a POST request for your SAML controller Or if you want to handle a GET request in your new action with your own login, you don't need to use a form.

Without the Devise gem, you probably need to authenticate your user with your own login form.

# saml_idp/idp/new
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  </head>
  <body>
      <% if @saml_idp_fail_msg %>
        <div id="saml_idp_fail_msg" class="flash error"><%= @saml_idp_fail_msg %></div>
      <% end %>
      <%= form_tag do %>
        <%= hidden_field_tag("SAMLRequest", params[:SAMLRequest]) %>
        <%= hidden_field_tag("RelayState", params[:RelayState]) %>
        <p>
          <%= label_tag :email %>
          <%= email_field_tag :email, params[:email], :autocapitalize => "off", :autocorrect => "off", :autofocus => "autofocus", :spellcheck => "false", :size => 30, :class => "email_pwd txt" %>
        </p>
        <p>
          <%= label_tag :password %>
          <%= password_field_tag :password, params[:password], :autocapitalize => "off", :autocorrect => "off", :spellcheck => "false", :size => 30, :class => "email_pwd txt" %>
        </p>
        <p>
          <%= submit_tag "Sign in", :class => "button big blueish" %>
        </p>
      <% end %>
  </body>
</html>

If you are a Devise user you following sample view will help to mimic a GET request automatically as a POST request.

# saml_idp/idp/new
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  </head>
  <body onload="document.forms[0].submit();" style="visibility:hidden;">
    <%= form_tag do %>
      <%= hidden_field_tag("SAMLRequest", params[:SAMLRequest]) %>
      <%= hidden_field_tag("RelayState", params[:RelayState]) %>
    <% end %>
  </body>
</html>

Create view for POST request (SAML Response)

The most important view for SAML IdP is the following auto submit form that submit SAML response to the SAML SP via browser. This is a minimal example of auto submit form, you can override the form in your Rails app to creating same file.

# saml_idp/idp/saml_post
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  </head>
  <body onload="document.forms[0].submit();" style="visibility:hidden;">
    <form action="<%= saml_acs_url %>" method="post">
      <%= hidden_field_tag("SAMLResponse", @saml_response) %>
      <%= hidden_field_tag("RelayState", params[:RelayState]) %>
      <%= submit_tag "Submit" %>
    </form>
  </body>
</html>

Security suggestion

  1. Never use sample public and private keys from this gem. Private key used to secure your SAML request and response. If you use a publicly published private key, your IdP service become a door without a lock.

  2. To implement the security validation feature on your controller The most common security validations are listed in OWASP SAML Security page. We would suggest implementing the required one for your IdP. SAML Security Cheat Sheet Recommending to implement: "AuthnRequest(ID, SP)" from Validate Protocol Usage.

  3. We highly recommend that you store your private key in a very secure place, such as KMS.