-
Notifications
You must be signed in to change notification settings - Fork 86
/
Copy pathoauth2.rb
223 lines (189 loc) · 7.6 KB
/
oauth2.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
module LinkedIn
# The LinkedIn::OAuth2::Client class. Inherits directly from [intreda/oauth2](https://github.com/intridea/oauth2)'s `OAuth2::Client`
#
# LinkedIn::OAuth2 sets the following default options:
#
# * site = "https://www.linkedin.com"
# * token_url = "/uas/oauth2/accessToken"
# * authorize_url = "/uas/oauth2/authorization"
#
# More details on LinkedIn's Authorization process can be found here: https://developer.linkedin.com/documents/authentication
class OAuth2 < ::OAuth2::Client
attr_accessor :access_token
# Instantiate a new OAuth 2.0 client using your client ID (aka API
# Key) and client secret (aka Secret Key).
#
# You should set the client_id and client_secret in the config.
#
# LinkedIn.configure do |config|
# config.client_id = ENV["LINKEDIN_CLIENT_ID"]
# config.client_secret = ENV["LINKEDIN_CLIENT_SECRET"]
# end
#
# This will let you initialize with zero arguments.
#
# If you have already set the `client_id` and `client_secret` in your
# config, the first and only argument can be the `options` hash.
#
# @param [String] client_id the client_id value
# @param [String] client_secret the client_secret value
# @param [Hash] options the options to create the client with
# @option options [Symbol] :token_method (:post) HTTP method to use to
# request token (:get or :post)
# @option options [Hash] :connection_opts ({}) Hash of connection options
# to pass to initialize Faraday with
# @option options [FixNum] :max_redirects (5) maximum number of redirects
# to follow
# @option options [Boolean] :raise_errors (true) whether or not to
# raise an error on malformed responses
# @yield [builder] The Faraday connection builder
def initialize(client_id=LinkedIn.config.client_id,
client_secret=LinkedIn.config.client_secret,
options = {}, &block)
if client_id.is_a? Hash
options = client_id
client_id = LinkedIn.config.client_id
end
options = default_oauth_options(options)
super client_id, client_secret, options, &block
@redirect_uri = options[:redirect_uri]
if self.options[:raise_errors]
check_credentials!(client_id, client_secret)
end
end
# Generates the URL users use to sign into your application.
#
# Once a user enters their LinkedIn credentials, they will be
# redirected to your `redirect_uri` with the `code` parameter attached
# to it. The value of the `code` parameter can be used to get an
# access token.
#
# We recommend you set your `client_id, `client_secret`, and
# `redirect_uri` in the `LinkedIn.configure` block. They can also be
# passed in as options.
#
# @param [Hash] options the options to generate the url with
# @option options [String] :redirect_uri The url that gets redirected
# to after a successful authentication. This must exactly match the
# redirect urls setup on your LinkedIn Application Settings page.
# This option is not required if you already set the redirect_uri in
# the config.
# @option options [String] :scope A string of requested permissions
# you want from users when they authenticate with your app. If these
# are set on yoru LinkedIn Application settings page, you do not
# need to pass them in. The string must be a space-sparated,
# case-sensitive list of available scopes. See available scopes on
# LinkedIn's API documentation page.
# @option options [String] :state A long string used for CSRF
# protection. It is added as the `state` GET param in the
# redirect_uri
# @option options [Boolean] :raise_errors (true) whether or not to
# raise an error on malformed responses
def auth_code_url(options={})
options = default_auth_code_url_options(options)
if self.options[:raise_errors]
check_redirect_uri!(options)
end
@redirect_uri = options[:redirect_uri]
self.auth_code.authorize_url(options)
end
# Returns the access token string for the newly authenticated user.
#
# It also sets the `access_token` field on this LinkedIn::OAuth2
# instance.
#
# The required `code`
#
# @param [String] code the auth code which is passed in as a GET
# parameter to your `redirect_uri` after users authenticate your app
# @param [Hash] options
# @option options [String] :redirect_uri You normally should not have
# to pass in the redirect_uri again. If `auth_code_url` was called
# on this LinkedIn::OAuth2 instance, then the `redirect_uri` will
# already be set. This is because the `redirect_uri` in the access
# token request must exactly match the `redirect_uri` in the auth
# code url.
# @option options [Boolean] :raise_errors (true) whether or not to
# raise an error on malformed responses
def get_access_token(code=nil, options={})
check_for_code!(code)
options = default_access_code_options(options)
if self.options[:raise_errors]
check_access_code_url!(options)
end
tok = self.auth_code.get_token(code, options)
self.access_token = LinkedIn::AccessToken.new(tok.token,
tok.expires_in,
tok.expires_at)
return self.access_token
rescue ::OAuth2::Error => e
raise OAuthError.new(e.response)
end
private ##############################################################
def default_access_code_options(custom_options={})
custom_options ||= {}
options = {raise_errors: true}
@redirect_uri = LinkedIn.config.redirect_uri if @redirect_uri.nil?
options[:redirect_uri] = @redirect_uri
options = options.merge custom_options
return options
end
def default_auth_code_url_options(custom_options={})
custom_options ||= {}
options = {raise_errors: true}
if not LinkedIn.config.redirect_uri.nil?
options[:redirect_uri] = LinkedIn.config.redirect_uri
end
if not LinkedIn.config.scope.nil?
options[:scope] = LinkedIn.config.scope
end
options = options.merge custom_options
if options[:state].nil?
options[:state] = generate_csrf_token
end
return options
end
def generate_csrf_token
SecureRandom.base64(32)
end
def check_access_code_url!(options={})
check_redirect_uri!(options)
if options[:redirect_uri] != @redirect_uri
raise redirect_uri_mismatch
end
end
def check_for_code!(code)
if code.nil?
msg = ErrorMessages.no_auth_code
raise InvalidRequest.new(msg)
end
end
def check_redirect_uri!(options={})
if options[:redirect_uri].nil?
raise redirect_uri_error
end
end
def default_oauth_options(custom_options={})
custom_options ||= {}
options = {}
options[:site] = LinkedIn.config.site
options[:token_url] = LinkedIn.config.token_url
options[:authorize_url] = LinkedIn.config.authorize_url
return options.merge custom_options
end
def check_credentials!(client_id, client_secret)
if client_id.nil? or client_secret.nil?
raise credential_error
end
end
def redirect_uri_error
InvalidRequest.new ErrorMessages.redirect_uri
end
def credential_error
InvalidRequest.new ErrorMessages.credentials_missing
end
def redirect_uri_mismatch
InvalidRequest.new ErrorMessages.redirect_uri_mismatch
end
end
end