我一直在开发一个使用新的 Linkedin OpenID 登录功能的应用程序。问题是它确实有效,但大多数时候它都有效。每隔几次(3-10次,相当随机)它不会让您登录,而是将您带回到根路径。我已经阅读了所有可能的情况并骚扰了 ChatGPT,但一无所获。我还尝试向控制器添加 cookie 清除机制,但没有效果。代码如下所示:
omniauth_callbacks_controller.rb:
# frozen_string_literal: true
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
before_action :retries
def linkedin
# flash[:notice] = I18n.t('omniauth_callbacks.notice.linkedin_callback_initiated')
login
end
def failure
if @retries < 1
@retries += 1
login # Retry the authentication
else
redirect_to root_path
end
end
def login
if auth_hash.nil?
# puts "Authentication data not received from provider."
redirect_to root_path
return
end
# flash[:notice] = I18n.t('omniauth_callbacks.notice.processing_linkedin_login', uid: auth_hash[:uid])
existing_user = User.find_by(uid: auth_hash[:uid])
if existing_user.present?
# flash[:notice] = I18n.t('omniauth_callbacks.notice.existing_user_found', uid: auth_hash[:uid])
sign_in_existing_user(existing_user)
else
# flash[:notice] = I18n.t('omniauth_callbacks.notice.no_existing_user')
create_or_sign_in_user
end
end
private
def retries
@retries ||= 0
end
def auth_hash
request.env['omniauth.auth'].tap do |auth|
# flash[:notice] = I18n.t('omniauth_callbacks.notice.auth_hash_received', auth: auth.inspect)
end
end
def sign_in_existing_user(user)
if user.provider == auth_hash[:provider]
# flash[:notice] = I18n.t('omniauth_callbacks.notice.signing_in_existing_user', email: user.email)
sign_in_and_redirect(user)
else
flash[:alert] = I18n.t('omniauth_callbacks.alert.user_exists_different_provider', email: user.email)
redirect_to root_path
end
end
def create_or_sign_in_user
user = User.from_omniauth(auth_hash).tap do |new_user|
# flash[:notice] = I18n.t('omniauth_callbacks.notice.user_persisted', email: new_user.email)
end
if user.persisted?
handle_persisted_user(user)
else
handle_nonpersisted_user(user)
end
end
def handle_persisted_user(user)
# flash[:notice] = I18n.t('omniauth_callbacks.notice.user_persisted', email: user.email)
sign_in_and_redirect(user)
# set_flash_message(:notice, :success, kind: 'LinkedIn') if is_navigational_format?
end
def handle_nonpersisted_user(user)
flash[:alert] = I18n.t('omniauth_callbacks.alert.unable_to_sign_in', errors: user.errors.full_messages.join(', '))
redirect_to root_path
end
end
Devise.rb 设置:
config.skip_session_storage = [:http_auth, :linkedin]
config.omniauth :linkedin, ENV.fetch('LINKEDIN_CLIENT_ID', nil) || Rails.application.credentials[:linkedin_id], ENV.fetch('LINKEDIN_CLIENT_SECRET', nil) || Rails.application.credentials[:linkedin_key], :scope => 'openid profile email'
宝石档案:
gem 'oauth2', '~> 2.0', '>= 2.0.9'
gem "omniauth", "~> 2.1.2"
gem 'omniauth-linkedin-openid', '~> 1.0.1'
gem 'omniauth-oauth2', '~> 1.8'
gem "omniauth-rails_csrf_protection", '~> 1.0.1'
未登录时的错误日志:
web-1 | D, [2024-07-02T07:59:54.367153 #1] DEBUG -- omniauth: (linkedin) Callback phase initiated.
web-1 | OAuth2::AccessToken.from_hash: `hash` contained more than one 'token' key (["access_token", "id_token"]); using "access_token".
web-1 | E, [2024-07-02T07:59:55.736180 #1] ERROR -- omniauth: (linkedin) Authentication failure! invalid_credentials: OAuth2::Error, {"status":401,"serviceErrorCode":65601,"code":"REVOKED_ACCESS_TOKEN","message":"The token used in the request has been revoked by the user"}
web-1 | Processing by OmniauthCallbacksController#failure as HTML
web-1 | Parameters: {"code"=>"AQS0fi3gwHvC1Dw-VkfwyZd9wXRzPx0fQVVkyFOua4AYQsOu7MhwvwIxWTlpWFG4XExwCVYt9NOky1USrmkQrfwlWea6xETgzcJmyvkK438UM8NHGfQNkkLSPeUQi_7WgZA_O0MKQVXB01LkcfXg28QHEJ02VBNT02MYbDY_R7IhSsaCOpg2pHJfZr2yalPWt14BZhGgJNgl4YkIlUE", "state"=>"e5756e8c7515f899c8f3adeb3a3e504fbbcb08ff9ddc75c0"}
web-1 | Redirected to http://127.0.0.1:3000/
我从哪里开始跟踪问题?
我发布这个问题已经有一段时间了,但一段时间后,我在 Golang 中找到了一个代码片段,它帮助我解决了这个问题,并且它起作用了。事实证明,对于 OpenID LinkedIN 身份验证功能,我们需要显式指定端点和身份验证方案,但任何地方都没有有关它的信息。
这是完成这项工作的代码:
config/initializers/devise.rb
config.omniauth :linkedin, ENV.fetch('LINKEDIN_CLIENT_ID', nil) || Rails.application.credentials[:linkedin_id], ENV.fetch('LINKEDIN_CLIENT_SECRET', nil) || Rails.application.credentials[:linkedin_key], :scope => 'openid profile email', client_options: {
site: 'https://api.linkedin.com',
authorize_url: 'https://www.linkedin.com/oauth/v2/authorization',
token_url: 'https://www.linkedin.com/oauth/v2/accessToken',
auth_scheme: :request_body # This is the equivalent to oauth2.AuthStyleInParams in Go }
此后再也没有出现过任何问题。