Open TJM opened 9 years ago
I love this idea! Even just a Devise example would be a great start for a ton of people I would think.
If you want to submit a PR that adds this directory and outlines how to do this within one file or a set of files, that'd be great.
I am thinking a directory of markdown docs, sort of like above, provide a fairly simple way of sprinkling documentation and code together into a single file. I might add Prerequisites or something? The thing that hurt me the worst (besides being a fairly newb ruby guy) was the initializer. I had to go through a lot of stack traces to figure out what was going wrong. It kept trying to call ".persistent" against my User object, and I couldn't figure out why :). Anyhow, I will see if I can carve off another half hour or something to polish this up.
That'd be Great, thanks a bunch for anything you can contribute!
Would also be nice to provide examples on how to test this with rspec/minitest. Running in circles trying to write tests for the IDP controller.
Did any of these examples ever materialise? I'm finding it difficult to understand the configuration requirements.
At the moment my service provider is never trying to get fresh metadata, and there is no persisted metadata. Validation of the request then fails due to missing acs_url. I've added a base, defined the service provider name and metadata URL, it just never calls refresh metadata.
Appreciate any help or examples!
-Matt
Also note that the Devise Example above only works if using the SAML Http redirect protocol. If using the HTTP post method this does not work, as you will never be redirected back to the sp.
@TJM : Did you solve the ".persistent" undefined method for the User model object in Devise?
@rayson1223 - I will admit that I am the "IT" guy, and I was just part of the project to help with the SAML stuff. I know "it works" for us, but use it at your own risk. The only place I see "persistent" in our initializer is:
config.name_id.formats =
{ # All 2.0
email_address: -> (principal) { principal.email },
transient: -> (principal) { principal.id },
persistent: -> (p) { p.id },
}
There is also some stuff further down about metadata, but I think its unrelated.
I've found an issue with this type of Devise usage. When you use:
before_action :authenticate_user!
To redirect incoming SAML auth requests to Devise's session controllers, you will easily exceed the cookie size limit if the SP is sending a signed authn request. For example, I'm using devise + omniauth-saml on the SP side with the following rather ordinary settings (Gitlab uses similar settings):
security: {
authn_requests_signed: true,
logout_requests_signed: true,
logout_responses_signed: true,
want_assertions_signed: true,
metadata_signed: true,
embed_sign: true,
signature_method: XMLSecurity::Document::RSA_SHA256,
digest_method: XMLSecurity::Document::SHA256,
}
If authn_requests_signed
is enabled, then before redirecting to Devise's sign in page, the URL containing the SAMLRequest parameter will fail to be stored in the session for use as return URL. This will be too large to be persisted in a session cookie jar.
Since saml_idp required signed logout requests to support SP-initated SLO, I've found that you have to at least exclude the logout action from authentication:
before_action :authenticate_user!, except: %i[logout]
I've not yet found a solution for enabling signed authn requests apart from switching to another type of session storage on the IdP.
I assume the ideal thing would be to redirect around Devise's session controllers while always persisting the SAMLRequest in the querystring, then after logging in or signing up getting redirected back (this time with a principal yielded by Devise when calling current_*
) everything would work. I don't know my way around Devise/Warden enough to know whether this is possible. I'm going to do some more digging.
@vipera were you able to figure anything out. I've got the same issue I believe.
@JohnKacz I have currently set authn_requests_signed
to false in the SP configuration. This "fixes" the issue by shortening the SAMLRequest payload. My IdP is currently used only by internally developed SP applications, so I'm permitting unsigned requests from those sources.
Alternatively, you could look into using Memcached or Redis as the session store within the saml_idp application. Then you should not run into this problem as you can store more data in the session store. I understand that CookieStore
has its benefits and that this might not be practical if you're not already using either for other caching.
So basically I didn't manage to get to the bottom of it, I worked around it. I didn't have the time to tackle it properly, but if anyone has any pointers I'd be glad to try and make it work with cookie-stored sessions.
@JohnKacz, @vipera
I'm not sure why authn_requests_signed
is affecting failure of the non authenticated SAML request. We need to do more investigation about this issue. If you guys could provide more information like logs or whatever to help to speed up the investigation that would be great.
About cookie store issue is cookie only allows to store 4kb but the SAML request is too big to store it. Instead of that you guys can store SP initiated SAML request URL (not request itself) for SAML request and let "devise" redirect back to SP url again after successfully logged into your IdP.
I had the same issue and wasn't able to get devise to help so I modified the validate_saml_request
before action and set the SAMLRequest to the session on post and then pulled it back out on get.
def validate_saml_request
if request.get?
params["SAMLRequest"] = session["SAMLRequest"]
elsif request.post?
session["SAMLRequest"] = params["SAMLRequest"]
store_location_for("user", "#{request.base_url}/saml/auth") if current_user.blank?
end
super
end
I'm on a forked branch of 0.9.0
@mrobock your code has a hint. I would like to clarify one thing who also looking for some solution. SAML has also specification GET request (not only POST), people who uses your way need to be aware of SP has only support POST request. And I think you really don't need to override the method since you have full management of your own session controller, or application controller of a Rails app.
I wondered if there should be an
examples
directory that had some different example deployments. It might be "handy" for new implementations? For example. We had an "existing" rails application that uses "devise" for user authentication / management. We just wanted to add the ability for it to be the IdP. It turns out that is super easy, once you have fought it long enough ;)Devise Example
app/controllers/saml_idp_controller.rb
config/routes.rb
(add the following)
config/initializers/saml_idp.rb
Add this file per the README. Note that it does require customization.
base = Rails.configuration.x.saml_idp_base
which should be set asconfig.x.saml_idp_base = https://www.example.com/
in yourconfig/environments/${RAILS_ENV}.rb
.secrets.yaml
(and not in version control).config.name_id_formats
must be set (watch out, the example has a#
before the=
)config.service_provider.persisted_metadata_getter
, see Issue #16 just in case that hasn't been fixed yet.