我在仅 API Rails 应用程序中实现 OAuth 方面得到了巨大帮助,并以 React 作为前端。互联网上没有太多可用的文档,所以我度过了一段艰难的时光。
附注我没有使用设备。
这是我的代码库:
宝石:
gem 'omniauth-google-oauth2'
gem 'omniauth-rails_csrf_protection'
gem 'omniauth'
nutrition-backend/app/models/patient.rb
(型号):
class Patient < ApplicationRecord
has_many :appointments
has_many :records
has_many :dietitians, through: :appointments
validates :first_name, presence: true
validates :last_name, presence: true
validates :email, presence: true, uniqueness: true
validates :password, presence: true, confirmation: true, on: :create
has_secure_password
validate :unique_email_across_models, on: :create
private
def unique_email_across_models
if Dietitian.exists?(email: email) || Patient.exists?(email: email)
errors.add(:email, 'is already taken')
end
end
end
nutrition-backend/app/controllers/sessions_controller.rb
:
class SessionsController < ApplicationController
skip_before_action :check_auth
def google_oauth_callback
binding.break
auth = request.env['omniauth.auth']
if auth.present?
email = auth.info.email
dietitian = Dietitian.find_by(email: email)
if dietitian
# If the dietitian already exists, sign in
token = generate_token(dietitian)
render json: { token: token, dietitian: dietitian.as_json(only: [:id, :first_name, :last_name, :email]) }, status: :ok
else
# If the dietitian doesn't exist, you can handle this case accordingly
render json: { error: 'Dietitian not found' }, status: :not_found
end
else
render json: { error: 'Authentication failed' }, status: :unprocessable_entity
end
end
private
def generate_token(user)
# Using a JWT library to generate a token
payload = { user_id: user.id }
JWT.encode(payload, Rails.application.credentials.secret_key_base, 'HS256')
end
end
nutrition-backend/config/initializers/omniauth.rb
:
Rails.application.config.middleware.use OmniAuth::Builder do
provider :google_oauth2, '1097164830695-qu5q175nq3r8bc5qvgo180n3lkvt1p8o.apps.googleusercontent.com', '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',
{
callback_path: '/auth/google_oauth2/callback', provider_ignores_state: Rails.env.development?
}
end
路线:
get '/auth/google_oauth2/callback', to: 'sessions#google_oauth_callback'
最初我收到一个错误:
OmniAuth::NoSessionError (You must provide a session to use OmniAuth.):
但能够通过在application.rb
中添加以下代码来调试它
config.middleware.use ActionDispatch::Cookies
config.middleware.use ActionDispatch::Session::CookieStore
但是,在那之后,我现在遇到的错误是:
D, [2023-09-23T11:43:37.562107 #32293] DEBUG -- omniauth: (google_oauth2) Callback phase initiated.
E, [2023-09-23T11:43:37.565538 #32293] ERROR -- omniauth: (google_oauth2) Authentication failure! undefined method `expired?' for nil:NilClass: NoMethodError, undefined method `expired?' for nil:NilClass
NoMethodError (undefined method `expired?' for nil:NilClass):
我尝试将
skip_before_action :verify_authenticity_token
添加到我的会话控制器,但没有解决问题。
有关其他上下文,这是我的 FRONTEND 代码库:
import { useState } from 'react';
import axios from 'axios';
import { GoogleOAuthProvider, GoogleLogin } from '@react-oauth/google';
function Auth() {
const [token, setToken] = useState(null);
const handleGoogleSuccess = async (response) => {
try {
const { data } = await axios.post('http://localhost:3000/auth/google_oauth2/callback', { response });
const { token } = data;
setToken(token);
} catch (error) {
console.error('Google OAuth Backend error:', error);
}
};
return (
<div>
{token ? (
// Rendered content for authenticated user
<p>You are logged in!</p>
) : (
// Rendered content for non-authenticated user
<div>
<GoogleOAuthProvider clientId="1097164830695-qu5q175nq3r8bc5qvgo180n3lkvt1p8o.apps.googleusercontent.com">
<GoogleLogin onSuccess={handleGoogleSuccess} onFailure={(error) => console.error('Google OAuth failure:', error)} />
</GoogleOAuthProvider>
</div>
)}
</div>
);
}
export default Auth;
全堆栈跟踪/错误
Started POST "/auth/google_oauth2/callback" for ::1 at 2023-09-24 10:56:38 +0800
ActiveRecord::SchemaMigration Pluck (0.7ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
D, [2023-09-24T10:56:38.747453 #10352] DEBUG -- omniauth: (google_oauth2) Callback phase initiated.
E, [2023-09-24T10:56:38.750371 #10352] ERROR -- omniauth: (google_oauth2) Authentication failure! undefined method `expired?' for nil:NilClass: NoMethodError, undefined method `expired?' for nil:NilClass
NoMethodError (undefined method `expired?' for nil:NilClass):
omniauth-oauth2 (1.8.0) lib/omniauth/strategies/oauth2.rb:92:in `callback_phase'
omniauth (2.1.1) lib/omniauth/strategy.rb:272:in `callback_call'
omniauth (2.1.1) lib/omniauth/strategy.rb:194:in `call!'
omniauth (2.1.1) lib/omniauth/strategy.rb:169:in `call'
omniauth (2.1.1) lib/omniauth/builder.rb:44:in `call'
rack (2.2.8) lib/rack/session/abstract/id.rb:266:in `context'
rack (2.2.8) lib/rack/session/abstract/id.rb:260:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/cookies.rb:704:in `call'
rack (2.2.8) lib/rack/etag.rb:27:in `call'
rack (2.2.8) lib/rack/conditional_get.rb:40:in `call'
rack (2.2.8) lib/rack/head.rb:12:in `call'
activerecord (7.0.8) lib/active_record/migration.rb:638:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'
activesupport (7.0.8) lib/active_support/callbacks.rb:99:in `run_callbacks'
actionpack (7.0.8) lib/action_dispatch/middleware/callbacks.rb:26:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/executor.rb:14:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/actionable_exceptions.rb:17:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/debug_exceptions.rb:28:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/show_exceptions.rb:29:in `call'
railties (7.0.8) lib/rails/rack/logger.rb:40:in `call_app'
railties (7.0.8) lib/rails/rack/logger.rb:25:in `block in call'
activesupport (7.0.8) lib/active_support/tagged_logging.rb:99:in `block in tagged'
activesupport (7.0.8) lib/active_support/tagged_logging.rb:37:in `tagged'
activesupport (7.0.8) lib/active_support/tagged_logging.rb:99:in `tagged'
railties (7.0.8) lib/rails/rack/logger.rb:25:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/remote_ip.rb:93:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/request_id.rb:26:in `call'
rack (2.2.8) lib/rack/runtime.rb:22:in `call'
activesupport (7.0.8) lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/server_timing.rb:61:in `block in call'
actionpack (7.0.8) lib/action_dispatch/middleware/server_timing.rb:26:in `collect_events'
actionpack (7.0.8) lib/action_dispatch/middleware/server_timing.rb:60:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/executor.rb:14:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/static.rb:23:in `call'
rack (2.2.8) lib/rack/sendfile.rb:110:in `call'
actionpack (7.0.8) lib/action_dispatch/middleware/host_authorization.rb:138:in `call'
rack-cors (2.0.1) lib/rack/cors.rb:102:in `call'
railties (7.0.8) lib/rails/engine.rb:530:in `call'
puma (5.6.7) lib/puma/configuration.rb:252:in `call'
puma (5.6.7) lib/puma/request.rb:77:in `block in handle_request'
puma (5.6.7) lib/puma/thread_pool.rb:340:in `with_force_shutdown'
puma (5.6.7) lib/puma/request.rb:76:in `handle_request'
puma (5.6.7) lib/puma/server.rb:443:in `process_client'
puma (5.6.7) lib/puma/thread_pool.rb:147:in `block in spawn_thread'
你的谷歌在前端的响应是什么样的?包括吗
{
code: 'YOUR_JWT_ID_TOKEN'
}
这是因为谷歌omniauth通过
搜索它验证者=正文&&正文['代码']
可能对遇到同样问题的人有帮助 - 我在类似的条件下遇到了同样的错误;然而,在 localhost 上它工作得很好,而在 生产 服务器上它不是。
问题是我的生产 Rails 凭证中的符号错误
google.appId
。因此,我建议仔细检查您的 appId/secret key
的可用性和正确性,这很有可能会有所帮助。