Rails API 上的 devise 和 Omniauth-google-oauth2 以及 React 上的 React-oauth 出现 CSRF 错误

问题描述 投票:0回答:1

我使用 React 7 作为 API 与omniauth-google-oauth2、omniauth-rails_csrf_protection、devise 和 devise-jwt。

对于 React React-oauth 和 jotai。

我像这样配置我的 Rails API:

db/schema.rb

...
  create_table "users", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
    t.string "first_name"
    t.string "last_name"
    t.string "password"
    t.string "email"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "encrypted_password", default: "", null: false
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.string "google_id"
    t.string "google_refresh_token"
    t.string "google_access_token"
    t.index ["email"], name: "index_users_on_email", unique: true
    t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
  end
...

配置/credentials.yml.enc

...
    google:
      app_id: APP_ID__FROM_GOOGLE
      app_secret: SECRET_FROM_GOOGLE

config/initializers/devise.rb

...
  config.omniauth :google_oauth2,
                  Rails.application.credentials.dig(:google, :app_id),
                  Rails.application.credentials.dig(:google, :app_secret),
                  { scope: 'email, profile',
                    prompt: 'select_account' }
...

配置/初始化器/cors.rb

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'http://localhost:3000'
    resource(
      '*',
      headers: :any,
      expose: %w[access-token Authorization client expired token-type uid],
      methods: %i[get patch put delete post options show]
    )
  end
end

配置/routes.rb

...
  devise_for :users,
             defaults: { format: :json },
             path: '',
             path_names: {
               sign_in: 'login',
               sign_out: 'logout',
               registration: 'signup'
             },
             controllers: {
               sessions: 'users/sessions',
               registrations: 'users/registrations',
               omniauth_callbacks: 'users/omniauth_callbacks'
             }

控制器/用户/omniauth_callbacks_controller.rb

module Users
  class OmniauthCallbacksController < Devise::OmniauthCallbacksController
    def google_oauth2
      @user = User.from_omniauth(request.env['omniauth.auth'])
      if @user.persisted?
        sign_in(@user)
        render json: {
          data: UserSerializer.new(@user).serializable_hash[:data][:attributes],
          message: 'Logged in successfully.'
        }, status: :ok
      else
        render json: {
          message: "error: #{user.errors.full_messages.join("\n")}"
        }, status: :unprocessable_entity
      end
    end
  end
end

模型/user.rb

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :jwt_authenticatable, jwt_revocation_strategy: JwtDenylist
  devise :omniauthable, omniauth_providers: [:google_oauth2]

  has_many :order_configurations, dependent: :destroy
  has_many :products, dependent: :destroy
  has_many :product_configurations, dependent: :destroy

  def self.from_omniauth(access_token)
    data = access_token.info
    find_or_create_by(google_id: data[:uid]) do |user|
      user.email = [:email]
      user.password = Devise.friendly_token[0, 20]
      user.skip_confirmation!
    end
  end
end

对于 React 前端,我得到:

main.tsx

...
    const googleClientId = SAME_APP_ID_AS_IN_RAILS_APP
...
    <GoogleOAuthProvider clientId={googleClientId}>
      <RouterProvider router={routes}/>
    </GoogleOAuthProvider>
...

AuthPage.tsx

import { useSetAtom } from 'jotai'
import userAtom from '../stores/userAtom.ts'
import { useNavigate } from 'react-router-dom'
import { useGoogleLogin } from '@react-oauth/google'

const AuthPage = () => {
    const setUser = useSetAtom(userAtom)
    const navigate = useNavigate()

    const login = useGoogleLogin({
        onSuccess: async (response) => {
                        console.log(response)
            const requestOptions = {
                method: 'POST',
                body: JSON.stringify({ 'access_token': response.access_token })
            }
            return fetch('http://localhost:3001/auth/google_oauth2/callback', requestOptions)
                .then(response => response.json())
                .then(response => {
                    setUser(response)
                    navigate('/')
                })
                .catch((error) => console.error(error))
        },
        flow: 'implicit'
    })

    return (
        <button onClick={() => login()}> Log with google</button>
    )
}

export default AuthPage

在 Rails 服务控制台中出现此错误

Started POST "/auth/google_oauth2/callback" for 127.0.0.1 at 2024-01-07 17:26:23 +0100
D, [2024-01-07T17:26:23.864119 #376013] DEBUG -- omniauth: (google_oauth2) Callback phase initiated.
E, [2024-01-07T17:26:23.864335 #376013] ERROR -- omniauth: (google_oauth2) Authentication failure! csrf_detected: OmniAuth::Strategies::OAuth2::CallbackError, csrf_detected | CSRF detected
Processing by Users::OmniauthCallbacksController#failure as */*
Redirected to http://localhost:3001/login
Completed 302 Found in 1ms (ActiveRecord: 0.0ms | Allocations: 225)

请求rails API之前的console.log:

{
  "access_token": "ACCESS_TOKEN_INFO",
  "token_type": "Bearer",
  "expires_in": 3599,
  "scope": "email profile https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile openid",
  "authuser": "0",
  "prompt": "none"
}

在调试模式下,我尝试在调试模式下在 google_oauth2 控制器方法中读取

request.env['omniauth.auth']
,但我无法捕获他,不存在。

我尝试将流程选项从

useGoogeLogin()
更改为
auth-code
并尝试在请求标头中将
access_token
作为不记名令牌。

在 OmniauthCallbacksController 中尝试添加

skip_before_action :verify_authenticity_token, only: :google_oauth2

但是我有这个错误

D, [2024-01-07T17:18:34.428091 #18514] DEBUG -- omniauth: (google_oauth2) Callback phase initiated.
E, [2024-01-07T17:18:34.431311 #18514] ERROR -- omniauth: (google_oauth2) Authentication failure! csrf_detected: OmniAuth::Strategies::OAuth2::CallbackError, csrf_detected | CSRF detected
E, [2024-01-07T17:18:34.463871 #18514] ERROR -- omniauth: (google_oauth2) Authentication failure! Before process_action callback :verify_authenticity_token has not been defined: ArgumentError, Before process_action callback :verify_authenticity_token has not been defined
  
ArgumentError (Before process_action callback :verify_authenticity_token has not been defined):
reactjs ruby-on-rails devise omniauth-google-oauth2
1个回答
0
投票

我现在也遇到同样的问题,请问你解决了吗?

© www.soinside.com 2019 - 2024. All rights reserved.