cf-uaa-lib 4.0.1 → 4.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/gem-push.yml +3 -3
- data/.github/workflows/ruby.yml +2 -2
- data/cf-uaa-lib.gemspec +2 -2
- data/examples/authorization_grant_public_pkce.rb +42 -0
- data/lib/uaa/token_issuer.rb +41 -4
- data/lib/uaa/version.rb +1 -1
- data/spec/token_issuer_spec.rb +35 -0
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9eebcd4d0b7e5068266fceab3b4686335b5446bb18a6bf4f0a68139ee958dcf6
|
4
|
+
data.tar.gz: d010ba705cca4e1c78c87b782e17faab10f3c3f1008a5337ff2345634053e0bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fedcc696e77cd58fb6a53b24d1eb24482c487e239bbf3e1c6470c66480f5e7f29fc8bd4ab178deb13c010cac2284a21a191c8025870961c4182ffcf212c03c03
|
7
|
+
data.tar.gz: 10a6ca261845b1d773b941da11fc1abffa37ee910fe1175bc5fa9a716a213ab38c70056e44d3d73e2a891ce9c3747243f00342d0a8c3ff8076e373ebeceb3b42
|
data/.github/dependabot.yml
CHANGED
@@ -11,11 +11,11 @@ jobs:
|
|
11
11
|
packages: write
|
12
12
|
|
13
13
|
steps:
|
14
|
-
- uses: actions/checkout@
|
14
|
+
- uses: actions/checkout@v4
|
15
15
|
- name: Set up Ruby 2.6
|
16
|
-
uses:
|
16
|
+
uses: ruby/setup-ruby@v1
|
17
17
|
with:
|
18
|
-
ruby-version: 2.6
|
18
|
+
ruby-version: '2.6'
|
19
19
|
|
20
20
|
- name: Publish to RubyGems
|
21
21
|
run: |
|
data/.github/workflows/ruby.yml
CHANGED
@@ -12,10 +12,10 @@ jobs:
|
|
12
12
|
runs-on: ubuntu-latest
|
13
13
|
strategy:
|
14
14
|
matrix:
|
15
|
-
ruby-version: ['2.5', '2.7', '3.0', '3.1']
|
15
|
+
ruby-version: ['2.5', '2.7', '3.0', '3.1', '3.2']
|
16
16
|
|
17
17
|
steps:
|
18
|
-
- uses: actions/checkout@
|
18
|
+
- uses: actions/checkout@v4
|
19
19
|
- name: Set up Ruby
|
20
20
|
uses: ruby/setup-ruby@v1
|
21
21
|
with:
|
data/cf-uaa-lib.gemspec
CHANGED
@@ -38,8 +38,8 @@ Gem::Specification.new do |s|
|
|
38
38
|
s.add_development_dependency 'bundler', '~> 2.2'
|
39
39
|
s.add_development_dependency 'rake', '>= 10.3.2', '~> 13.0'
|
40
40
|
s.add_development_dependency 'rspec', '>= 2.14.1', '~> 3.9'
|
41
|
-
s.add_development_dependency 'simplecov', '~> 0.
|
42
|
-
s.add_development_dependency 'simplecov-rcov', '~> 0.
|
41
|
+
s.add_development_dependency 'simplecov', '~> 0.22.0'
|
42
|
+
s.add_development_dependency 'simplecov-rcov', '~> 0.3.0'
|
43
43
|
s.add_development_dependency 'ci_reporter', '>= 1.9.2', '~> 2.0'
|
44
44
|
s.add_development_dependency 'json_pure', '>= 1.8.1', '~> 2.5'
|
45
45
|
s.add_development_dependency 'ci_reporter_rspec', '~> 1.0'
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Start a develop UAA with default profile or add client with allowpublic=true
|
4
|
+
# uaac client add login -s loginsecret \
|
5
|
+
# --authorized_grant_types authorization_code,refresh_token \
|
6
|
+
# --scope "openid" \
|
7
|
+
# --authorities uaa.none \
|
8
|
+
# --allowpublic true \
|
9
|
+
# --redirect_uri=http://localhost:7000/callback
|
10
|
+
|
11
|
+
require 'uaa'
|
12
|
+
require 'cgi'
|
13
|
+
|
14
|
+
url = ENV["UAA_URL"] || 'http://localhost:8080/uaa'
|
15
|
+
client = "login"
|
16
|
+
secret = nil
|
17
|
+
|
18
|
+
def show(title, object)
|
19
|
+
puts "#{title}: #{object.inspect}"
|
20
|
+
puts
|
21
|
+
end
|
22
|
+
|
23
|
+
uaa_options = { skip_ssl_validation: true, use_pkce:true, client_auth_method: 'none'}
|
24
|
+
uaa_options[:ssl_ca_file] = ENV["UAA_CA_CERT_FILE"] if ENV["UAA_CA_CERT_FILE"]
|
25
|
+
show "uaa_options", uaa_options
|
26
|
+
|
27
|
+
uaa_info = CF::UAA::Info.new(url, uaa_options)
|
28
|
+
show "UAA server info", uaa_info.server
|
29
|
+
|
30
|
+
token_issuer = CF::UAA::TokenIssuer.new(url, client, secret, uaa_options)
|
31
|
+
auth_uri = token_issuer.authcode_uri("http://localhost:7000/callback", nil)
|
32
|
+
show "UAA authorization URL", auth_uri
|
33
|
+
|
34
|
+
puts "Enter Callback URL: "
|
35
|
+
callback_url = gets
|
36
|
+
show "Perform Token Request with: ", callback_url
|
37
|
+
|
38
|
+
token = token_issuer.authcode_grant(auth_uri, URI.parse(callback_url).query.to_s)
|
39
|
+
show "User authorization grant", token
|
40
|
+
|
41
|
+
token_info = CF::UAA::TokenCoder.decode(token.info["access_token"], nil, nil, false) #token signature not verified
|
42
|
+
show "Decoded access token", token_info
|
data/lib/uaa/token_issuer.rb
CHANGED
@@ -12,6 +12,7 @@
|
|
12
12
|
#++
|
13
13
|
|
14
14
|
require 'securerandom'
|
15
|
+
require "digest"
|
15
16
|
require 'uaa/http'
|
16
17
|
require 'cgi'
|
17
18
|
|
@@ -53,6 +54,7 @@ class TokenIssuer
|
|
53
54
|
include Http
|
54
55
|
|
55
56
|
private
|
57
|
+
@client_auth_method = 'client_secret_basic'
|
56
58
|
|
57
59
|
def random_state; SecureRandom.hex end
|
58
60
|
|
@@ -74,8 +76,15 @@ class TokenIssuer
|
|
74
76
|
params[:scope] = Util.strlist(scope)
|
75
77
|
end
|
76
78
|
headers = {'content-type' => FORM_UTF8, 'accept' => JSON_UTF8}
|
77
|
-
if @
|
78
|
-
|
79
|
+
if @client_auth_method == 'client_secret_basic' && @client_secret && @client_id
|
80
|
+
if @basic_auth
|
81
|
+
headers['authorization'] = Http.basic_auth(@client_id, @client_secret)
|
82
|
+
else
|
83
|
+
headers['X-CF-ENCODED-CREDENTIALS'] = 'true'
|
84
|
+
headers['authorization'] = Http.basic_auth(CGI.escape(@client_id), CGI.escape(@client_secret))
|
85
|
+
end
|
86
|
+
elsif @client_id && params[:code_verifier]
|
87
|
+
params[:client_id] = @client_id
|
79
88
|
else
|
80
89
|
headers['X-CF-ENCODED-CREDENTIALS'] = 'true'
|
81
90
|
headers['authorization'] = Http.basic_auth(CGI.escape(@client_id || ''), CGI.escape(@client_secret || ''))
|
@@ -91,6 +100,10 @@ class TokenIssuer
|
|
91
100
|
redirect_uri: redirect_uri, state: state)
|
92
101
|
params[:scope] = scope = Util.strlist(scope) if scope = Util.arglist(scope)
|
93
102
|
params[:nonce] = state
|
103
|
+
if not @code_verifier.nil?
|
104
|
+
params[:code_challenge] = get_challenge
|
105
|
+
params[:code_challenge_method] = 'S256'
|
106
|
+
end
|
94
107
|
"/oauth/authorize?#{Util.encode_form(params)}"
|
95
108
|
end
|
96
109
|
|
@@ -116,6 +129,11 @@ class TokenIssuer
|
|
116
129
|
@token_target = options[:token_target] || target
|
117
130
|
@key_style = options[:symbolize_keys] ? :sym : nil
|
118
131
|
@basic_auth = options[:basic_auth] == true ? true : false
|
132
|
+
@client_auth_method = options[:client_auth_method] || 'client_secret_basic'
|
133
|
+
@code_verifier = options[:code_verifier] || nil
|
134
|
+
if @code_verifier.nil? && options[:use_pkce] && options[:use_pkce] == true
|
135
|
+
@code_verifier = get_verifier
|
136
|
+
end
|
119
137
|
initialize_http_options(options)
|
120
138
|
end
|
121
139
|
|
@@ -235,8 +253,27 @@ class TokenIssuer
|
|
235
253
|
rescue URI::InvalidURIError, ArgumentError, BadResponse
|
236
254
|
raise BadResponse, "received invalid response from target #{@target}"
|
237
255
|
end
|
238
|
-
|
239
|
-
|
256
|
+
if not @code_verifier.nil?
|
257
|
+
request_token(grant_type: 'authorization_code', code: authcode,
|
258
|
+
redirect_uri: ac_params['redirect_uri'], code_verifier: @code_verifier)
|
259
|
+
else
|
260
|
+
request_token(grant_type: 'authorization_code', code: authcode,
|
261
|
+
redirect_uri: ac_params['redirect_uri'])
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# Generates a random verifier for PKCE usage
|
266
|
+
def get_verifier
|
267
|
+
if not @code_verifier.nil?
|
268
|
+
@verifier = @code_verifier
|
269
|
+
else
|
270
|
+
@verifier ||= SecureRandom.base64(96).tr("+/", "-_").tr("=", "")
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# Calculates the challenge from code_verifier
|
275
|
+
def get_challenge
|
276
|
+
@challenge ||= Digest::SHA256.base64digest(get_verifier).tr("+/", "-_").tr("=", "")
|
240
277
|
end
|
241
278
|
|
242
279
|
# Uses the instance client credentials in addition to the +username+
|
data/lib/uaa/version.rb
CHANGED
data/spec/token_issuer_spec.rb
CHANGED
@@ -264,6 +264,7 @@ describe TokenIssuer do
|
|
264
264
|
end
|
265
265
|
|
266
266
|
context 'with auth code grant' do
|
267
|
+
let(:options) { {use_pkce: true} }
|
267
268
|
|
268
269
|
it 'gets the authcode uri to be sent to the user agent for an authcode' do
|
269
270
|
redir_uri = 'http://call.back/uri_path'
|
@@ -275,6 +276,8 @@ describe TokenIssuer do
|
|
275
276
|
params['scope'].should == 'openid'
|
276
277
|
params['redirect_uri'].should == redir_uri
|
277
278
|
params['state'].should_not be_nil
|
279
|
+
params['code_challenge'].should =~ /^[0-9A-Za-z_-]{43}$/i
|
280
|
+
params['code_challenge_method'].should == 'S256'
|
278
281
|
end
|
279
282
|
|
280
283
|
it 'gets an access token with an authorization code' do
|
@@ -292,6 +295,10 @@ describe TokenIssuer do
|
|
292
295
|
cburi = 'http://call.back/uri_path'
|
293
296
|
redir_uri = subject.authcode_uri(cburi)
|
294
297
|
state = /state=([^&]+)/.match(redir_uri)[1]
|
298
|
+
challenge = /code_challenge=([^&]+)/.match(redir_uri)[1]
|
299
|
+
challenge.should =~ /^[0-9A-Za-z_-]{43}$/i
|
300
|
+
challenge_method = /code_challenge_method=([^&]+)/.match(redir_uri)[1]
|
301
|
+
challenge_method.should == 'S256'
|
295
302
|
reply_query = "state=#{state}&code=kz8%2F5gQZ2pc%3D"
|
296
303
|
token = subject.authcode_grant(redir_uri, reply_query)
|
297
304
|
token.should be_an_instance_of TokenInfo
|
@@ -303,6 +310,34 @@ describe TokenIssuer do
|
|
303
310
|
|
304
311
|
end
|
305
312
|
|
313
|
+
context 'pkce with own code verifier' do
|
314
|
+
let(:options) { {basic_auth: false, code_verifier: 'umoq1e_4XMYXvfHlaO9mSlSI17OKfxnwfR5ZD-oYreFxyn8yQZ-ZHPZfUZ4n3WjY_tkOB_MAisSy4ddqsa6aoTU5ZOcX4ps3de933PczYlC8pZpKL8EQWaDZOnpOyB2W'} }
|
315
|
+
|
316
|
+
it 'calculate code_challenge on existing verifier' do
|
317
|
+
redir_uri = 'http://call.back/uri_path'
|
318
|
+
uri_parts = subject.authcode_uri(redir_uri, 'openid').split('?')
|
319
|
+
code_challenge = subject.get_challenge
|
320
|
+
code_verifier = subject.get_verifier
|
321
|
+
params = Util.decode_form(uri_parts[1])
|
322
|
+
params['code_challenge'].should == code_challenge
|
323
|
+
params['code_challenge_method'].should == 'S256'
|
324
|
+
code_verifier.should == options[:code_verifier]
|
325
|
+
code_challenge.should == 'TAnM2AKGgiQKOC16cRpMdF_55qwmz3B333cq6T18z0s'
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
context 'no pkce active as this is the default' do
|
330
|
+
#let(:options) { {use_pkce: false} }
|
331
|
+
# by default PKCE is off
|
332
|
+
it 'no code pkce generation with an authorization code' do
|
333
|
+
redir_uri = 'http://call.back/uri_path'
|
334
|
+
uri_parts = subject.authcode_uri(redir_uri, 'openid').split('?')
|
335
|
+
params = Util.decode_form(uri_parts[1])
|
336
|
+
params['code_challenge'].should_not
|
337
|
+
params['code_challenge_method'].should_not
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
306
341
|
end
|
307
342
|
|
308
343
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cf-uaa-lib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.
|
4
|
+
version: 4.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dave Syer
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date:
|
15
|
+
date: 2023-09-08 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: multi_json
|
@@ -134,28 +134,28 @@ dependencies:
|
|
134
134
|
requirements:
|
135
135
|
- - "~>"
|
136
136
|
- !ruby/object:Gem::Version
|
137
|
-
version: 0.
|
137
|
+
version: 0.22.0
|
138
138
|
type: :development
|
139
139
|
prerelease: false
|
140
140
|
version_requirements: !ruby/object:Gem::Requirement
|
141
141
|
requirements:
|
142
142
|
- - "~>"
|
143
143
|
- !ruby/object:Gem::Version
|
144
|
-
version: 0.
|
144
|
+
version: 0.22.0
|
145
145
|
- !ruby/object:Gem::Dependency
|
146
146
|
name: simplecov-rcov
|
147
147
|
requirement: !ruby/object:Gem::Requirement
|
148
148
|
requirements:
|
149
149
|
- - "~>"
|
150
150
|
- !ruby/object:Gem::Version
|
151
|
-
version: 0.
|
151
|
+
version: 0.3.0
|
152
152
|
type: :development
|
153
153
|
prerelease: false
|
154
154
|
version_requirements: !ruby/object:Gem::Requirement
|
155
155
|
requirements:
|
156
156
|
- - "~>"
|
157
157
|
- !ruby/object:Gem::Version
|
158
|
-
version: 0.
|
158
|
+
version: 0.3.0
|
159
159
|
- !ruby/object:Gem::Dependency
|
160
160
|
name: ci_reporter
|
161
161
|
requirement: !ruby/object:Gem::Requirement
|
@@ -237,6 +237,7 @@ files:
|
|
237
237
|
- README.md
|
238
238
|
- Rakefile
|
239
239
|
- cf-uaa-lib.gemspec
|
240
|
+
- examples/authorization_grant_public_pkce.rb
|
240
241
|
- examples/password_grant_and_decode_token.rb
|
241
242
|
- lib/uaa.rb
|
242
243
|
- lib/uaa/http.rb
|