Skip to content
SAML 2.0 Library for Ruby
Ruby Other
  1. Ruby 99.5%
  2. Other 0.5%
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.

README.md

Ruby SAML2 Library

Build Status Code Climate Gem Version

About

This library is for building a custom SAML 2.0 SP or IdP with minimal headache. A simple example of a Rails controller that just passes on an already authenticated user to a single SP:

require 'saml2'

class SamlIdpController < ApplicationController
  def create
    authn_request, @relay_state = SAML2::Bindings::HTTPRedirect.decode(params[:SAMLRequest])
    unless authn_request.is_a?(SAML2::AuthnRequest) &&
      authn_request.valid_schema? &&
      authn_request.valid_interoperable_profile? &&
      authn_request.resolve(self.class.service_provider)

      flash[:error] = "Invalid login request"
      return redirect_to @current_user ? root_url : login_url
    end

    if @current_user
      response = SAML2::Response.respond_to(authn_request,
          NameID.new(self.class.entity_id),
          self.class.idp_name_id(@current_user, sp))
      response.sign(self.class.x509_certificate, self.class.private_key)

      @saml_response = Base64.encode64(response.to_xml)
      @saml_acs_url = authn_request.assertion_consumer_service.location
      render template: "saml2/http_post", layout: false
    else
      redirect_to login_url
    end
  end

  protected
  def self.idp_name_id(user)
    SAML2::NameID.new(user.uuid, SAML2::NameID::Format::PERSISTENT)
  end

  def self.saml_config
    @config ||= YAML.load(File.read('saml.yml'))
  end

  def self.service_provider
    @sp ||= SAML2::Entity.parse(File.read(saml_config[:service_provider])).roles.first
  end

  def self.entity_id
    saml_config[:entity_id]
  end

  def self.x509_certificate
    @cert ||= File.read(saml_config[:encryption][:certificate])
  end

  def self.private_key
    @key ||= File.read(saml_config[:encryption][:private_key])
  end

  def self.signature_algorithm
    saml_config[:encryption][:algorithm]
  end
end

An example of a basic SP (obtain idp_metadata.xml from your IdP; craft your own sp_metadata.xml):

require 'saml2'

class SamlSpController < ApplicationController
  class << self
    def idp_metadata
      @idp_metadata ||= SAML2::Entity.parse(Rails.root.join('config/saml/idp_metadata.xml'))
    end

    def sp_metadata
      @sp_metadata ||= SAML2::Entity.parse(Rails.root.join('config/saml/sp_metadata.xml'))
    end
  end

  def new
    authn_request = self.class.sp_metadata.initiate_authn_request(self.class.idp_metadata)
    redirect_to SAML2::Bindings::HTTPRedirect.encode(authn_request)
  end

  def create
    response, _relay_state = SAML2::Bindings::HTTP_POST.decode(request.request_parameters)
    unless self.class.sp_metadata.valid_response?(response, self.class.idp_metadata)
      logger.error("Failed to validate SAML response: #{response.errors}")
      raise ActionController::RoutingError.new('Not Found')
    end

    reset_session
    session[:username] = response.assertions.first.subject.name_id.id
    logger.info("Logged in as #{session[:username]}")

    redirect_to root_url
  end

  def metadata
    render xml: self.class.sp_metadata.to_xml
  end
end

And then in your routes.rb:

  get 'login' => 'saml_sp#new'
  post 'login' => 'saml_sp#create'
  get 'SAML2' => 'saml_sp#metadata'

Copyright

Copyright (c) 2015-present Instructure, Inc. See LICENSE for details.

You can’t perform that action at this time.