Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How does this gem work with devise + omniauth-saml and what's wrong with my setup? #18

Closed
leesmith opened this issue Sep 24, 2024 · 1 comment

Comments

@leesmith
Copy link

leesmith commented Sep 24, 2024

I'm trying to get devise + omniauth-saml to work with multiple, dynamic IdPs and the solution I built for a Rails 6 app apparently no longer works in Rails 7. So I'm giving this gem a try and instead of putting the IdP id on the session to use in the setup phase (like I was doing in a Rails 6 app), I'm trying to put the reference to the IdP in the route (what this gem is for, I presume).

I can call out to the IdP but the assertion coming back fails with a 404:

2024-09-24T21:33:12.898310+00:00 app[web.1]: I, [2024-09-24T21:33:12.898283 #2]  INFO -- : [fabfefd5-af66-4621-9de3-902b1700db0a] Completed 404 Not Found in 0ms (ActiveRecord: 0.0ms (0 queries, 0 cached) | GC: 0.0ms)
2024-09-24T21:33:12.899336+00:00 app[web.1]: F, [2024-09-24T21:33:12.899295 #2] FATAL -- : [fabfefd5-af66-4621-9de3-902b1700db0a]
2024-09-24T21:33:12.899336+00:00 app[web.1]: [fabfefd5-af66-4621-9de3-902b1700db0a] AbstractController::ActionNotFound (Could not find devise mapping for path "/users/auth/saml/e5d2bd5486910e3a5738aca80997159e/callback".
2024-09-24T21:33:12.899337+00:00 app[web.1]: This may happen for two reasons:
2024-09-24T21:33:12.899337+00:00 app[web.1]:
2024-09-24T21:33:12.899338+00:00 app[web.1]: 1) You forgot to wrap your route inside the scope block. For example:
2024-09-24T21:33:12.899338+00:00 app[web.1]:
2024-09-24T21:33:12.899339+00:00 app[web.1]: devise_scope :user do
2024-09-24T21:33:12.899339+00:00 app[web.1]: get "/some/route" => "some_devise_controller"
2024-09-24T21:33:12.899340+00:00 app[web.1]: end
2024-09-24T21:33:12.899340+00:00 app[web.1]:
2024-09-24T21:33:12.899340+00:00 app[web.1]: 2) You are testing a Devise controller bypassing the router.
2024-09-24T21:33:12.899340+00:00 app[web.1]: If so, you can explicitly tell Devise which mapping to use:
2024-09-24T21:33:12.899340+00:00 app[web.1]:
2024-09-24T21:33:12.899340+00:00 app[web.1]: @request.env["devise.mapping"] = Devise.mappings[:user]
2024-09-24T21:33:12.899340+00:00 app[web.1]:
2024-09-24T21:33:12.899340+00:00 app[web.1]: ):
2024-09-24T21:33:12.899341+00:00 app[web.1]: [fabfefd5-af66-4621-9de3-902b1700db0a]
2024-09-24T21:33:12.899341+00:00 app[web.1]: [fabfefd5-af66-4621-9de3-902b1700db0a] devise (4.9.4) app/controllers/devise_controller.rb:104:in `unknown_action!'

Any ideas what might be wrong with my setup? Thanks in advance!

# config/initializers/devise.rb

Devise.setup do |config|
...snip...
  config.omniauth :saml
...snip...
end
# config/initializers/omniauth.rb

Rails.application.config.middleware.use OmniAuth::Builder do
  OmniAuth::MultiProvider.register(self,
                                   provider_name: :saml,
                                   identity_provider_id_regex: /\w+/,
                                   path_prefix: '/users/auth/saml',
                                   callback_suffix: 'callback',
                                   name_identifier_format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
                                   issuer: 'example.com',
                                   allowed_clock_drift: 10.seconds) do |identity_provider_id, rack_env|
    identity_provider = IdentityProvider.find_by!(uid: identity_provider_id)
    {
      idp_cert: identity_provider.cert,
      idp_sso_target_url: identity_provider.target_url
    }
  end
end

# config/routes.rb

devise_for :users, controllers: {
  omniauth_callbacks: 'users/omniauth_callbacks'
}

match "/users/auth/saml/:identity_provider_id/callback",
  via: [:get, :post],
  to: "users/omniauth_callbacks#saml",
  as: "user_omniauth_callback"

match "/users/auth/saml/:identity_provider_id",
  via: [:get, :post],
  to: "users/omniauth_callbacks#passthru",
  as: "user_omniauth_authorize"
# app/controllers/users/omniauth_callbacks_controller.rb

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  skip_before_action :verify_authenticity_token

  def saml
    auth_hash = request.env["omniauth.auth"]

    if auth_hash.valid?
      Rails.logger.info("::::::::::: Auth hash is valid!") { auth_hash.info.inspect }
      # User lookup by email
      user = User.find_by(email: auth_hash.info.email.downcase)

      # Create new user if they don't already exist
      user = OnboardSsoUser.new(auth_hash).onboard if user.blank?

      # Sign in
      sign_in_and_redirect user
    else
      Rails.logger.info("::::::::::: Auth hash is invalid!") { auth_hash.info.inspect }
      redirect_to sso_url, error: "Invalid request"
    end
  end

end
@leesmith
Copy link
Author

Solved. Here was the fix:

# config/routes.rb

devise_for :users, controllers: {
  omniauth_callbacks: 'users/omniauth_callbacks'
}

devise_scope :user do
   match 'users/auth/saml/:identity_provider_id/callback',
     via: [:get, :post],
     to: 'users/omniauth_callbacks#saml',
     as: "user_omniauth_callback"
   match 'users/auth/saml/:identity_provider_id',
     via: [:get, :post],
     to: 'users/omniauth_callbacks#passthru',
     as: "user_omniauth_authorize"
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant