Search code examples
ruby-on-railsdevisesamlomniauthdoorkeeper

Session Loss and Empty 'user_redirect_to' Variable in SAML-based Authentication for Rails OAuth 2.0 System


I am developing an authentication system based on OAuth 2.0 on Rails.

I'm using the Devise gem to handle user sessions and login, Doorkeeper gem to manage OAuth 2.0 workflows. in addiotion to username and password login, I have social and enterprise login functionality as well. Which is handled by the OmniAuth gem. Specifically, for Google login, I'm utilizing the omniauth-google-oauth2 gem, while for enterprise login, we have implemented SAML-based authentication for Azure.

The problem we're encountering is related to the SAML-based workflows. When we call the "/oauth/authorize" endpoint from the client, it correctly redirects to the sign-in page, and the "session['user_redirect_to']" variable is properly set. After the login process is completed, it should redirect the user back to the client based on the callback URL provided in the "/oauth/authorize" endpoint.

This flow works flawlessly for both the username/password login and Google sign-in. However, when the SAML callback is triggered, the session is lost, and the "session['user_redirect_to']" variable becomes empty. As a result, the application is unable to redirect the user back to the provided callback URL.

I was able to make it work by passing user_redirect_to as a state param to the saml request and on the callback i was able to fetch the callback url from param and set it back to the session. But i know its not secure and there should be a much cleaner solution available.

i know its kind of a mess, but let me know if you need any more details


Solution

  • SAML works such that the other site (IDP) will POST back to your app. In newer Rails versions, the Default Cookie SameSite Policy would be Strict or at least Lax, as "None" or omitted before.

    In all current Browsers, that will invalidate the session (Cookie will not be sent, when a request POSTed to our domain),

    BUT there is a explicit mitigation, here verbatim by Chromium Team:

    Q: What is the Lax + POST mitigation?

    This is a specific exception made to account for existing cookie usage on some Single Sign-On implementations where a CSRF token is expected on a cross-site POST request. This is purely a temporary solution and will be removed in the future. It does not add any new behavior, but instead is just not applying the new SameSite=Lax default in certain scenarios. Specifically, a cookie that is at most 2 minutes old will be sent on a top-level cross-site POST request. However, if you rely on this behavior, you should update these cookies with the SameSite=None; Secure attributes to ensure they continue to function in the future.

    So, we needed to:

    1. Make sure to use a session store, and explicitly set Secure: true
    # config/initializers/session_store.rb
    
    Rails.application.config.session_store :cookie_store, key: '_app_session', secure: true
    
    1. Make sure you are still using the SameSite None for that request:
    Rails.application.config.action_dispatch.cookies_same_site_protection = lambda { |request|
      if request.fullpath.include?("/auth/saml")
        :none
      else
        :lax
      end
    }
    
    1. Make sure, you don't overwrite the cookie same site policy in a initializer:
    # config/initializers/new_framework_defaults_6_1.rb
    # don't activate that globally, as it will override the other setting
    #Rails.application.config.action_dispatch.cookies_same_site_protection = :lax