Skip to content
J. Scott Johnson edited this page Oct 28, 2016 · 12 revisions

Welcome to the saml_idp wiki!

I'm a relative SAML Newbie and I'm attempting to leverage saml_idp to build a ruby based idp where my users can login via devise and then other web sites they are trying to use can authenticate against that. I'm attempting to use this wiki to document the process for other people. First of all I'd like to say thank you to Jon Phenow who has done wonderful work extending the original ruby-saml-idp with all of the back end work necessary to make this possible.

I would comment that his statement "it should be straight forward ..." is fairly inaccurate. Just the need to mess with x.509 certs means that its not straight forward. Certs are never simple.

Here's what I have found to be the process for building an IdP:

Step 0 - Install the Gem

Add this:

gem 'saml_idp'

to Gemfile and then bundle install

Step 1 - Add To Your Routes file

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

Step 2 - Create a Controller and It is inherit not Include for Your Controller

Change the include mentioned to the readme to an inherit - See: https://github.com/sportngin/saml_idp/issues/57

class SamlIdpController < SamlIdp::IdpController

  def idp_authenticate(email, password) # not using params intentionally
    user = User.by_email(email).first
    user && user.valid_password?(password) ? user : nil
  end
  private :idp_authenticate

  def idp_make_saml_response(found_user) # not using params intentionally
    # NOTE encryption is optional
    encode_response found_user, encryption: {
      cert: saml_request.service_provider.cert,
      block_encryption: 'aes256-cbc',
      key_transport: 'rsa-oaep-mgf1p'
    }
  end
  private :idp_make_saml_response

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

Note: You are going to need to define a class method called by_email on your user model for this to work.

Step 3 - Test the Controller in Development Mode

Goto:

http://localhost:YOUR_PORT/saml/metadata 

And you should see some big XML response. If you do then this is actually working.

Step 4 - Note This Only Works in Production and Development

Don't try and run this on a staging server -- it doesn't work and you will get this error: undefined method `make_response!' for SamlIdpController:Class

I don't understand why but when I moved my staging server, on a hunch, over to production it worked perfectly. Welcome to bizarroville; crap happens.

Step 5 - Add an initializer

In the directory config/initializers you want to create saml.rb with the contents that Jon describes on the home page. Don't screw up and put it in config as I did or you'll then spend quite a bit of time scratching your head going "why aren't my changes showing up". Initializers are loaded ONLY at startup so shut down your script/server and then restart it. And go back to the /saml/metadata url and you'll likely get this error:

OpenSSL::PKey::RSAError in SamlIdpController#show
Neither PUB key nor PRIV key: nested asn1 error

Despite being an error, that's actually a huge win since it indicates progress.

The solution is obvious -- in the big initializer that you added you need to customize it with the certificate and the secret key. Here's what to do:

  1. Find the gem's source code with bundle show saml_idp
  2. Browse the source code for the gem and open the file default.rb; it is in there, just look at the file tree or use a fuzzy finder function.
  3. Copy and paste the cert and secret key into the initializer.
  4. Edit the initializer and adjust things like your contact info, url structure, etc.
  5. Save.
  6. Close.
  7. Restart your server and then goto the /saml/metadata url and you should see your data (if you customized it) appearing in the xml response.

NOTE: THIS IS ABSOLUTELY INSECURE. UNDERSTAND THAT AND DON'T DEPLOY WITH A CERT WHICH YOU PULLED OFF OF GITHUB. YOU NEED TO REGENERATE THE CERT AND I HAVEN'T WRITTEN THAT DOCUMENTATION YET.

Step 6. You May Need to Fork This Gem Yourself

So the way that SAML works is like this:

Definitions

  • principal - the user
  • service provider (SP) - the site that the user wants access to
  • identity provider (IdP) - the site that does the authentication

Flow

  • A principal goes to a site somewhere on the Internet or an Intranet and wants access to a protected a resource. This site is the SP.
  • A mechanism of authentication is used -- typically this would be username / password but it could be multi-factor auth
  • The SP requests and obtains an identity assertion from the identity provider or IdP. On the basis of this assertion, the service provider can make an access control decision – in other words it can decide whether to perform some service for the connected principal. Before delivering the identity assertion to the SP, the IdP may request some information from the principal – such as a user name and password – in order to authenticate the principal.

So if you think about this flow then one thing becomes apparent -- somewhere there is a login and password form in which the user enters credentials. And that login and password comes from the IdP server. Here's an example of one which was generated by all this:

<form action="/saml/auth" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="&#x2713;" /><input type="hidden" name="authenticity_token" value="HzeTqa5McbYlqQDnzTJed7qm5N02g9L3OQUMW+CGAdv7/4G878v5LIqFPYKbt+u1o+gQjgxeDLELb6/LTTIK0A==" />
  <input type="hidden" name="SAMLRequest" id="SAMLRequest" value="nZFNa8MwDIb/SvDdi/OxrogmEFoGhW6Mduthl+G5Cg04dmYp+/j3S1IG3Q497Co9et9X0oJ0azuoej66Lb71SBxVRBi48W7pHfUthh2G98bg03ZTiCNzB3FsvdH26InjUSA2J1JE61UhXnKdJYhayZs6O0iVZLmcp/WrTE2tEFO8nhszoEQ9rh2xdlyIVCUzmSiZzh+TDNQMcvUsoj0GGoIM7Sslos/WOoIpcSH64MBragicbpGADeyquw0MJHTBszfeinIx0jBZhbP5y+P65wCi/LsuZCpRi/hM9WTRwf0gs149eNuYr6iy1n8sA2rGQnDoh8vc+tBqvmw8VpqDrCcUOGhHDTr+z+ZxeUr5+7flNw==" />
  <input type="hidden" name="RelayState" id="RelayState" value="" />

  <p>
    <label for="email">Email</label>
    <input type="email" name="email" id="email" value="" autocapitalize="off" autocorrect="off" autofocus="autofocus" spellcheck="false" size="30" class="email_pwd txt" />
  </p>

  <p>
    <label for="password">Password</label>
    <input type="password" name="password" id="password" value="" autocapitalize="off" autocorrect="off" spellcheck="false" size="30" class="email_pwd txt" />
  </p>

  <p>
    <input type="submit" name="commit" value="Sign in" class="button big blueish" data-disable-with="Sign in" />
  </p>
</form>

There doesn't seem to be any mechanism in saml_idp to pass in an authentication template so if you want to change this template you either need to update the gem and make a pull request or fork the gem and make your own changes to the html.

If you opt to fork it and make your own changes then you need to change the files in app/views/saml_idp/idp and there the file you likely need to change is new.html.erb

Clone this wiki locally