Add middleware to Devise failure app [duplicate] - ruby-on-rails

I use Devise for authentication and Apartment for multi-tenancy support on a SAAS app.
After a failed login, devise "redirects" to the login page (Users::SessionsController#new) and the value of Apartment::Tenant.current which was previously set in a TenantElevator middleware goes back to its default value of "public".
This is happening because Devise isn't actally redirecting to the login page but calling the FailureApp (which renders the login page) with a new rack env and returning its response. The new rack app doesn't have TenantElevator middleware so the tenant isn't set within the rack app.
Does anyone have any idea how to fix this? Maybe a way I can add the TenantElevator middleware to the Failure app?

Just stumbled on this and figured providing an answer may be useful for someone.
Haven't encountered this issue in a while, but looking through the sourcecode of the app I was working on, I fixed this issue by simply modifying where the apartment's middleware is inserted i.e modifying the order of the middleware.
Add the following snippet at the very bottom of your apartment.rb initializer file which should be located at app/config/initializers/apartment.rb
Rails.application.config.middleware.insert_before ActionDispatch::ShowExceptions, Apartment::Elevators::Domain
Essentially, insert your middleware before the ShowExceptions middleware, this ensures Devise, and ShowExceptions middleware, always runs under/within the context of the tenant set by whatever TenantElevator your app uses.
There may currently be a better way to handle this -- I am not sure, but this was the best option I found back when I had this issue.

Related

Rails 5 API app returning white screen with Devise sign_in page

I am working on a Rails 5 API only app using JWT tokens.
I'd like to use Rails Admin for internal processes and would like to configure Devise (or Clearance) for auth for staff members instead of integrating JWT tokens with Rails Admin directly.
The problem I have is once I set up Devise or Clearance (the controllers, models, and routes are there) the sign in screens return an empty HTML page.
There are some related issues with Clearance mentioned in https://github.com/thoughtbot/clearance/issues/741 but I've been unable to figure out why the HTML does not load.
The Rails API does exclude some middleware but it's not clear to me which is missing or causing the issue. Thanks.
I solved the problem by converting the Rails API only app to be a normal Rails app. Not the best solution, in my opinion, but it works!

Apartment current_tenant resets to 'public' after failed devise login

I use Devise for authentication and Apartment for multi-tenancy support on a SAAS app.
After a failed login, devise "redirects" to the login page (Users::SessionsController#new) and the value of Apartment::Tenant.current which was previously set in a TenantElevator middleware goes back to its default value of "public".
This is happening because Devise isn't actally redirecting to the login page but calling the FailureApp (which renders the login page) with a new rack env and returning its response. The new rack app doesn't have TenantElevator middleware so the tenant isn't set within the rack app.
Does anyone have any idea how to fix this? Maybe a way I can add the TenantElevator middleware to the Failure app?
Just stumbled on this and figured providing an answer may be useful for someone.
Haven't encountered this issue in a while, but looking through the sourcecode of the app I was working on, I fixed this issue by simply modifying where the apartment's middleware is inserted i.e modifying the order of the middleware.
Add the following snippet at the very bottom of your apartment.rb initializer file which should be located at app/config/initializers/apartment.rb
Rails.application.config.middleware.insert_before ActionDispatch::ShowExceptions, Apartment::Elevators::Domain
Essentially, insert your middleware before the ShowExceptions middleware, this ensures Devise, and ShowExceptions middleware, always runs under/within the context of the tenant set by whatever TenantElevator your app uses.
There may currently be a better way to handle this -- I am not sure, but this was the best option I found back when I had this issue.

is rails "secret_token" still important with config.session_store(:cache_store)?

i've done my google due diligence and can't find an explicit answer. so, good people of stack overflow...
if, in a rails 3 app, i'm not using cookies to store sessions, is it important to securely manage the "Application.config.secret_token"? furthermore, it is used at all?
The secret_token is used by the cookie_store, used to store session data client side. Here is a nice write-up of how to execute arbitrary code using a known secret_token.
This cookie_store is more precisely ActionDispatch::Session::CookieStore, a rack middleware that rails loads into your rack stack when you set session_store(:cookie_store). So if you're setting that to :session_store you should be fine not setting secret_token.
You can examine Rails.configuration.middleware to see all your middlewares and confirm ActionDispatch::Session::CookieStore is not one of them.
FWIW, a rails 3.2 app will start with secret_token not set, but requests that try to set session variables will fail 500. I haven't tracked down exactly where the failure happens.
But if you're not setting secret_token, and you don't have ActionDispatch::Session::CookieStore in your rack stack, and your app appears to work, you are safe from that particular attack.
The other use of secret_token is digest authentication.
In summary, to answer the question, if you're not using digest authentication, and you don't use cookie_store (e.g., by setting session_store(:cache_store)), then secret_token is not important.
The Application.config.secret_token is also used for HTTP Basic and Digest Access Authentication.
Rais 3.2 HTTP Basic and Digest Access Authentication source file
Basic and Digest Access Authentication RFC

Devise throwing HTTP auth on XHR and logging out

I'm having a ton of issues with Devise, using OmniAuth, to authenticate my Rails app. I relaunch my server and open up a new tab in Incognito mode (so that the cookies are cleared) and load my app. I log in, and then go through to the app.
When I get to a page that calls an authenticated action via AJAX, it asks for a username and password via HTTP Basic Authentication. I've disabled this in my devise.rb.
config.http_authenticatable = false
config.http_authenticatable_on_xhr = false
When I then go back to a previous page, it redirects me to the login page and asks for a login. This also happens when I visit a page that doesn't require authentication and then go back to an authenticated page.
This is getting immensely frustrating. I've unpacked Devise and Warden to my vendor/gems directory so that I can try to debug it, but I honestly can't figure out where to begin. Any help would be hugely appreciated.
Your AJAX call probably isn't setting the CSRF token. You might need to update your UJS gem (jquery-rails probably) or manually set the X-CSRF-Token HTTP header to the value of the tag. See this question: Devise session immediately expiring on .js call [AJAX]. You can test if this is the problem by disabling CSRF protection temporarily by chucking config.allow_forgery_protection = false in config/application.rb.
If you go the manual route, you should probably grab the value of the 'authenticity_token' meta tag first, and use that as the name of the actual token meta tag, rather than hard coding the reference to 'csrf-token'.
I would recommend updating to Rails 3.0.10 or 3.1 if you can. I was still having problems on 3.0.7.

Omniauth: How to set authentication provider details at runtime

I have a rails app that is accessible from 2 domains. Facebook requires me to register a facebook app for each of these domains and gives me credentials for each. With Omniauth I can only specify one set of credentials that is set on application startup. However, I would need to supply FB with different credentials depending on the host of the request.
There are 2 problems here:
How can I change the Omniauth credentials for facebook at runtime?
How can I intercept the call to facebook, check the domain and set the credentials accordingly? A before filter will not work, as Omniauth uses Rack Middleware.
Any suggestions are highly appreciated!
Copying the answer from the comments in order to remove this question from the "Unanswered" filter:
I solved this myself now. The problem was that the fb strategy calls
back to fb a second time to get an access token. In that second call
the wrong credentials were used (the ones set in the initializer). So
I had to patch the OAuth2 strategy so that it calls through to the
rails app again, to set the runtime credentials for that second call.
In the call back, which normally only handles the response form
Omniauth, I set the credentials and return a 404 unless
request.env["omniauth.auth"] is present. That works fine but has some
side effects for apps without dynamic providers.
The problem is now, that even if an application doesn't want to set the credentials at runtime, it has to add a condition to the callback like if request.env["omniauth.auth"] to avoid the callback code being executed when it is called the first time. The solution is probably to add a parameter to the Omniauth builder like :dynamic_provider and only call through to the app if it is set.
~ answer per Nico
This question is fairly old but still relevant. Nowdays it is also possible to set provider details dynamically during OmniAuth's Setup Phase.
For example:
Rails.application.config.middleware.use do
provider :example,
setup: ->(env) do
env['omniauth.strategy'].options[:foo] = env['rack.session']['foo']
env['omniauth.strategy'].options[:client_options][:site] = Something.dynamic('param')
end
end
Source: https://github.com/omniauth/omniauth/wiki/Dynamic-Providers

Resources