dlindahl / omniauth-cas

A CAS OmniAuth Strategy
MIT License
88 stars 79 forks source link

Support Single Sign Out #13

Closed pencil closed 10 years ago

pencil commented 11 years ago

Would be nice to have support for CAS' Single Sign Out.

dlindahl commented 11 years ago

I will definitely be looking into this. Though off the top of my head, I'm not sure this is the domain of an OmniAuth strategy.

I'm in the process of looking at CASino to power my employer's CAS server and will certainly come around to this in the process.

dlindahl commented 11 years ago

@pencil Ive taken a first stab at implementing this:

Rails.application.config.middleware.use OmniAuth::Builder do
  # The `request` arguments passed into the SSO callback is an instance
  # of Rack::Request with the SAML XML pre-parsed:
  # {
  #   'session_index' => 'ST-abc123-123456',
  #   'name_id' => '@NOT_USED@',
  #   'logoutRequest' => '<samlp:LogoutRequest ...'
  # }
  provider :cas, on_single_sign_out:Proc.new do |request|
    # Take whatever action is necessary to remove any local reference to the user's session
    ServiceTickets.where(ticket:request.params[:session_index]).destroy
  end
end

You can even route the SSO request directly to a Rails controller:

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :cas, on_single_sign_out:Proc.new do |request|
    CasSessionsController.action(:single_sign_out).call request.env
  end
end

I have done a rough smoke test with my instance of CASino and it seems to work well enough. It would be great if you could take it for a spin and let me know if you have any feedback.

The code is currently in the issues/13/sso branch.

saegey commented 10 years ago

I'm receiving a callback controller error using CASino and omniauth cas with gitlab. Do you have more detailed example for single sign out?

Processing by OmniauthCallbacksController#failure as */*
  Parameters: {"logoutRequest"=>"<samlp:LogoutRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"9464dd68-508f-4e94-916f-99b7b5b94310\" Version=\"2.0\" IssueInstant=\"2014-01-08 08:31:01 +0000\">\n  <saml:NameID>@NOT_USED@</saml:NameID>\n  <samlp:SessionIndex>ST-13891698504552-tEbuhiNm9rdFCP6fP3wRCEKziO9HPpk6BsIj4CFx</samlp:SessionIndex>\n</samlp:LogoutRequest>\n", "url"=>nil}
Can't verify CSRF token authenticity
dlindahl commented 10 years ago

@saegey Is this at all related to #7?

pencil commented 10 years ago

Rails, right? Think you would have to disable the CSRF check for the logout requests.

saegey commented 10 years ago

@pencil Yes rails 4. @dlindahl I don't think it is related to #7. Omniauth-cas does work but logging out causes that error and single sign out doesn't work.

saegey commented 10 years ago

Pretty sure I fixed my problem. I will document my configuration of Gitlab 6.4.2, CASino App and Omniauth CAS so it can end up on the Gitlab unoffical auth page

pencil commented 10 years ago

That would be great!

malickcisse commented 10 years ago

Hi, I am having the same problem as @saegey , How did you manage to fix it?

Started POST "/users/auth/cas/callback?url=http%3A%2F%2Flocalhost%3A3000%2F" for 127.0.0.1 at 2014-04-29 15:43:57 +0200
Processing by Devise::OmniauthCallbacksController#failure as */*
  Parameters: {"logoutRequest"=>"<samlp:LogoutRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"70e708b5-6e28-42aa-8db2-a31745c70633\" Version=\"2.0\" IssueInstant=\"2014-04-29 15:43:57 +0200\">\n  <saml:NameID>@NOT_USED@</saml:NameID>\n  <samlp:SessionIndex>ST-13987790295449-RfyzNl3ZeJYlVmBl63rboEsQMEfq2dtL9QYVvcg2</samlp:SessionIndex>\n</samlp:LogoutRequest>\n", "url"=>"http://localhost:3000/"}
WARNING: Can't verify CSRF token authenticity
Redirected to http://localhost:3000/users/sign_in
Completed 302 Found in 1.5ms (ActiveRecord: 0.0ms)
eriko commented 10 years ago

Does anyone have an example of integrating this into an existing rails app. I am thinking that I need to add a route that responds to post 'login/cas' but I an not sure what that should point to.

novaforge commented 10 years ago

@saegey , I think i'm facing the same issue as you!! Did you document your configurations somewhere ?

dlindahl commented 10 years ago

@eriko Your login page should POST to your CAS server with a callback URL that points to /auth/cas/callback. The CAS server will redirect the user there upon successful login at which point OmniAuth will handle the route and do its thing.

novaforge commented 9 years ago

@dlindahl Can you give us more details about how to implement lambda for "provider :cas, on_single_sign_out" ? I don't have access to ServiceTickets and also with default implementation omniauth-cas doesn't store session by service ticket.

I implement something like that : option :on_single_sign_out, lambda { |request| rack_input = request.env['rack.input'].read params = Rack::Utils.parse_query(rack_input, '&')

Take whatever action is necessary to remove any local reference to the user's session

       --- > ????
}

I can get params['session_index'] but i don't know what to do with it. Thanks

eriko commented 9 years ago

You are going to have to stash the session ticket when the user logs in and then use for the this process. It is how you verify that the logout request is actually a valid one. That said I have not gotten it fully working with omniauth and only really know about from dealing with some similar issues with canvas lms.

So I am of limited help. I am doing something like this to provide a cas SSO(on) and SSO(out) service for discourse to replace my plugin so it may be a bit overly complex. Before send the user off to cas login

  def login
    cookies.delete :query_string # some issues in testing where this stuck around
    cookies.signed[:query_string] = request.query_string
    cookies.signed[:referer] = request.env["HTTP_REFERER"]
    redirect_to '/auth/cas'
  end

after return and creating the account and stashing the ST and in this case the path back into discourse to log the user out there.

if configatron.ssout.enable
      login = Login.new
      login.referer = cookies.signed[:referer]
      login.ticket = request.env["omniauth.auth"]['credentials']['ticket']
      login.username = username
      login.save
    end
novaforge commented 9 years ago

Thanks @eriko; that the way the job is done by phpCAS, or java CAS client or even with the ruby client but not omniauth-cas.

Even if I succeed to store ST for a user, how i can tell to omniauth to delete its session. Do you know how sessions are stored ?

eriko commented 9 years ago

Actually this is with omniauth-cas. I think that it is your job to store the session and auth stuff. You can also use something like devise to manage that information. https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview

novaforge commented 9 years ago

@eriko I had created an issue about logout support, I got it working with some changes. All is explained in #36.