Reputation: 161
I have been developing an app that is using new Linkedin OpenID login feature. The thing is that it works, but it works most of the time. Every few times, (3-10, quite random) it doesn't log you in but throws you back to the root path. I have read every possible scenario and molested ChatGPT, but gotten nowhere. I also tried adding cookie clearing mechanism to the controller but that had no effect. The code looks like this:
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 setup:
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'
Gemfile:
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'
Error logs when not logging in:
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/
Where do I begin tracking the problem?
Upvotes: 2
Views: 389
Reputation: 333
Thank you for posting this! The client options you used here didn't quite work for me, but I found this thread and solution, and the options posted there did indeed work for me: https://github.com/decioferreira/omniauth-linkedin-oauth2/issues/17#issuecomment-2168592407
client_options: {
authorize_url: 'https://www.linkedin.com/oauth/v2/authorization?response_type=code',
site: 'https://api.linkedin.com',
token_method: :post_with_query_string,
token_url: 'https://www.linkedin.com/oauth/v2/accessToken'
}
Upvotes: 1
Reputation: 161
It's been a while since I posted this question, but after some time I found a code snippet in Golang that helped me out to figure out the issue and it worked. Turns out, that for OpenID LinkedIN auth function we need to explicitly specify the endpoints and the auth scheme and there is no info about it anywhere.
Here is the code that did the job:
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 }
Never had a single problem since.
Upvotes: 2