Project

saml2

0.06
Low commit activity in last 3 years
There's a lot of open issues
A long-lived project that still receives updates
The saml2 library is yet another SAML library for Ruby, with an emphasis on _not_ re-implementing XML, especially XML Security, _not_ parsing via Regex or generating XML by string concatenation, _not_ serializing/re-parsing multiple times just to get it into the correct format to sign or validate.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 12.0
~> 3.5
~> 11.0

Runtime

>= 3.2, < 7.2
>= 1.5.8, < 2.0
 Project Readme

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.