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

CSRF detected #49

Closed
rjocoleman opened this issue Feb 19, 2017 · 19 comments
Closed

CSRF detected #49

rjocoleman opened this issue Feb 19, 2017 · 19 comments
Labels
Milestone

Comments

@rjocoleman
Copy link

Using omniauth-auth0 v2.0.0 but otherwise following the Rails 5 guides in the docs leads to a csrf_detected error coming out of omniauth.

provider_ignores_state = true used to be set in the provider by default. This was removed in v2.0.0. Setting this explicitly avoids the CSRF detected error but it doesn't seem like a good idea.

Is there another suggested implementation to avoid setting provider_ignores_state = true?

@pouellet
Copy link

pouellet commented Mar 6, 2017

This might or not applied to you, but we ran in a similar problem using omniauth-auth0 and the devise omniauth integration. In our case, we manually generated the authorize url instead of relying on the omniauth route helper (user_auth0_omniauth_authorize_path). Omniauth is taking care of generating a state query parameter, storing it in the session and comparing when the request comes back.

You will want to make sure that this method https://github.com/intridea/omniauth-oauth2/blob/master/lib/omniauth/strategies/oauth2.rb#L51 gets called as part of the request generation.

@MattFenelon
Copy link

I'm having the same problem. It seems that the Auth0 Lock dialog is broken with this version of the gem. It works if I do what the README says and just redirect the user to /auth/auth0, which uses the hosted Login page.

As far as I can tell, OmniAuth-OAuth2 is expecting the callback URL to include a state query-string parameter, but it doesn't. I am still trying to track down why that is. Any help appreciated.

@pouellet
Copy link

Auth0 will return the state value if you pass it as a parameter when you send the user to the Auth0 authentication page. For it to work, the authorize request should look something like:

https://<account>.auth0.com/authorize?auth0Client=<auth0Client>&client_id=<clientId>&redirect_uri=<callback_uri>&response_type=code&state=<uuid>

callback_uri => https://<host>/users/auth/auth0/callback

If you use the user_auth0_omniauth_authorize_path helper, it will automatically generate the state query parameter and save it in the session so that it can be validated when the callback is called.

In our case, it looks something like this:

  1. Have your login button link to https://<host>/login and have this hit an AuthenticationController in the Rails app (in our case, we put it in the same controller that handles the auth0 callback).
  2. In the controller action, redirect the user to user_auth0_omniauth_authorize_path. At this point, OmniAuth-OAuth2 generates the Auth0 authorize url, including a unique state query parameter that it store in the session.
  3. The browser follows the redirect, the user authenticate with Auth0 and Auth0 redirects to your callback url and includes the state that you passed it.
  4. OmniAuth-OAuth2 validates that the state returned in the callback equals what is stored in the session.
class AuthenticationController < Devise::OmniauthCallbacksController

  def login
    redirect user_auth0_omniauth_authorize_path
  end

   ...
end

I hope it helps.

@rjocoleman
Copy link
Author

@pouellet thanks so much, that really did help! I'm now provider_ignores_state = false 👍

@MattFenelon
Copy link

Thanks @pouellet. That seems to be the same flow as linking the user to /auth/auth0? I am trying to use this gem with Auth0's Lock widget.

@rjocoleman mentioned using provider_ignores_state = false. I'm still not convinced this is a safe option to use - any malicious site can form a valid request to Auth0 and authenticate a user against the origin site.

That said, I'm not sure if this gem is where this problem needs to be tackled. Perhaps provider_ignores_state = false should be set, to turn off omniauth's csrf protection, and then a custom CSRF solution put in place, that adds a CSRF protection token into the state param of the Lock widget.

@rjocoleman
Copy link
Author

@MattFenelon I was using lock.js, but I took it out and started using the hosted login page.

