How to fix SSL error thrown by omniauth LinkedIn
Asked Answered
E

1

9

I'm trying to set up an authentication via LinkedIn in the rails 5.2 application, for the same I'm referring to the documentation given by devise but I am getting the following error:

ERROR -- omniauth: (linkedin) Authentication failure! Connection reset by peer: Faraday::SSLError, Connection reset by peer

I have added these using the following gems for the configuration

  1. devise ~> 4.8.0
  2. omniauth-linkedin-oauth2 ~> 1.0.0
  3. omniauth ~> 2.0.4

I even tried running on the active domain in the production server which contains the valid SSL certificate but still, the same error is thrown.

Efflorescent answered 28/5, 2021 at 10:8 Comment(0)
G
0

Some informations about LinkedIn for you:

LinkedIn no longer supports the JavaScript SDK. The recommended approach is to use OAuth 2.0 and LinkedIn's Auth APIs.

And:

LinkedIn does not support TLS 1.0. Support for TLS 1.1 has been marked for deprecation starting 02/01/2020. Please use TLS 1.2 when calling LinkedIn APIs. All API requests to api.linkedin.com must be made over HTTPS. Calls made over HTTP will fail.

Step 1: Add Jquery for Javascript library, run command:

$ yarn add jquery

Then, set content of config/webpack/environment.js:

const { environment } = require('@rails/webpacker')
const webpack = require('webpack')
environment.plugins.prepend('Provide',
 new webpack.ProvidePlugin({
   $: 'jquery/src/jquery',
   jQuery: 'jquery/src/jquery'
 })
)
module.exports = environment

Step 2: Create ssl connection by add thin gem

gem 'thin'
$ bundle install

Edit config/application.rb and add:

config.force_ssl = true

In project command line, type:

$ openssl genrsa 2048 > host.key
$ chmod 400 host.key
$ openssl req -new -x509 -nodes -sha256 -days 365 -key host.key -out host.cert

After these commands will create two file: host.key and host.cert. Then run:

$ thin start --ssl --ssl-key-file=./host.key --ssl-cert-file=./host.cert

It will run project in default address: https://0.0.0.0:3000. If you want to run on https://localhost:3000, just type:

$ thin start -a localhost --ssl --ssl-key-file=./host.key --ssl-cert-file=./host.cert

Step 3: Create Linkedin oauth2 app.

Go to link: https://www.linkedin.com/developers/

Click the button Create app, then fill the informations to App name, LinkedIn Page (have to finish it by a custom page), App logo, Terms tick box. Then click to Create app to register your app.

At Settings tab, set the domain of your app, I run with localhost so I will set https://localhost:3000.

At Auth tab, save the Client ID and Client Secret to config/application.yml (remember to run commands $ bundle exec figaro install before this) like these:

LINKEDIN_APP_ID: 86g3...sfjm
LINKEDIN_APP_SECRET: OKnb...jzSL

Then edit, type and save to part Authorized redirect URLs for your app:

https://localhost:3000/auth/linkedin/callback

Check available scopes to use in this page! Mine is r_emailaddress r_liteprofile.

At Products tab, select Sign In with LinkedIn, status will change to Review in progress. All is ok if this status disappear after refresh F5 by a while!

Step 4: Set all codes like mine. With simple config/routes.rb:

Rails.application.routes.draw do
  devise_for :users, :controllers => { :omniauth_callbacks => "omniauth_callbacks" } 

  get '/auth/linkedin/callback', to: "linkedin#callback"
  post '/auth/linkedin/url', to: "linkedin#popup"
  post '/auth/linkedin/token', to: "linkedin#get_token"
  post '/auth/linkedin/info', to: "linkedin#get_info"
  post '/auth/linkedin/out', to: "linkedin#stop"
  root to: "linkedin#home"
end

Create app/controllers/linkedin_controller.rb with content:

