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
devise ~> 4.8.0
omniauth-linkedin-oauth2 ~> 1.0.0
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.
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:
I'm trying to set Artillery config to be able to send nested JSON body. This is how my configuration looks like:
config:
target: <URL>
phases:
- duration: 10
arrivalRate: 20
processor: "./input-parser.js"
scenarios:
- flow:
- function: "parseJsonFile"
- post:
url: /workflow-instance
headers:
Content-Type: "application/json"
json:
name: "{{ name }}"
namespace: "{{ namespace }}"
template_name: "{{ template_name }}"
attributes: "{{ attributes }}"
- get:
url: "/workflow-instance/status?name={{ template_name }}&namespace={{ namespace }}"
I have a problem with "attributes" because the content of attributes is:
{ pod_name: 'POD_NAME', port: 'PORT_NUMBER' }
So basically, this will not work:
attributes: "{ pod_name: 'POD_NAME', port: 'PORT_NUMBER' }"
as well as this:
attributes:
pod_name: 'POD_NAME'
port: 'PORT_NUMBER'
I didn't found good examples for this particular case in Artillery docs.
The following workaround worked for me Embedding JSON Data into YAML file
Then you'd have to change your attributes for:
attributes: '{ pod_name: "POD_NAME", port: "PORT_NUMBER" }'
I'm using:
Artillery: 1.7.9
Artillery Pro: not installed (https://artillery.io/pro)
Node.js: v14.6.0
OS: darwin/x64
For future readers looking for hardcoding nested JSON, this worked for me:
...
scenarios:
-
flow:
-
post:
url: "/"
json:
text: {"filter": {"enabled": true}}
In Symfony 4, i try to pass parameter from my .env
to a controller:
in .env:
###> First user creation variables ###
FIRST_USER_LOGIN=admintest
FIRST_USER_PASSWORD=azertyui
###< First user creation variables ###
in services.yaml:
parameters:
locale: 'en'
first.user:
login: '%env(FIRST_USER_LOGIN)%'
password: '%env(FIRST_USER_PASSWORD)%'
in my controller:
$utilisateur->setUsername($this->getParameter('first.user.login'))
->setPassword(
$encoder->encodePassword(
$utilisateur,
$this->getParameter('first.user.password')
)
)
->setRoles(['ROLE_ADMIN']);
I don't know what I'm doing wrong but I've got this error message:
The parameter "first.user.login" must be defined.
Solved: just changed the services.yaml:
parameters:
locale: 'en'
first.user.login: '%env(FIRST_USER_LOGIN)%'
first.user.password: '%env(FIRST_USER_PASSWORD)%'
Im using redis-rails in my project to store a cache of users, and I don't know why a extra line is added at begining of cache.
This is my config:
config.cache_store = :redis_store, {
host: ENV['REDIS_SERVER'] || 'localhost',
port: 6379,
db: 0,
namespace: ENV['CUSTOMER']
}
This is my code:
namespace :update_employees_cache do
desc "Update employees cache"
task update: :environment do
employees = []
Employee.where(active: true).each do |item|
employees.push({ id: item.id, name: item.name })
end
Rails.cache.write "employees", employees.to_json
end
end
This is the result
At line 1, o: ActiveSupport::Cache::Entry:#valueI"�
What is this?
After open a issue in the project repo I discovered that is the default behavior of rails wrapping the cache with that data.
In my case I need to avoid it, then is needed set row as true in configs.
config.cache_store = :redis_store, {
host: ENV['REDIS_SERVER'] || 'localhost',
port: 6379,
db: 0,
namespace: ENV['CUSTOMER'],
raw: true
}
I'm parsing a URL in my Rails application. I want to get the domain example.com stripped of any protocol, subdomain, directories, etc.
My method:
def clean_host(url)
uri = URI.parse(url)
return uri
end
What I actually get for example.com/ is:
scheme:
user:
password:
host:
port:
path: example.com/
query:
opaque:
registry:
fragment:
parser:
What I eventually want is to get the domain example.com stripped of any protocol, subdomain, directories, etc.
I've looked into domainatrix but it wont bundle with my project. Is there a way to do this with Ruby's URI.parse? Or will I have to look into other avenues?
The problem is that example.com/ isn't a full URL. URI rightly assumes it's a path. If it were me and you're going to have a lot of these fragments I'd just brute force it.
> "example.com/".sub(%r{^.*?://}, '').sub(%r{/.*$}, '')
=> "example.com"
> "http://subdomain.example.com/path/here".sub(%r{^.*?://}, '').sub(%r{/.*$}, '')
=> "subdomain.example.com"
Stripping the subdomain off is another ball of wax as you'd need to example the TLD to determine what is appropriate so you don't end up with say "com.uk"
You should just be able to do request.domain assuming you are doing this in a controller.
You can also use request.path and request.subdomain
For example: http://www.domain.com/users/123
request.subdomain => 'www'
request.domain => 'domain.com'
request.path => '/users/123'