Take a look at both the authorize_params (https://github.com/intridea/omniauth-oauth2/blob/master/lib/omniauth/strategies/oauth2.rb#L51-L60 and https://github.com/auth0/omniauth-auth0/blob/master/lib/omniauth/strategies/auth0.rb#L53-L57) methods, make sure it's called and add a param state to your lockjs redirectUrl.

@hoverlover
Copy link

hoverlover commented Mar 17, 2017

This is not working for me. I pass a state param to the auth0 authorization URL, but it doesn't pass it back. The only param I get is code. Is there a setting in my auth0 account that's preventing it from being sent back?

I'm using lock-passwordless-2.2.min.js on the Auth0 hosted login page, btw. Here's the relevant script part:

<script>
    // Decode utf8 characters properly
    var config = JSON.parse(decodeURIComponent(escape(window.atob('@@config@@'))));
    config.extraParams = config.extraParams || {};
    config.responseType = config.extraParams.response_type;
    config.dict = {
      email: {
        headerText: "Enter your email to sign in"
      },
      title: "My Title"
    };
    config.closeable = false;
    var connection = config.connection;
    var prompt = config.prompt;
    
    var loginHint = config.extraParams.login_hint;
    
    var lock = new Auth0LockPasswordless('key', 'my-domain.auth0.com');
    lock.emailcode(config);
  </script>

@hoverlover
Copy link

OK, I figured this out. When passing a state parameter to the auth0 hosted login page, it gets put in the extraParams object after parsing the @@config@@. I simply added an authParams object to the lock config, including a state key with the value set to config.extraParams.state.

Here's the updated script example:

<script>
    // Decode utf8 characters properly
    var config = JSON.parse(decodeURIComponent(escape(window.atob('@@config@@'))));
    config.extraParams = config.extraParams || {};
    config.responseType = config.extraParams.response_type;
    config.dict = {
      email: {
        headerText: "Enter your email to sign in"
      },
      title: "My Title"
    };
    config.closeable = false;
    config.authParams = {
        scope: 'openid profile',  // Learn about scopes: https://auth0.com/docs/scopes
        state: config.extraParams.state
      };
    
    var connection = config.connection;
    var prompt = config.prompt;
    
    var loginHint = config.extraParams.login_hint;
    
    var lock = new Auth0LockPasswordless('key', 'my-domain.auth0.com');
    lock.emailcode(config);
  </script>

Maybe someone from the Auth0 team can comment on why the state url param is stuffed into an extraParams object.

@atwoodjw
Copy link

atwoodjw commented Mar 22, 2017

I got CSRF working w/ Lock.

omniauth (1.6.1)
omniauth-auth0 (2.0.0)
omniauth-oauth2 (1.4.0)
lock.min.js (10.9.1)

session_helper.rb
module SessionHelper
  def state_meta_tag
    state = SecureRandom.hex(24)
    session['omniauth.state'] = state

    tag('meta', name: 'state', content: state)
  end
end
application.html.erb
<%= csrf_meta_tags %>
<%= state_meta_tag %>
session.js.erb
var options = {
  auth: {
    redirectUrl: '<%= Rails.application.routes.url_helpers.auth_callback_url(:oauth2) %>',
    responseType: 'code',
    params: {
      scope: 'openid email',
      state: $('meta[name="state"]').attr('content')
    }
  },
  theme: {
    primaryColor: '#EA5A52'
  }
};

var lock = new Auth0Lock('<%= ENV['AUTH0_CLIENT_ID'] %>', '<%= ENV['AUTH0_DOMAIN'] %>', options);
lock.show();

@ClaudioFloreani
Copy link

@atwoodjw can you specify which version of the omniauth gems? (omniauth, omniauth-auth0, omniauth-oauth2)

The problem arise only when switching to omniauth-auth0 version 2.0.0, so at least some upgrading instructions should be provided for customers going to the next major release.

@atwoodjw
Copy link

Sure.

omniauth (1.6.1)
omniauth-auth0 (2.0.0)
omniauth-oauth2 (1.4.0)
lock.min.js (10.9.1)

Yes, the issue only arrises in omniauth-auth0 (2.0.0) because provider_ignores_state = true is no longer set by default. CSRF protection wasn't enable by default in earlier versions.

@errfanwadia
Copy link

Above @atwoodjw worked like a charm.

But I am not able to achieve this for IdP initiated SSO as the IdP first authenticate and the callback URL is called directly from auth0. There is no way that you can store any state param in session as the request is initiated from IdP. Request to suggest any best practices

@dguettler
Copy link

@errfanwadia I ran into similar issue when doing impersonation. Everything happens on the Auth0 side before the callback to omniauth. The only solution I'm aware of so far was to disable the state check

@cocojoe cocojoe added the bug label Jul 18, 2018
@cocojoe
Copy link
Member

cocojoe commented Jul 18, 2018

@joshcanhelp Needs sorted.

@cocojoe
Copy link
Member

cocojoe commented Jul 18, 2018

Sorry for the long delay, picking up on this repo now. We will be going through some updates soon.

@joshcanhelp
Copy link
Contributor

Chiming in here since the fixes are using old versions of our libraries that should be updated. Again, apologies for the long delay in response here.

See my comment here for an example of how to use this library with Lock. Make sure you're using the latest major/minor version of Lock, preferably the latest (11.7.x as of this writing). This library includes Passwordless now as well, no need for the separate library.

That said ... you're best off using the universal login page (redirecting to /authorize) as described in our Rails quickstart.

Thank you @atwoodjw for the example posted!

@hoverlover - those are parameters sent to the /authorize endpoint, hence the naming. See the example above or mine linked here as that naming has changed a bit (auth.params.state).

@errfanwadia - If you're still struggling with this, please open a new issue with information about your setup so we can troubleshoot that.

@Cruz-glitch
Copy link

https:///users/auth/auth0/callback

@Cruz-glitch
Copy link

class AuthenticationController < Devise::OmniauthCallbacksController

def login
redirect user_auth0_omniauth_authorize_path
end

...
end

@2xUPe
Copy link

2xUPe commented Apr 5, 2021

class AuthenticationController < Devise::OmniauthCallbacksController

def login
redirect user_auth0_omniauth_authorize_path
end

...
end

Today I tried to sign in to https://code.videolan.org by using github.com. I got a pin , but it failed with CSRF detected. What should I do? How should I understand your comment? Is it fixed or not? Or is it a bug on https://code.videolan.org?

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

No branches or pull requests

13 participants