Tatsurou
Tatsurou

Reputation: 161

Linkedin Oauth In Ruby on Rails is Not Logging In Every Few Times

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

Answers (2)

Trip
Trip

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

Tatsurou
Tatsurou

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

Related Questions