linkedin_openid_sign_in 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +113 -0
- data/Rakefile +11 -0
- data/app/controllers/linkedin_openid_sign_in/callbacks_controller.rb +65 -0
- data/app/controllers/linkedin_openid_sign_in/sign_in_controller.rb +27 -0
- data/config/routes.rb +4 -0
- data/lib/linkedin_openid_sign_in/engine.rb +28 -0
- data/lib/linkedin_openid_sign_in/version.rb +3 -0
- data/lib/linkedin_openid_sign_in.rb +8 -0
- metadata +121 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f13602126171ac7fb7e1968d8f0bffeccb8461d39d2dbcd02a2ca7c1d9190060
|
4
|
+
data.tar.gz: d3997463861d587f6d93eb7808ab66400a9f1411be0f8e8f4e7355ab8d8620e8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c7c904ad2cda94ac403435d3741d8f2e44737d7191bbac4c81757c466ccdc8043afb6420cb8f8ba8c076829aabbaf80608fc17b23d26bab601a1223d40f0ef75
|
7
|
+
data.tar.gz: a3dd97d3550fdb3283b6ed0f1987dd10c9c86e3685d311cb7281804dd643e8311466fa73c35d393c0637cbd364177e558ab036aa66f8a0ff7497da404a003aa4
|
data/README.md
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
# LinkedIn Sign-In for Rails
|
2
|
+
This gem enables you to add LinkedIn sign-in functionality to your Rails app. With it, users can sign up for and sign in to your service using their LinkedIn accounts. This gem is highly inspired by [google_sign_in](https://github.com/basecamp/google_sign_in), and its configuration and usage are very similar.
|
3
|
+
|
4
|
+
## Installation
|
5
|
+
Add this line to your application's Gemfile:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
gem 'linkedin_openid_sign_in'
|
9
|
+
```
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
```bash
|
13
|
+
$ bundle
|
14
|
+
```
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
```bash
|
18
|
+
$ gem install linkedin_openid_sign_in
|
19
|
+
```
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
### Configuration
|
24
|
+
|
25
|
+
Setup you LinkedIn app and get OAuth credentials
|
26
|
+
1. Go to [LinkedIn Developer Console](https://www.linkedin.com/developers/apps)
|
27
|
+
2. Click "Create App" button on the top right corner
|
28
|
+
3. Enter app name
|
29
|
+
4. You need LinkedIn page, you can add existing page or create new one here. Page admin will have to accept your app.
|
30
|
+
5. Add logo
|
31
|
+
6. Click "Create App" button
|
32
|
+
7. ⚠️IMPORTANT: Page admin will have to accept your app. You can check status on the top of the page.
|
33
|
+
8. Click "Auth" tab
|
34
|
+
9. Add "Authorized redirect URLs" (e.g. `http://localhost:3000/linkedin_openid_sign_in/callbacks#show`)
|
35
|
+
|
36
|
+
This gem use `/linkedin_openid_sign_in/callbacks` path as callback path and then redirect to provided `redirect_url` option
|
37
|
+
|
38
|
+
10. Click "Save" button
|
39
|
+
11. Copy "Client ID" and "Client Secret" and add to your Rails app credentials
|
40
|
+
ex.: `EDITOR='code --wait' rails credentials:edit`
|
41
|
+
|
42
|
+
```yaml
|
43
|
+
linkedin_sign_in:
|
44
|
+
client_id: [Your client ID here]
|
45
|
+
client_secret: [Your client secret here]
|
46
|
+
```
|
47
|
+
|
48
|
+
## Usage
|
49
|
+
|
50
|
+
### Link to login
|
51
|
+
|
52
|
+
```
|
53
|
+
<%= link_to 'Linkedin login', linkedin_openid_sign_in.sign_in_path(redirect_url: create_login_url) %>
|
54
|
+
```
|
55
|
+
|
56
|
+
### Callback redirect
|
57
|
+
You can adjust `redirect_url` to your needs. It will be called after successful login.
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
# config/routes.rb
|
61
|
+
Rails.application.routes.draw do
|
62
|
+
# ...
|
63
|
+
get 'login/create', to: 'logins#create', as: :create_login
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
# app/controllers/logins_controller.rb
|
69
|
+
class LoginsController < ApplicationController
|
70
|
+
def create
|
71
|
+
if user = authenticate_with_google
|
72
|
+
cookies.signed[:user_id] = user.id
|
73
|
+
redirect_to user
|
74
|
+
else
|
75
|
+
redirect_to new_session_url, alert: 'authentication_failed'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
def authenticate_with_google
|
81
|
+
if sub = flash[:linkedin_sign_in]['sub']
|
82
|
+
User.find_by linkedin_id: sub
|
83
|
+
elsif flash[:linkedin_sign_in]
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
```
|
89
|
+
|
90
|
+
### Success response
|
91
|
+
Response is stored in `flash[:linkedin_sign_in]` and contains following keys:
|
92
|
+
```ruby
|
93
|
+
{
|
94
|
+
'iss': 'https://www.linkedin.com',
|
95
|
+
'aud': 'aud',
|
96
|
+
'iat': 1700919389,
|
97
|
+
'exp': 1700919389,
|
98
|
+
'sub': 'sub',
|
99
|
+
'email': 'btolarz@gmail.com',
|
100
|
+
'email_verified': true,
|
101
|
+
'locale': 'en_US'
|
102
|
+
}
|
103
|
+
```
|
104
|
+
|
105
|
+
Check details on https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2?context=linkedin%2Fconsumer%2Fcontext#id-token-payload
|
106
|
+
|
107
|
+
### Failure response
|
108
|
+
|
109
|
+
1. InvalidState - raised when returned state is different than stored state
|
110
|
+
2. Error from linkedIn - returned in `flash[:linkedin_sign_in][:error]` and `flash[:linkedin_sign_in][:error_description]` ex.: `user_cancelled_login`
|
111
|
+
|
112
|
+
## License
|
113
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
module LinkedinOpenidSignIn
|
2
|
+
class CallbacksController < ActionController::Base
|
3
|
+
InvalidState = Class.new(StandardError)
|
4
|
+
|
5
|
+
def show
|
6
|
+
flash_data = flash[:linkedin_sign_in].symbolize_keys
|
7
|
+
|
8
|
+
raise InvalidState.new("Invalid state") if params[:state].blank? || params[:state] != flash_data[:state]
|
9
|
+
|
10
|
+
data = if params[:error].present?
|
11
|
+
{
|
12
|
+
error: params[:error],
|
13
|
+
error_description: params[:error_description]
|
14
|
+
}
|
15
|
+
elsif token
|
16
|
+
json = JSON.parse(token)
|
17
|
+
decode_token(json['id_token']).merge(access_token: json['access_token'])
|
18
|
+
else
|
19
|
+
{
|
20
|
+
error: 'invalid_token',
|
21
|
+
error_description: 'Returned token is invalid'
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
redirect_to flash_data[:redirect_url], flash: { linkedin_sign_in: data.symbolize_keys }
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def state
|
31
|
+
@state ||= session[:linkedin_sign_in_state]
|
32
|
+
end
|
33
|
+
|
34
|
+
def redirect_url
|
35
|
+
@redirect_url ||= session[:linkedin_sign_in_redirect_url]
|
36
|
+
end
|
37
|
+
|
38
|
+
def token
|
39
|
+
return @token if defined? @token
|
40
|
+
|
41
|
+
url = URI("https://www.linkedin.com/oauth/v2/accessToken")
|
42
|
+
|
43
|
+
https = Net::HTTP.new(url.host, url.port)
|
44
|
+
https.use_ssl = true
|
45
|
+
|
46
|
+
request = Net::HTTP::Post.new(url)
|
47
|
+
request["Content-Type"] = "application/x-www-form-urlencoded"
|
48
|
+
request.body = "code=#{params[:code]}" +
|
49
|
+
"&client_id=#{LinkedinOpenidSignIn.client_id}" +
|
50
|
+
"&client_secret=#{LinkedinOpenidSignIn.client_secret}" +
|
51
|
+
"&redirect_uri=#{callback_url}" +
|
52
|
+
"&grant_type=authorization_code"
|
53
|
+
|
54
|
+
response = https.request(request)
|
55
|
+
@token = response.body
|
56
|
+
rescue StandardError => e
|
57
|
+
logger.error(e)
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def decode_token(token)
|
62
|
+
JWT.decode(token, nil, false, { algorithm: 'RS256' })[0]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module LinkedinOpenidSignIn
|
2
|
+
class SignInController < ActionController::Base
|
3
|
+
def show
|
4
|
+
client_id = LinkedinOpenidSignIn.client_id
|
5
|
+
scope = LinkedinOpenidSignIn.options[:scope]
|
6
|
+
|
7
|
+
linkedin_url = "https://www.linkedin.com/oauth/v2/authorization?response_type=code"+
|
8
|
+
"&client_id=#{client_id}" +
|
9
|
+
"&redirect_uri=#{callback_url}" +
|
10
|
+
"&state=#{state}" +
|
11
|
+
"&scope=#{scope}"
|
12
|
+
|
13
|
+
redirect_to linkedin_url, allow_other_host: true, flash: {
|
14
|
+
linkedin_sign_in: {
|
15
|
+
state: state,
|
16
|
+
redirect_url: params[:redirect_url]
|
17
|
+
}
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def state
|
24
|
+
@state ||= SecureRandom.urlsafe_base64(24)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rails/engine'
|
2
|
+
require 'linkedin_openid_sign_in' unless defined?(LinkedinOpenidSignIn)
|
3
|
+
|
4
|
+
module LinkedinOpenidSignIn
|
5
|
+
class Engine < ::Rails::Engine
|
6
|
+
isolate_namespace LinkedinOpenidSignIn
|
7
|
+
|
8
|
+
config.linkedin_openid_sign_in = ActiveSupport::OrderedOptions.new.update options: LinkedinOpenidSignIn.options
|
9
|
+
|
10
|
+
initializer 'linkedin_openid_sign_in.config' do |app|
|
11
|
+
config.after_initialize do
|
12
|
+
LinkedinOpenidSignIn.client_id = config.linkedin_openid_sign_in.client_id || app.credentials.dig(:linkedin_sign_in, :client_id)
|
13
|
+
LinkedinOpenidSignIn.client_secret = config.linkedin_openid_sign_in.client_secret || app.credentials.dig(:linkedin_sign_in, :client_secret)
|
14
|
+
|
15
|
+
LinkedinOpenidSignIn.options = {
|
16
|
+
scope: 'openid email'
|
17
|
+
}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
initializer 'linkedin_openid_sign_in.mount' do |app|
|
22
|
+
app.routes.prepend do
|
23
|
+
mount LinkedinOpenidSignIn::Engine, at: app.config.linkedin_openid_sign_in.root || 'linkedin_openid_sign_in'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
module LinkedinOpenidSignIn
|
2
|
+
mattr_accessor :client_id
|
3
|
+
mattr_accessor :client_secret
|
4
|
+
mattr_accessor :options, default: {}
|
5
|
+
end
|
6
|
+
|
7
|
+
require "linkedin_openid_sign_in/version"
|
8
|
+
require 'linkedin_openid_sign_in/engine' if defined?(Rails) && !defined?(LinkedinOpenidSignIn::Engine)
|
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: linkedin_openid_sign_in
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Bogusław Tolarz
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-12-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jwt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.5.6
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.5.6
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sqlite3
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: byebug
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: webmock
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description:
|
84
|
+
email:
|
85
|
+
- btolarz@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- README.md
|
91
|
+
- Rakefile
|
92
|
+
- app/controllers/linkedin_openid_sign_in/callbacks_controller.rb
|
93
|
+
- app/controllers/linkedin_openid_sign_in/sign_in_controller.rb
|
94
|
+
- config/routes.rb
|
95
|
+
- lib/linkedin_openid_sign_in.rb
|
96
|
+
- lib/linkedin_openid_sign_in/engine.rb
|
97
|
+
- lib/linkedin_openid_sign_in/version.rb
|
98
|
+
homepage: https://github.com/btolarz/linkedin_sign_in
|
99
|
+
licenses:
|
100
|
+
- MIT
|
101
|
+
metadata: {}
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
requirements: []
|
117
|
+
rubygems_version: 3.4.10
|
118
|
+
signing_key:
|
119
|
+
specification_version: 4
|
120
|
+
summary: LinkedIn OpenId Sign In for Rails
|
121
|
+
test_files: []
|