openid_connect_client 0.1.5 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +9 -11
- data/example.rb +10 -10
- data/lib/openid_connect_client.rb +127 -128
- data/lib/openid_connect_client/version.rb +1 -1
- data/openid_connect_client.gemspec +3 -3
- metadata +5 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c095094d3ccbaf0a5274b82deeee47d7933ebe0
|
4
|
+
data.tar.gz: 3f2f9888753f07cf3bf4fd01e1e7b29342f56f99
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: de23cbb223192312d1b87e1c3bc2da712b08e2fecd275692febddef8c1d4991caeb51c13cbda7c6a3d059b4a33b1291e60acc94e63f4b2fb05fbbe83e24efb45
|
7
|
+
data.tar.gz: 0cfe996add279debd7caad3a24c928faecca0488bdda1a0863d99d341ec49e39d08e47389a34978176014416bf443973767ce8d3ca85274a188c4f2531a5a1df
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# openid-connect-ruby
|
2
2
|
|
3
|
-
|
3
|
+
This is a literal, not so idiomatic ruby port of Michael Jett's excellent [OpenID Connect](https://github.com/jumbojett/OpenID-Connect-PHP) library for PHP.
|
4
4
|
|
5
5
|
## Requirements
|
6
6
|
- [curb](https://github.com/taf2/curb)
|
@@ -11,33 +11,31 @@ gem install openid_connect_client
|
|
11
11
|
```
|
12
12
|
|
13
13
|
## Usage
|
14
|
-
|
14
|
+
It's done in two steps: first, in your login controller you'll request authorization and redirect the user to the OpenID Connect provider. If your app gets authorized, then the provider will redirect the user back to your callback url where you'll be able to ask the provider for the user data.
|
15
15
|
|
16
16
|
See `example.rb`
|
17
17
|
|
18
|
-
###
|
18
|
+
### In the login controller
|
19
19
|
```ruby
|
20
|
-
# 1. Client setup
|
20
|
+
# 1. Client setup
|
21
21
|
oidc = OpenIDConnectClient::Client.new('https://provider.com/openid', 'CLIENT_ID', 'SECRET')
|
22
22
|
oidc.redirect_url = "http://yourweb.com/callback"
|
23
|
-
oidc.scopes = "openid email profile address
|
23
|
+
oidc.scopes = "openid email profile address"
|
24
24
|
|
25
25
|
# 2. Request authorization
|
26
26
|
oidc.authorize()
|
27
27
|
|
28
|
-
# 3. Save state in session
|
28
|
+
# 3. Save state in session
|
29
29
|
session[:state] = oidc.state
|
30
30
|
|
31
31
|
# 4. Redirect user to OpenID Connect provider
|
32
32
|
redirect_to(oidc.auth_endpoint)
|
33
33
|
```
|
34
34
|
|
35
|
-
###
|
35
|
+
### In the callback controller
|
36
36
|
```ruby
|
37
|
-
# 1.
|
37
|
+
# 1. Get client
|
38
38
|
oidc = OpenIDConnectClient::Client.new('https://provider.com/openid', 'CLIENT_ID', 'SECRET')
|
39
|
-
oidc.redirect_url = "http://yourweb.com/callback"
|
40
|
-
oidc.scopes = "openid email profile address phone"
|
41
39
|
|
42
40
|
# 2. Restore state
|
43
41
|
oidc.state = session[:state]
|
@@ -48,7 +46,7 @@ oidc.params = request.parameters
|
|
48
46
|
# 4. Authenticate your app against the provider
|
49
47
|
oidc.authenticate()
|
50
48
|
|
51
|
-
# 5. Fetch the user's details
|
49
|
+
# 5. Fetch the user's details
|
52
50
|
given_name = oidc.get('given_name')
|
53
51
|
email = oidc.get('email')
|
54
52
|
address = oidc.get('address')
|
data/example.rb
CHANGED
@@ -3,38 +3,38 @@ require 'erb'
|
|
3
3
|
require 'openid_connect_client'
|
4
4
|
|
5
5
|
TEMPLATE = DATA.read.freeze
|
6
|
-
|
7
|
-
class App < NYNY::App
|
6
|
+
|
7
|
+
class App < NYNY::App
|
8
8
|
use Rack::Session::Cookie, :secret => 'your_secret'
|
9
|
-
|
9
|
+
|
10
10
|
get '/' do
|
11
11
|
oidc = get_client()
|
12
12
|
oidc.authorize()
|
13
|
-
|
13
|
+
|
14
14
|
session[:state] = oidc.state
|
15
15
|
redirect_to(oidc.auth_endpoint)
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
get '/callback' do
|
19
19
|
oidc = get_client(params)
|
20
20
|
oidc.authenticate()
|
21
|
-
|
21
|
+
|
22
22
|
email = oidc.get('email')
|
23
23
|
given_name = oidc.get('given_name')
|
24
24
|
address = oidc.get('address')
|
25
|
-
|
25
|
+
|
26
26
|
ERB.new(TEMPLATE).result(binding)
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
helpers do
|
30
30
|
def get_client(params = nil)
|
31
31
|
oidc = OpenIDConnectClient::Client.new('PROVIDER_ENDPOINT', 'CLIENT_ID', 'SECRET')
|
32
32
|
oidc.redirect_url = 'http://localhost:9292/callback'
|
33
33
|
oidc.scopes = 'openid email profile address phone'
|
34
|
-
|
34
|
+
|
35
35
|
oidc.state = session[:state]
|
36
36
|
oidc.params = params if params
|
37
|
-
|
37
|
+
|
38
38
|
oidc
|
39
39
|
end
|
40
40
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module OpenIDConnectClient
|
2
|
-
class OpenIDConnectClientException < Exception
|
2
|
+
class OpenIDConnectClientException < Exception
|
3
3
|
end
|
4
4
|
|
5
5
|
class Client
|
@@ -9,10 +9,10 @@ module OpenIDConnectClient
|
|
9
9
|
require 'base64'
|
10
10
|
require 'openssl'
|
11
11
|
require 'curb'
|
12
|
-
|
12
|
+
|
13
13
|
|
14
14
|
private #==============================================================================================================================
|
15
|
-
|
15
|
+
|
16
16
|
#
|
17
17
|
# Gets anything that we need configuration wise including endpoints, and other values
|
18
18
|
#
|
@@ -20,47 +20,46 @@ module OpenIDConnectClient
|
|
20
20
|
# @throws OpenIDConnectClientException
|
21
21
|
#
|
22
22
|
def get_provider_config()
|
23
|
-
|
24
23
|
well_known_config_response = fetch_url(@well_known_config_url).body_str
|
25
|
-
|
24
|
+
|
26
25
|
unless well_known_config_response
|
27
26
|
raise OpenIDConnectClientException, "Unable to get provider configuration data. Make sure your provider has a well known configuration available."
|
28
27
|
end
|
29
|
-
|
28
|
+
|
30
29
|
values = JSON[well_known_config_response]
|
31
|
-
|
30
|
+
|
32
31
|
values.each do |key, value|
|
33
32
|
@state[key.to_sym] = value
|
34
33
|
end
|
35
34
|
end
|
36
35
|
|
37
|
-
#
|
36
|
+
#
|
38
37
|
# @param param
|
39
38
|
# @throws OpenIDConnectClientException
|
40
39
|
# @return string
|
41
40
|
#
|
42
41
|
def get_provider_config_value(param)
|
43
42
|
# If the configuration value is not available, attempt to fetch it from a well known config endpoint
|
44
|
-
# This is also known as auto "discovery"
|
43
|
+
# This is also known as auto "discovery"
|
45
44
|
if @state[param].nil?
|
46
45
|
well_known_config_response = fetch_url(@well_known_config_url).body_str
|
47
|
-
|
46
|
+
|
48
47
|
unless well_known_config_response
|
49
48
|
raise OpenIDConnectClientException, "Unable to get provider configuration data. Make sure your provider has a well known configuration available."
|
50
49
|
end
|
51
|
-
|
52
|
-
value = JSON[well_known_config_response][param.to_s]
|
53
|
-
|
50
|
+
|
51
|
+
value = JSON[well_known_config_response][param.to_s]
|
52
|
+
|
54
53
|
unless value
|
55
54
|
raise OpenIDConnectClientException, "The provider #{param} has not been set. Make sure your provider has a well known configuration available."
|
56
55
|
end
|
57
|
-
|
56
|
+
|
58
57
|
@state[param] = value
|
59
58
|
end
|
60
|
-
|
59
|
+
|
61
60
|
@state[param]
|
62
61
|
end
|
63
|
-
|
62
|
+
|
64
63
|
#
|
65
64
|
# @param array keys
|
66
65
|
# @param array header
|
@@ -73,14 +72,14 @@ module OpenIDConnectClient
|
|
73
72
|
return key
|
74
73
|
end
|
75
74
|
end
|
76
|
-
|
75
|
+
|
77
76
|
if header["kid"]
|
78
77
|
raise OpenIDConnectClientException, "Unable to find a key for (algorithm, kid): #{header["alg"]}, #{header["kid"]}."
|
79
78
|
else
|
80
79
|
raise OpenIDConnectClientException, "Unable to find a key for RSA."
|
81
80
|
end
|
82
81
|
end
|
83
|
-
|
82
|
+
|
84
83
|
#
|
85
84
|
# @param jwt string encoded JWT
|
86
85
|
# @throws OpenIDConnectClientException
|
@@ -89,21 +88,21 @@ module OpenIDConnectClient
|
|
89
88
|
def verify_JWT_signature(jwt)
|
90
89
|
parts = jwt.split(".")
|
91
90
|
signature = decode_64(parts.pop())
|
92
|
-
|
91
|
+
|
93
92
|
decoded_header = decode_64(parts[0])
|
94
93
|
header = JSON[decoded_header]
|
95
|
-
|
94
|
+
|
96
95
|
payload = parts.join(".")
|
97
|
-
|
96
|
+
|
98
97
|
fetched_jwks = fetch_url(get_provider_config_value(:jwks_uri)).body_str
|
99
98
|
jwks = JSON[fetched_jwks]
|
100
|
-
|
99
|
+
|
101
100
|
unless not jwks.nil?
|
102
101
|
raise OpenIDConnectClientException, "Error decoding JSON from jwks_uri."
|
103
102
|
end
|
104
|
-
|
103
|
+
|
105
104
|
verified = false
|
106
|
-
|
105
|
+
|
107
106
|
case header["alg"]
|
108
107
|
when 'RS256', 'RS384', 'RS512'
|
109
108
|
hashtype = "sha" + header["alg"][0,2]
|
@@ -111,10 +110,10 @@ module OpenIDConnectClient
|
|
111
110
|
else
|
112
111
|
raise OpenIDConnectClientException, "No support for signature type: #{header["alg"]}."
|
113
112
|
end
|
114
|
-
|
113
|
+
|
115
114
|
verified
|
116
115
|
end
|
117
|
-
|
116
|
+
|
118
117
|
#
|
119
118
|
# @param string hashtype
|
120
119
|
# @param object key
|
@@ -124,7 +123,7 @@ module OpenIDConnectClient
|
|
124
123
|
unless key["n"] and key["e"]
|
125
124
|
raise OpenIDConnectClientException, "Malformed key object."
|
126
125
|
end
|
127
|
-
|
126
|
+
|
128
127
|
digest = case hashtype
|
129
128
|
when 'md2' then OpenSSL::Digest::MD2.new
|
130
129
|
when 'md5' then OpenSSL::Digest::MD5.new
|
@@ -134,11 +133,11 @@ module OpenIDConnectClient
|
|
134
133
|
when 'sha512' then OpenSSL::Digest::SHA512.new
|
135
134
|
else OpenSSL::Digest::SHA256.new
|
136
135
|
end
|
137
|
-
|
136
|
+
|
138
137
|
key = get_rsa_key(url_safe_base64(key["n"]), url_safe_base64(key["e"]))
|
139
138
|
key.public_key.verify(digest, signature, payload)
|
140
139
|
end
|
141
|
-
|
140
|
+
|
142
141
|
#
|
143
142
|
# @param object claims
|
144
143
|
# @return bool
|
@@ -150,7 +149,7 @@ module OpenIDConnectClient
|
|
150
149
|
return (claims["iss"] == @provider_url and ((claims["aud"] == @client_id) or (claims["aud"].include? @client_id)))
|
151
150
|
end
|
152
151
|
end
|
153
|
-
|
152
|
+
|
154
153
|
#
|
155
154
|
# @param jwt string encoded JWT
|
156
155
|
# @param int section the section we would like to decode
|
@@ -159,13 +158,13 @@ module OpenIDConnectClient
|
|
159
158
|
def decode_JWT(jwt, section = 0)
|
160
159
|
parts = jwt.split(".")
|
161
160
|
url_decoded = decode_64(parts[section])
|
162
|
-
|
161
|
+
|
163
162
|
JSON[url_decoded]
|
164
163
|
end
|
165
|
-
|
164
|
+
|
166
165
|
|
167
166
|
# Utility methods ==================================================================================================================
|
168
|
-
|
167
|
+
|
169
168
|
#
|
170
169
|
# @param string json
|
171
170
|
# @return bool
|
@@ -178,7 +177,7 @@ module OpenIDConnectClient
|
|
178
177
|
return false
|
179
178
|
end
|
180
179
|
end
|
181
|
-
|
180
|
+
|
182
181
|
#
|
183
182
|
# @param string json
|
184
183
|
# @return bool
|
@@ -191,7 +190,7 @@ module OpenIDConnectClient
|
|
191
190
|
return false
|
192
191
|
end
|
193
192
|
end
|
194
|
-
|
193
|
+
|
195
194
|
#
|
196
195
|
# @param object object
|
197
196
|
# @return string
|
@@ -203,22 +202,24 @@ module OpenIDConnectClient
|
|
203
202
|
h.keys.sort.each do |key|
|
204
203
|
result << (CGI.escape(key) + '=' + CGI.escape(h[key]) + separator)
|
205
204
|
end
|
206
|
-
|
205
|
+
|
207
206
|
result = result.sub(/#{separator}$/, '') # Remove the trailing k-v separator
|
208
207
|
return result
|
209
208
|
end
|
210
|
-
|
209
|
+
|
210
|
+
#
|
211
|
+
# From https://github.com/jof/php_http_build_query by Jonathan Lassoff
|
211
212
|
#
|
212
213
|
# @param object object
|
213
214
|
# @param string parent_key
|
214
215
|
#
|
215
216
|
def hashify(object, parent_key = '')
|
216
217
|
unless object.is_a?(Hash) or object.is_a?(Array) or parent_key.length > 0
|
217
|
-
raise ArgumentError.new('This is made for serializing Hashes and Arrays only.')
|
218
|
+
raise ArgumentError.new('This is made for serializing Hashes and Arrays only.')
|
218
219
|
end
|
219
220
|
|
220
221
|
result = {}
|
221
|
-
|
222
|
+
|
222
223
|
case object
|
223
224
|
when String, Symbol, Numeric
|
224
225
|
result[parent_key] = object.to_s
|
@@ -232,26 +233,26 @@ module OpenIDConnectClient
|
|
232
233
|
else
|
233
234
|
new_parent_key = parent_key + '[' + key.to_s + ']'
|
234
235
|
end
|
235
|
-
|
236
|
+
|
236
237
|
hashify(value, new_parent_key)
|
237
238
|
end
|
238
|
-
|
239
|
+
|
239
240
|
hash = hashes.reduce { |memo, hash| memo.merge hash }
|
240
241
|
result.merge! hash
|
241
242
|
when Enumerable
|
242
243
|
# _Very_ similar to above, but iterating with "each_with_index"
|
243
244
|
hashes = {}
|
244
245
|
object.each_with_index do |value, index|
|
245
|
-
|
246
|
+
|
246
247
|
if parent_key.length == 0
|
247
248
|
new_parent_key = index.to_s
|
248
249
|
else
|
249
250
|
new_parent_key = parent_key + '[' + index.to_s + ']'
|
250
251
|
end
|
251
|
-
|
252
|
+
|
252
253
|
hashes.merge! hashify(value, new_parent_key)
|
253
254
|
end
|
254
|
-
|
255
|
+
|
255
256
|
result.merge! hashes
|
256
257
|
else
|
257
258
|
raise Exception.new("This should only be serializing Strings, Symbols, or Numerics.")
|
@@ -259,7 +260,7 @@ module OpenIDConnectClient
|
|
259
260
|
|
260
261
|
return result
|
261
262
|
end
|
262
|
-
|
263
|
+
|
263
264
|
#
|
264
265
|
# @param string str
|
265
266
|
# @return string
|
@@ -267,11 +268,11 @@ module OpenIDConnectClient
|
|
267
268
|
def decode_64(str)
|
268
269
|
Base64.decode64(url_safe_base64(str))
|
269
270
|
end
|
270
|
-
|
271
|
-
#
|
272
|
-
# Per RFC4648, "base64 encoding with URL-safe and filename-safe alphabet". This just replaces characters 62 and 63.
|
271
|
+
|
272
|
+
#
|
273
|
+
# Per RFC4648, "base64 encoding with URL-safe and filename-safe alphabet". This just replaces characters 62 and 63.
|
273
274
|
# None of the reference implementations seem to restore the padding if necessary, but we'll do it anyway.
|
274
|
-
#
|
275
|
+
#
|
275
276
|
# @param string str
|
276
277
|
# @return string
|
277
278
|
#
|
@@ -282,30 +283,30 @@ module OpenIDConnectClient
|
|
282
283
|
when 3 then str + '='
|
283
284
|
else str
|
284
285
|
end
|
285
|
-
|
286
|
+
|
286
287
|
str.tr('-_', '+/')
|
287
288
|
end
|
288
|
-
|
289
|
+
|
289
290
|
#
|
290
291
|
# @param string xml_string
|
291
292
|
# @return object
|
292
293
|
#
|
293
294
|
def get_rsa_key(modulus, exponent)
|
294
|
-
#d = XML::Parser.string(xml_string).parse
|
295
|
+
# d = XML::Parser.string(xml_string).parse
|
295
296
|
m = Base64.decode64(modulus).unpack('H*')
|
296
297
|
e = Base64.decode64(exponent).unpack('H*')
|
297
298
|
|
298
299
|
pub_key = OpenSSL::PKey::RSA.new
|
299
|
-
|
300
|
-
#modules
|
300
|
+
|
301
|
+
# modules
|
301
302
|
pub_key.n = OpenSSL::BN.new(m[0].hex.to_s)
|
302
|
-
|
303
|
-
#exponent
|
303
|
+
|
304
|
+
# exponent
|
304
305
|
pub_key.e = OpenSSL::BN.new(e[0].hex.to_s)
|
305
|
-
|
306
|
-
#return Public Key
|
306
|
+
|
307
|
+
# return Public Key
|
307
308
|
pub_key
|
308
|
-
end
|
309
|
+
end
|
309
310
|
|
310
311
|
#
|
311
312
|
# Used for arbitrary value generation for nonces and state
|
@@ -315,7 +316,7 @@ module OpenIDConnectClient
|
|
315
316
|
def random_string()
|
316
317
|
SecureRandom.urlsafe_base64
|
317
318
|
end
|
318
|
-
|
319
|
+
|
319
320
|
#
|
320
321
|
# @param url
|
321
322
|
# @param null post_body string If this is set the post type will be POST
|
@@ -324,21 +325,20 @@ module OpenIDConnectClient
|
|
324
325
|
# @return mixed
|
325
326
|
#
|
326
327
|
def fetch_url(url, post_body = nil, headers = Array.new)
|
327
|
-
curb = Curl::Easy.new(url) do |curl|
|
328
|
+
curb = Curl::Easy.new(url) do |curl|
|
328
329
|
headers.each do |key, value|
|
329
330
|
curl.headers[key] = value
|
330
331
|
end
|
331
|
-
|
332
|
-
if post_body
|
332
|
+
|
333
|
+
if post_body
|
333
334
|
if is_json?(post_body)
|
334
335
|
content_type = "application/json"
|
335
336
|
else
|
336
337
|
content_type = "application/x-www-form-urlencoded"
|
337
338
|
end
|
338
|
-
|
339
|
+
|
339
340
|
curl.headers["Content-Type"] = content_type
|
340
341
|
curl.post_body = post_body
|
341
|
-
|
342
342
|
else
|
343
343
|
curl.http(:GET)
|
344
344
|
end
|
@@ -346,7 +346,7 @@ module OpenIDConnectClient
|
|
346
346
|
curl.timeout = 60
|
347
347
|
curl.proxy_url = @proxy_url if self.instance_variable_defined? :@proxy_url
|
348
348
|
curl.verbose = true
|
349
|
-
|
349
|
+
|
350
350
|
if self.instance_variable_defined? :@cert_path
|
351
351
|
curl.ssl_verify_peer = true
|
352
352
|
curl.ssl_verify_host = true
|
@@ -355,24 +355,24 @@ module OpenIDConnectClient
|
|
355
355
|
curl.ssl_verify_peer = false
|
356
356
|
end
|
357
357
|
end
|
358
|
-
|
358
|
+
|
359
359
|
curb.post_body = post_body if post_body
|
360
360
|
result = curb.perform
|
361
|
-
|
361
|
+
|
362
362
|
if result
|
363
363
|
return curb
|
364
364
|
else
|
365
365
|
return false
|
366
366
|
end
|
367
367
|
end
|
368
|
-
|
369
|
-
|
368
|
+
|
369
|
+
|
370
370
|
public #==============================================================================================================================
|
371
|
-
|
371
|
+
|
372
372
|
attr_reader :access_token, :refresh_token, :auth_endpoint
|
373
373
|
attr_writer :http_proxy, :cert_path, :params
|
374
374
|
attr_accessor :client_name, :client_id, :client_secret, :well_known_config_url, :state, :provider_config
|
375
|
-
|
375
|
+
|
376
376
|
#
|
377
377
|
# @param provider_url string optional
|
378
378
|
# @param client_id string optional
|
@@ -386,38 +386,38 @@ module OpenIDConnectClient
|
|
386
386
|
@user_info = Hash.new
|
387
387
|
@params = Hash.new
|
388
388
|
@response = Hash.new
|
389
|
-
|
389
|
+
|
390
390
|
@client_id = client_id
|
391
391
|
@client_secret = client_secret
|
392
392
|
@provider_url = provider_url
|
393
|
-
|
393
|
+
|
394
394
|
substitute = "/"
|
395
|
-
|
395
|
+
|
396
396
|
if self.instance_variable_defined? :@provider_url
|
397
397
|
@well_known_config_url = provider_url.gsub(/[#{substitute}]+$/, '') + "/.well-known/openid-configuration/"
|
398
398
|
end
|
399
399
|
end
|
400
|
-
|
401
|
-
#
|
400
|
+
|
401
|
+
#
|
402
402
|
# Builds the user authentication url.
|
403
403
|
#
|
404
404
|
# @return void
|
405
|
-
#
|
406
|
-
def authorize()
|
405
|
+
#
|
406
|
+
def authorize()
|
407
407
|
get_provider_config()
|
408
|
-
|
408
|
+
|
409
409
|
auth_endpoint = get_provider_config_value(:authorization_endpoint)
|
410
410
|
response_type = "code"
|
411
|
-
|
411
|
+
|
412
412
|
# Generate and store a nonce in the session
|
413
413
|
# The nonce is an arbitrary value
|
414
414
|
nonce = random_string()
|
415
415
|
@state["openid_connect_nonce"] = nonce
|
416
|
-
|
416
|
+
|
417
417
|
# State essentially acts as a session key for OIDC
|
418
418
|
state = random_string()
|
419
419
|
@state["openid_connect_state"] = state
|
420
|
-
|
420
|
+
|
421
421
|
@auth_params = @auth_params.merge({
|
422
422
|
response_type: response_type,
|
423
423
|
redirect_uri: @redirect_url,
|
@@ -426,7 +426,7 @@ module OpenIDConnectClient
|
|
426
426
|
state: state,
|
427
427
|
scope: 'openid'
|
428
428
|
})
|
429
|
-
|
429
|
+
|
430
430
|
# If the client has been registered with additional scopes
|
431
431
|
if @scopes.length > 0
|
432
432
|
@auth_params[:scope] = @scopes.join(' ')
|
@@ -434,83 +434,82 @@ module OpenIDConnectClient
|
|
434
434
|
@auth_endpoint = auth_endpoint
|
435
435
|
end
|
436
436
|
end
|
437
|
-
|
438
|
-
#
|
437
|
+
|
438
|
+
#
|
439
439
|
# Gets the access token needed to request user info.
|
440
440
|
#
|
441
441
|
# @return bool
|
442
442
|
# @throws OpenIDConnectClientException
|
443
|
-
#
|
443
|
+
#
|
444
444
|
def authenticate()
|
445
445
|
# Do a preemptive check to see if the provider has raised an error from a previous redirect
|
446
446
|
unless @response[:error].nil?
|
447
447
|
raise OpenIDConnectClientException, "Error: #{@response[:error]} Description: #{@response[:error_description]}"
|
448
448
|
end
|
449
|
-
|
449
|
+
|
450
450
|
# If we have an authorization code then proceed to request a token
|
451
451
|
if not @params["code"].nil? || @params["code"].empty?
|
452
452
|
code = @params["code"]
|
453
453
|
token_endpoint = get_provider_config_value(:token_endpoint)
|
454
454
|
grant_type = "authorization_code"
|
455
|
-
|
456
|
-
|
455
|
+
|
456
|
+
token_params = {
|
457
457
|
grant_type: grant_type,
|
458
458
|
code: code,
|
459
459
|
redirect_uri: @redirect_url,
|
460
460
|
client_id: @client_id,
|
461
461
|
client_secret: @client_secret
|
462
462
|
}
|
463
|
-
|
463
|
+
|
464
464
|
# Convert token params to string format
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
465
|
+
token_params = http_build_query(token_params)
|
466
|
+
token_data = fetch_url(token_endpoint, token_params).body_str
|
467
|
+
|
469
468
|
unless token_data
|
470
469
|
raise OpenIDConnectClientException, "Unable to get token data from the provider."
|
471
470
|
end
|
472
|
-
|
471
|
+
|
473
472
|
token_json = JSON[token_data]
|
474
|
-
|
473
|
+
|
475
474
|
# Throw an error if the server returns one
|
476
475
|
if token_json["error"]
|
477
476
|
raise OpenIDConnectClientException, token_json["error_description"]
|
478
477
|
end
|
479
|
-
|
478
|
+
|
480
479
|
# Do an OpenID Connect session check
|
481
480
|
unless @params["state"] == @state["openid_connect_state"]
|
482
481
|
raise OpenIDConnectClientException, "Unable to determine state."
|
483
482
|
end
|
484
|
-
|
483
|
+
|
485
484
|
unless token_json["id_token"]
|
486
485
|
raise OpenIDConnectClientException, "User did not authorize openid scope."
|
487
486
|
end
|
488
|
-
|
487
|
+
|
489
488
|
# Verify the signature
|
490
489
|
unless verify_JWT_signature(token_json["id_token"])
|
491
490
|
raise OpenIDConnectClientException, "Unable to verify signature."
|
492
491
|
end
|
493
|
-
|
492
|
+
|
494
493
|
claims = decode_JWT(token_json["id_token"], 1)
|
495
|
-
|
494
|
+
|
496
495
|
# If this is a valid claim
|
497
496
|
unless verify_JWT_claims(claims)
|
498
497
|
raise OpenIDConnectClientException, "Unable to verify JWT claims."
|
499
498
|
end
|
500
|
-
|
499
|
+
|
501
500
|
# Save the access token
|
502
501
|
@access_token = token_json["access_token"]
|
503
|
-
|
502
|
+
|
504
503
|
# Save the refresh token, if we got one
|
505
504
|
if token_json["refresh_token"]
|
506
505
|
@refresh_token = token_json["refresh_token"]
|
507
506
|
end
|
508
|
-
|
507
|
+
|
509
508
|
# Success!
|
510
509
|
return true
|
511
510
|
end
|
512
511
|
end
|
513
|
-
|
512
|
+
|
514
513
|
#
|
515
514
|
# @param attribute
|
516
515
|
#
|
@@ -542,60 +541,60 @@ module OpenIDConnectClient
|
|
542
541
|
if @user_info.include? attribute
|
543
542
|
return @user_info["#{attribute}"]
|
544
543
|
end
|
545
|
-
|
544
|
+
|
546
545
|
user_info_endpoint = get_provider_config_value(:userinfo_endpoint)
|
547
546
|
schema = "openid"
|
548
547
|
user_info_endpoint += "?schema=#{schema}"
|
549
548
|
headers = {"Authorization" => "Bearer #{@access_token}"}
|
550
549
|
user_data = fetch_url(user_info_endpoint, nil, headers).body_str
|
551
|
-
|
550
|
+
|
552
551
|
if user_data.nil? || user_data.empty?
|
553
552
|
raise OpenIDConnectClientException, "Unable to get #{attribute} from the provider."
|
554
553
|
end
|
555
|
-
|
554
|
+
|
556
555
|
user_json = JSON[user_data]
|
557
556
|
@user_info = user_json
|
558
|
-
|
557
|
+
|
559
558
|
if @user_info.include? attribute
|
560
559
|
return @user_info["#{attribute}"]
|
561
560
|
end
|
562
|
-
|
561
|
+
|
563
562
|
return nil
|
564
563
|
end
|
565
|
-
|
564
|
+
|
566
565
|
#
|
567
566
|
# Dynamic registration
|
568
|
-
#
|
567
|
+
#
|
569
568
|
# @return void
|
570
569
|
# @throws OpenIDConnectClientException
|
571
570
|
#
|
572
571
|
def register()
|
573
572
|
registration_endpoint = get_provider_config_value(:registration_endpoint)
|
574
|
-
|
573
|
+
|
575
574
|
send_object = {
|
576
575
|
redirect_uris: [@redirect_url],
|
577
576
|
client_name: @client_name
|
578
577
|
}
|
579
|
-
|
578
|
+
|
580
579
|
@response = fetch_url(registration_endpoint, JSON[send_object])
|
581
580
|
json_response = JSON[response]
|
582
|
-
|
581
|
+
|
583
582
|
if not json_response
|
584
583
|
raise OpenIDConnectClientException, "Error registering: JSON response received from the server was invalid."
|
585
584
|
elsif json_response[:error_description]
|
586
585
|
raise OpenIDConnectClientException, json_response[:error_description]
|
587
586
|
end
|
588
|
-
|
587
|
+
|
589
588
|
if json_response[:client_id]
|
590
589
|
@client_secret = json_response[:client_id]
|
591
590
|
else
|
592
591
|
raise OpenIDConnectClientException, "Error registering: Please contact the OpenID Connect provider and obtain a Client ID and Secret directly from them."
|
593
592
|
end
|
594
593
|
end
|
595
|
-
|
596
|
-
|
594
|
+
|
595
|
+
|
597
596
|
# Getters/Setters ==================================================================================================================
|
598
|
-
|
597
|
+
|
599
598
|
#
|
600
599
|
# @param hash hash
|
601
600
|
# @return hash
|
@@ -603,7 +602,7 @@ module OpenIDConnectClient
|
|
603
602
|
def add_auth_param(hash)
|
604
603
|
@auth_params = @auth_params.merge(hash)
|
605
604
|
end
|
606
|
-
|
605
|
+
|
607
606
|
#
|
608
607
|
# @param hash hash
|
609
608
|
# @return hash
|
@@ -611,14 +610,14 @@ module OpenIDConnectClient
|
|
611
610
|
def add_provider_config_param(hash)
|
612
611
|
@state = @state.merge(hash)
|
613
612
|
end
|
614
|
-
|
613
|
+
|
615
614
|
#
|
616
615
|
# @param scopes - example: openid, given_name, etc...
|
617
616
|
#
|
618
|
-
def scopes=(scopes)
|
617
|
+
def scopes=(scopes)
|
619
618
|
@scopes = scopes.split(' ') if scopes
|
620
619
|
end
|
621
|
-
|
620
|
+
|
622
621
|
#
|
623
622
|
# @param hash state
|
624
623
|
# @return hash
|
@@ -626,7 +625,7 @@ module OpenIDConnectClient
|
|
626
625
|
def state=(state)
|
627
626
|
@state = @state.merge(state) if state
|
628
627
|
end
|
629
|
-
|
628
|
+
|
630
629
|
#
|
631
630
|
# @return string
|
632
631
|
# @throws OpenIDConnectClientException
|
@@ -639,7 +638,7 @@ module OpenIDConnectClient
|
|
639
638
|
|
640
639
|
@provider_url
|
641
640
|
end
|
642
|
-
|
641
|
+
|
643
642
|
#
|
644
643
|
# @param provider_url
|
645
644
|
# @return string
|
@@ -652,7 +651,7 @@ module OpenIDConnectClient
|
|
652
651
|
|
653
652
|
@state[:issuer] = url
|
654
653
|
end
|
655
|
-
|
654
|
+
|
656
655
|
#
|
657
656
|
# Gets the URL of the current page we are on, encodes, and returns it
|
658
657
|
#
|
@@ -667,7 +666,7 @@ module OpenIDConnectClient
|
|
667
666
|
|
668
667
|
@redirect_url
|
669
668
|
end
|
670
|
-
|
669
|
+
|
671
670
|
#
|
672
671
|
# @param url Sets redirect URL for auth flow
|
673
672
|
# @return string
|
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Rita Zerrizuela"]
|
10
10
|
spec.email = ["zeta@widcket.com"]
|
11
11
|
|
12
|
-
spec.summary = %q{An easy to use OpenID Connect Client for Ruby
|
13
|
-
spec.description = %q{
|
12
|
+
spec.summary = %q{An easy to use OpenID Connect Client for Ruby}
|
13
|
+
spec.description = %q{An easy to use OpenID Connect Client for Ruby}
|
14
14
|
spec.homepage = "https://github.com/LabGCBA/openid-connect-ruby.git"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
@@ -22,6 +22,6 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_development_dependency "bundler", "~> 1.11"
|
23
23
|
spec.add_development_dependency "rake", "~> 10.0"
|
24
24
|
spec.add_development_dependency "rspec", "~> 3.0"
|
25
|
-
|
25
|
+
|
26
26
|
spec.add_runtime_dependency "curb", '~> 0'
|
27
27
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: openid_connect_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rita Zerrizuela
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-07-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -66,9 +66,7 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
-
description:
|
70
|
-
the due to the different nature of Ruby and PHP, usage is not an exact match. See
|
71
|
-
Readme.
|
69
|
+
description: An easy to use OpenID Connect Client for Ruby
|
72
70
|
email:
|
73
71
|
- zeta@widcket.com
|
74
72
|
executables: []
|
@@ -109,8 +107,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
107
|
version: '0'
|
110
108
|
requirements: []
|
111
109
|
rubyforge_project:
|
112
|
-
rubygems_version: 2.
|
110
|
+
rubygems_version: 2.6.6
|
113
111
|
signing_key:
|
114
112
|
specification_version: 4
|
115
|
-
summary: An easy to use OpenID Connect Client for Ruby
|
113
|
+
summary: An easy to use OpenID Connect Client for Ruby
|
116
114
|
test_files: []
|