cf-uaa-lib 4.0.2 → 4.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b74dffd99890350b0ed57b02a6a79343e47967be405bf6fc48e0630f534e6d3
4
- data.tar.gz: 229fb7742d1503f18bb6a9097741c11b4de378e68e9ea66f198645031d92d381
3
+ metadata.gz: 9eebcd4d0b7e5068266fceab3b4686335b5446bb18a6bf4f0a68139ee958dcf6
4
+ data.tar.gz: d010ba705cca4e1c78c87b782e17faab10f3c3f1008a5337ff2345634053e0bc
5
5
  SHA512:
6
- metadata.gz: 0d2b41fbb96a3fbf94aa5e27ea7f5f408bd7c23c7555736a01750970b5a52672ae9318376a7615141ab2a777def229d587a431f887be67247747bc230b18a2fd
7
- data.tar.gz: 5ccb01cb63301c8abf33a11a1fbb3bba8e3ccc40d200e8b7ae12106cb8a6320c4ba5be912858cbfa25dadde377e2b859aaad8c60dcd27673d046801b40be4564
6
+ metadata.gz: fedcc696e77cd58fb6a53b24d1eb24482c487e239bbf3e1c6470c66480f5e7f29fc8bd4ab178deb13c010cac2284a21a191c8025870961c4182ffcf212c03c03
7
+ data.tar.gz: 10a6ca261845b1d773b941da11fc1abffa37ee910fe1175bc5fa9a716a213ab38c70056e44d3d73e2a891ce9c3747243f00342d0a8c3ff8076e373ebeceb3b42
@@ -11,7 +11,7 @@ jobs:
11
11
  packages: write
12
12
 
13
13
  steps:
14
- - uses: actions/checkout@v3
14
+ - uses: actions/checkout@v4
15
15
  - name: Set up Ruby 2.6
16
16
  uses: ruby/setup-ruby@v1
17
17
  with:
@@ -15,7 +15,7 @@ jobs:
15
15
  ruby-version: ['2.5', '2.7', '3.0', '3.1', '3.2']
16
16
 
17
17
  steps:
18
- - uses: actions/checkout@v3
18
+ - uses: actions/checkout@v4
19
19
  - name: Set up Ruby
20
20
  uses: ruby/setup-ruby@v1
21
21
  with:
@@ -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
@@ -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 @basic_auth
78
- headers['authorization'] = Http.basic_auth(@client_id, @client_secret)
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
- request_token(grant_type: 'authorization_code', code: authcode,
239
- redirect_uri: ac_params['redirect_uri'])
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
@@ -14,6 +14,6 @@
14
14
  # Cloud Foundry namespace
15
15
  module CF
16
16
  module UAA
17
- VERSION = '4.0.2'
17
+ VERSION = '4.0.3'
18
18
  end
19
19
  end
@@ -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.2
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: 2023-05-10 00:00:00.000000000 Z
15
+ date: 2023-09-08 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: multi_json
@@ -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