linkedin_openid_sign_in 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []