问题描述
我正在尝试通过 LinkedIn 在 rails 5.2 应用程序中设置身份验证,同样我指的是 documentation given by devise 但我收到以下错误:
ERROR -- omniauth: (linkedin) Authentication failure! Connection reset by peer: Faraday::SSLError,Connection reset by peer
我使用以下 gems 添加了这些配置
- 设计~> 4.8.0
- omniauth-linkedin-oauth2 ~> 1.0.0
- omniauth ~> 2.0.4
我什至尝试在包含有效 SSL 证书的生产服务器中的活动域上运行,但仍然抛出相同的错误。
解决方法
为您提供有关 LinkedIn 的一些信息:
LinkedIn no longer supports the JavaScript SDK. The recommended approach is to use OAuth 2.0 and LinkedIn's Auth APIs.
还有:
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
:为 Javascript 库添加 Jquery,运行命令:
$ yarn add jquery
然后,设置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
:通过添加 thin
gem
gem 'thin'
$ bundle install
编辑 config/application.rb
并添加:
config.force_ssl = true
在项目命令行中,输入:
$ openssl genrsa 2048 > host.key
$ chmod 400 host.key
$ openssl req -new -x509 -nodes -sha256 -days 365 -key host.key -out host.cert
执行这些命令后会创建两个文件:host.key
和 host.cert
。然后运行:
$ thin start --ssl --ssl-key-file=./host.key --ssl-cert-file=./host.cert
它将在默认地址运行项目:https://0.0.0.0:3000。如果你想在 https://localhost:3000 上运行,只需输入:
$ thin start -a localhost --ssl --ssl-key-file=./host.key --ssl-cert-file=./host.cert
Step 3
:创建 Linkedin oauth2 应用。
转到链接:https://www.linkedin.com/developers/
点击按钮Create app
,然后填写信息到应用名称、LinkedIn页面(必须通过自定义页面完成)、应用徽标、条款复选框。然后点击“创建应用”以注册您的应用。
在“设置”选项卡中,设置您的应用程序的域,我使用 localhost 运行,因此我将设置 https://localhost:3000
。
在 Auth 选项卡中,将客户端 ID 和客户端密钥保存到 config/application.yml
(记住在此之前运行命令 $ bundle exec figaro install
),如下所示:
LINKEDIN_APP_ID: 86g3...sfjm
LINKEDIN_APP_SECRET: OKnb...jzSL
然后为您的应用编辑、键入并保存到部分授权重定向网址:
https://localhost:3000/auth/linkedin/callback
检查可在此页面中使用的范围!我的是r_emailaddress r_liteprofile
。
在产品选项卡上,选择 Sign In with LinkedIn
,状态将更改为 Review in progress
。刷新F5一段时间后这个状态消失就OK了!
Step 4
:像我一样设置所有代码。使用简单的 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
用内容创建app/controllers/linkedin_controller.rb
:
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
注意安装这些 gems,我们不需要任何 oauth2 链接库:
gem 'uri'
gem 'net-http'
$ bundle install
我们将通过这个回调视图退出弹出的LinkedIn登录app/views/linkedins/callback.html.erb
:
<script>
// Close this popup show from LinkedIn window open
close();
</script>
创建此主视图 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>