zquestz / omniauth-google-oauth2

Oauth2 strategy for Google
1.45k stars 413 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 10 months 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 10 months ago

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

Gasol commented 9 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 9 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 9 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 9 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 4 months ago

@Gasol did it work in prod for you?