zquestz / omniauth-google-oauth2

Oauth2 strategy for Google
1.47k stars 414 forks source link

Authentication failure! csrf_detected #443

Open donovanhubbard opened 1 year ago

donovanhubbard commented 1 year ago

I'm setting up a single page application using react that has ruby on rails as the backend. I have my google api credentials and I have the front end sign in with google setup according to these instructions. https://developers.google.com/identity/gsi/web/guides/client-library

When I click the sign in button I can auth with google successfully and I'm redirected to the callback url. I have a controller method setup but the code is throwing an exception prior to running any of it. I get the following error message:

D, [2023-05-04T22:29:16.073195 #10561] DEBUG -- omniauth: (google_oauth2) Callback phase initiated.
E, [2023-05-04T22:29:16.076422 #10561] ERROR -- omniauth: (google_oauth2) Authentication failure! csrf_detected: OmniAuth::Strategies::OAuth2::CallbackError, csrf_detected | CSRF detected
Processing by Users::OmniauthCallbacksController#failure as HTML

I've narrowed the exception code down to these lines here. https://github.com/omniauth/omniauth-oauth2/blob/3a43234ab5dd36a75f9c125c58fcfe1a37b26805/lib/omniauth/strategies/oauth2.rb#L86-L87

Both !options.provider_ignores_state and request.params["state"].to_s.empty? evaluate to true. It looks like it's expecting a parameter called state but the POST request that google's javascript api is sending only contains the parameters credentials which contains a JWT and g_csrf_token.

Am I missing something here? Am I using the wrong libraries or something?

ruby version: 3.2.2

rails version: 7.0.4.3

devise gem 4.9.2

omniauth gem 2.1.1

omniauth-google-oauth2 gem 1.1.1

viktor-shmigol commented 1 year ago

I'm having the same error: csrf_detected. I identified the problem and found it happens when I use the latest gem rack v3.0.8. However, if I downgrade it to v2.2.8, it's working without an issue.

ruby version: 3.2.2 rails version: 7.1.1 omniauth-google-oauth2 version: 1.1.1 omniauth-rails_csrf_protection 1.0.1

I'm not sure, but it may be related to this gem as well: https://github.com/cookpad/omniauth-rails_csrf_protection

zquestz commented 1 year ago

Does any version of rack v3 work? Might want to add this to the docs.

Gasol commented 11 months ago

Does any version of rack v3 work? Might want to add this to the docs.

It is working for me on Rails 7.1 with Rack 3.0.8.

viktor-shmigol commented 11 months ago

It's working in the dev environment but does not in the Prod. @Gasol Did you try it in the Production environment?

Gasol commented 11 months ago

@viktor-shmigol The project I am working on is still under development. I'll get back to you when it gets deployed.

I think it should works as well. Since the only different are oauth2 credentials and redirect_uri.

lylo commented 11 months ago

I'm seeing a similar issue here (although I'm using standard Rails not React) when trying to use Google One Tap with Omniauth as per https://developers.google.com/identity/gsi/web/guides/display-button.

I'm using:

gem "omniauth-google-oauth2"
gem "omniauth-rails_csrf_protection"

My initializer is:

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :google_oauth2,
           Rails.application.credentials.google.client_id,
           Rails.application.credentials.google.client_secret
           {
             provider_ignores_state: true
           }
end

And my view is:

      <script src="https://accounts.google.com/gsi/client" async></script>
      <div id="g_id_onload"
          data-client_id="<%= Rails.application.credentials.dig(:google, :client_id) %>"
          data-login_uri="<%= auth_callback_url(:google_oauth2) %>"
          data-auto_prompt="false">
      </div>
      <div class="g_id_signin"
          data-type="standard"
          data-size="large"
          data-theme="outline"
          data-text="sign_in_with"
          data-shape="rectangular"
          data-logo_alignment="left">
      </div>

The Google flow works but Omniauth complains when the callback is received:

Started POST "/auth/google_oauth2/callback" for ::1 at 2023-12-04 15:21:40 +0000
DEBUG -- omniauth: (google_oauth2) Callback phase initiated.
ERROR -- omniauth: (google_oauth2) Authentication failure! csrf_detected: OmniAuth::Strategies::OAuth2::CallbackError, csrf_detected | CSRF detected
ERROR -- omniauth: (google_oauth2) Authentication failure! invalid_credentials: OmniAuth::Strategies::OAuth2::CallbackError, csrf_detected | CSRF detected
ERROR -- omniauth: (google_oauth2) Authentication failure! csrf_detected | CSRF detected: OmniAuth::Strategies::OAuth2::CallbackError, csrf_detected | CSRF detected
OmniAuth::Strategies::OAuth2::CallbackError (csrf_detected | CSRF detected):

If I don't try and use the Google One Tap in this way:

<%= button_to "Log in with Google", google_auth_path, data: { turbo: false } %>

Then it works fine, no CSFR error at all. However, there is a subtle difference. When I use the standard button_to above, OmniAuth behaves differently:

Started POST "/auth/google_oauth2" for ::1 at 2023-12-04 15:26:38 +0000
DEBUG -- omniauth: (google_oauth2) Request phase initiated.
Started GET "/auth/google_oauth2/callback?state=[state]&code=[code]&scope=email+profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+openid&authuser=0&prompt=consent" for ::1 at 2023-12-04 15:26:41 +0000
DEBUG -- omniauth: (google_oauth2) Callback phase initiated.
OAuth2::AccessToken.from_hash: `hash` contained more than one 'token' key (["access_token", "id_token"]); using "access_token".

As you can see there is the additional POST to /auth/google_ouath2 ("request phase initiated") which then results in a GET requests to the callback, not a POST.

If anyone has a workaround for this, I'd love to know.

Perhaps Omniauth is not (yet) compatible with Google One Tap sign in?

zquestz commented 6 months ago

@Gasol did it work in prod for you?

andrer0cha commented 1 month ago

Are there any news or solutions? I'm facing the same issue trying to run a react on rails app with Devise and omniauth. I also tried using the npm library but it's not working.

zquestz commented 4 weeks ago

If two libraries are not working, it is very likely the google app is not configured correctly. Make sure your callback paths are correct.