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 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,11 @@
1
+ require "bundler/setup"
2
+ require "bundler/gem_tasks"
3
+ require "rake/testtask"
4
+
5
+ Rake::TestTask.new do |test|
6
+ test.libs << "test"
7
+ test.test_files = FileList["test/**/*_test.rb"]
8
+ test.warning = false
9
+ end
10
+
11
+ task default: :test
@@ -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,4 @@
1
+ LinkedinOpenidSignIn::Engine.routes.draw do
2
+ get '/sign_in', to: 'sign_in#show'
3
+ get '/callback', to: 'callbacks#show'
4
+ end
@@ -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,3 @@
1
+ module LinkedinOpenidSignIn
2
+ VERSION = "0.1.0"
3
+ 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: []