class LinkedinController < ApplicationController
  # Lib to get token
  require "uri"
  require "net/http"
  # Data variables set/get
  attr_accessor :client_id, :client_secret, :redirect_uri, :scope, :raise_error
  # Class variable with 2@
  @@token = ""
  # Return view linkedins/home page
  def home
    render 'linkedins/home'
  end
  # Call back from popup login window of LinkedIn site
  def callback
    Rails.logger.debug "Callback called! Params:"
    Rails.logger.debug params
    @code = params[:code]
    @state = params[:state]
    @redirect = '/auth/linkedin/callback'
    # Get token
    url = URI("https://www.linkedin.com/oauth/v2/accessToken")
    https = Net::HTTP.new(url.host, url.port)
    https.use_ssl = true
    request = Net::HTTP::Post.new(url)
    request["Content-Type"] = "application/x-www-form-urlencoded"
    host_uri = ENV['HOST']+@redirect
    request.body = "grant_type=authorization_code&code=#{@code}&client_id=#{ENV['LINKEDIN_APP_ID']}&client_secret=#{ENV['LINKEDIN_APP_SECRET']}&redirect_uri=#{host_uri}"

    response = https.request(request)
    Rails.logger.debug "response.read_body:"
    # Rails.logger.debug response.read_body
    r = JSON.parse(response.read_body)
    Rails.logger.debug r["access_token"]
    @@token = r["access_token"]

    render 'linkedins/callback'
  end
  # Config init values
  def start
    @client_id = ENV['LINKEDIN_APP_ID']
    @client_secret = ENV['LINKEDIN_APP_SECRET']
    @raise_error = 'true'
    @redirect = '/auth/linkedin/callback'
    @redirect_uri = ENV['HOST']+@redirect
    @scope = 'r_emailaddress r_liteprofile'
    @state = generate_csrf_token
  end
  # Return popup url for sign in by LinkedIn, method = POST
  def popup
    self.start
    @url = "https://www.linkedin.com/uas/oauth2/authorization?client_id=#{@client_id}&raise_errors=#{@raise_error}&redirect_uri=#{@redirect_uri}&response_type=code&scope=#{@scope}&state=#{@state}"
    # return @url
    render json: { status: 'Success', message: 'Load url for popup finished!', link: @url},status: :ok
  end
  # Get token of current account Linkedin logged
  def get_token
    Rails.logger.debug 'From get_token, @@token cache:'
    Rails.logger.debug @@token
    render json: { status: 'Success', message: 'Load token finished!', token: @@token},status: :ok
  end
  # Get basic info
  def get_info
    Rails.logger.debug 'From get_info!'
    # Create custom api linking
    fields = ['id', 'firstName', 'lastName', 'profilePicture']
    link = "https://api.linkedin.com/v2/me?projection=(#{ fields.join(',') })"

    url = URI(link)
    https = Net::HTTP.new(url.host, url.port)
    https.use_ssl = true
    request = Net::HTTP::Get.new(url)
    request["Authorization"] = "Bearer #{@@token}"
    response = https.request(request)
    Rails.logger.debug "From get_info, variable response:"
    Rails.logger.debug response
    r = JSON.parse(response.read_body)
    # r = JSON.parse(response.body)
    first_name = r['firstName']['localized']['en_US'].to_s
    last_name = r['lastName']['localized']['en_US'].to_s
    full_name = first_name + " " + last_name
    render json: { status: 'Success',message: 'Load link basic info finished!', name: full_name},status: :ok
  end
  # For logout LinkedIn, by method revoke
  def stop
    link = 'https://www.linkedin.com/oauth/v2/revoke'
    url = URI(link)
    https = Net::HTTP.new(url.host, url.port)
    https.use_ssl = true
    request = Net::HTTP::Post.new(url)
    request["Content-Type"] = "application/x-www-form-urlencoded"
    request.body = "client_id=#{ENV['LINKEDIN_APP_ID']}&client_secret=#{ENV['LINKEDIN_APP_SECRET']}&token=#{@@token}"
    response = https.request(request)
    Rails.logger.debug "Test logout linkedin!"
    render json: { status: 'Success',message: 'Log out finished!'},status: :ok
  end
  # Genereate random state
  def generate_csrf_token
    SecureRandom.base64(32)
  end
end

Note to install these gems, and we don't need any oauth2 linkedin libs:

gem 'uri'
gem 'net-http'
$ bundle install

We will exit popup LinkedIn login by this callback view app/views/linkedins/callback.html.erb:

<script>
  // Close this popup show from LinkedIn window open
  close();
</script>

Create this main view app/views/linkedins/home.html.erb:

<p>Linkedin Login Home page</p>
<button id="linkedin-login" type="button">Login</button>
<p id="linkedin-informations">Token here!</p>

<button id="linkedin-logout" type="button">Logout</button>
<p id="linkedin-results">Results here!</p>

<script>
  $('#linkedin-login').on('click', function(e){
    // e.preventDefault()
    var url_popup = ""
    var ltoken = ""
    var lurl = ""
    $.post('/auth/linkedin/url', function(json) {
      console.log(json)
      url_popup = json.link
      if (url_popup != "") {
        console.log('Successful to load url popup!')
        const w = 600
        const h = 600
        const top = (screen.height - h) / 4, left = (screen.width - w) / 2
        
        var child = window.open(url_popup, "popupWindow", `width=${w}, height=${h}, top=${top}, left=${left}, scrollbars=yes`)
        
        function checkChild() {
          if (child.closed) {  
            clearInterval(timer);
            $.post('/auth/linkedin/token', function(json) {
              console.log('Load token link successful!')
              $('#linkedin-informations').html('Token is comming ...')
              ltoken = json.token
              console.log(json.token)
              $('#linkedin-informations').html(json.token)
            })
            $.post('/auth/linkedin/info', function(json) {
              console.log('Load info link successful!')
              $('#linkedin-results').html('Information is comming ...')
              console.log(json)
              $('#linkedin-results').html(`Your login account: ${json.name}`)
              
            }) 
          }
        }

        var timer = setInterval(checkChild, 500);
      }
    })
    
    
  })

  $('#linkedin-logout').on('click', function(e){
    e.preventDefault()
    $.post('/auth/linkedin/out', function(json) {
      console.log('Log out successful!')
      $('#linkedin-results').html(`You logged out!`)
    })
  })

</script>

Successful screen: enter image description here

Guarnerius answered 3/6, 2021 at 4:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.