-
Notifications
You must be signed in to change notification settings - Fork 181
Rails_Integration
gem 'saml_idp'
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]
Following example is shows only shows that required methods you need to define in your controller. If you are Devise user please properly use Devise methods for authentication of your users.
Note: For Devise user please aware of that SAML request could be more that 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
Following sample views are used as SAML GET request and mimic POST request for your SAML controller Or if you want to handle GET request in your new action with your own login you don't need to use form.
Without 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 Devise user you following sample view will help to mimic GET request automatically as 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>
Most important view for SAML IdP is following auto submit form that submit SAML response to SAML SP via browser.
# 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_tag(saml_acs_url) do %>
<%= hidden_field_tag("SAMLResponse", @saml_response) %>
<%= hidden_field_tag("RelayState", params[:RelayState]) %>
<%= submit_tag "Submit" %>
<% end %>
</body>
</html>
-
Never use sample public and private keys from this gem. Private key used for secure your SAML request and response. If you use publicly published private key your IdP service become door without lock.
-
To implement security validation feature on your controller Most of common security validations are listed in OWASP SAML Security page. We would suggest to implement required one for your IdP. SAML Security Cheat Sheet Recommending to implement: "AuthnRequest(ID, SP)" from Validate Protocol Usage.
-
We highly recommended that store your private key in very secure place such as KMS.