cf-uaa-lib 4.0.0 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e8a741ef102742885eff043d198628e3bc05f0bcc98c752a57ce5f6b3c18bf43
4
- data.tar.gz: 6ffc61520e49b04d65bd0404f577727986e3f625e6ef095fc8a0d361c50be5ab
3
+ metadata.gz: 283326148ca7b9df992d6ea50b3e0161ef74d07626147979c83a5c6e63daec86
4
+ data.tar.gz: b643f26e60fa73ccdee7e9fc7b89b3b3c010e9c9007be12e98e2f7493cc53a6d
5
5
  SHA512:
6
- metadata.gz: 75d8062f4bdc94bc7375a18124f72b3664a2d4074a63699c4e55f52ae085375ae587e7c113859b345bd7476878d744ceb66320cd42ccc9ef3e02f0315e591f78
7
- data.tar.gz: 223a89a483dfe638062240e4326e8c8e485b62a1e07108ed87feb5fff67ce1f5a705923939f23d6135bd4eeb6f8638cd60ae7638cc62ff2e1376aafe94720051
6
+ metadata.gz: 110d6d2259dcce0a7e1cac4ffbf24625a32c6a265fc5329adf4d2cc3ade2803aed54012021d8ad2238cd0aeb5259d3b973173671c7ca1b54fe6cc775146a5001
7
+ data.tar.gz: d217c50866d14c2eac62b03dbe406f572398f9f1d747b0a079c26236662474143d6d252b609df03c228123c677b299d1f3b8e700f49af7f0651561d3e715f647
data/README.md CHANGED
@@ -8,31 +8,133 @@ For documentation see: https://rubygems.org/gems/cf-uaa-lib
8
8
 
9
9
  ## Install from rubygems
10
10
 
11
- $ gem install cf-uaa-lib
11
+ ```plain
12
+ gem install cf-uaa-lib
13
+ ```
12
14
 
13
15
  ## Build from source
14
16
 
15
- $ bundle install
16
- $ gem build cf-uaa-lib.gemspec
17
- $ gem install cf-uaa-lib<version>.gem
17
+ ```plain
18
+ bundle install
19
+ rake install
20
+ ```
18
21
 
19
22
  ## Use the gem
20
23
 
21
- #!/usr/bin/env ruby
22
- require 'uaa'
23
- token_issuer = CF::UAA::TokenIssuer.new("https://uaa.cloudfoundry.com", "vmc")
24
- puts token_issuer.prompts.inspect
25
- token = token_issuer.implicit_grant_with_creds(username: "<your_username>", password: "<your_password>")
26
- token_info = CF::UAA::TokenCoder.decode(token.info["access_token"], nil, nil, false) #token signature not verified
27
- puts token_info["user_name"]
24
+ Create a UAA client that allows users to authenticate with username/password and allow client application to use `openid` scope to invoke `/userinfo` endpoint for the user.
25
+
26
+ ```plain
27
+ uaa create-client decode-token-demo -s decode-token-demo -v \
28
+ --authorized_grant_types password,refresh_token \
29
+ --scope "openid" \
30
+ --authorities uaa.none
31
+ ```
32
+
33
+ Create a user with which to authorize our `decode-token-demo` client application.
34
+
35
+ ```plain
36
+ uaa create-user myuser \
37
+ --email myuser@example.com \
38
+ --givenName "My" \
39
+ --familyName "User" \
40
+ --password myuser_secret
41
+ ```
42
+
43
+ Create this Ruby script (script is available at `examples/password_grant_and_decode_token.rb`):
44
+
45
+ ```ruby
46
+ #!/usr/bin/env ruby
47
+
48
+ require 'uaa'
49
+
50
+ url = ENV["UAA_URL"]
51
+ client, secret = "decode-token-demo", "decode-token-demo"
52
+ username, password = ENV["UAA_USERNAME"], ENV["UAA_PASSWORD"]
53
+
54
+ def show(title, object)
55
+ puts "#{title}: #{object.inspect}"
56
+ puts
57
+ end
58
+
59
+ uaa_options = {}
60
+ uaa_options[:ssl_ca_file] = ENV["UAA_CA_CERT_FILE"] if ENV["UAA_CA_CERT_FILE"]
61
+ show "uaa_options", uaa_options
62
+
63
+ uaa_info = CF::UAA::Info.new(url, uaa_options)
64
+ show "UAA server info", uaa_info.server
65
+
66
+ token_keys = uaa_info.validation_keys_hash
67
+ show "Signing keys for access tokens", token_keys
68
+
69
+ token_issuer = CF::UAA::TokenIssuer.new(url, client, secret, uaa_options)
70
+ show "Login prompts", token_issuer.prompts
71
+
72
+ token = token_issuer.owner_password_grant(username, password, "openid")
73
+ show "User '#{username}' password grant", token
74
+
75
+ auth_header = "bearer #{token.info["access_token"]}"
76
+ show "Auth header for resource server API calls", auth_header
77
+
78
+ userinfo = uaa_info.whoami(auth_header)
79
+ show "User info", userinfo
80
+
81
+ last_exception = nil
82
+ token_keys.each_pair do |keyname, token_key|
83
+ begin
84
+ token_coder = CF::UAA::TokenCoder.new(uaa_options.merge(pkey: token_key["value"], verify: true))
85
+ token_info = token_coder.decode(auth_header)
86
+ show "Decoded access token", token_info
87
+ last_exception = nil
88
+ rescue CF::UAA::Decode => e
89
+ last_exception = e
90
+ end
91
+ end
92
+ raise last_exception if last_exception
93
+ ```
94
+
95
+ To run the script, setup the env vars for your UAA and run the ruby script:
96
+
97
+ ```bash
98
+ export UAA_URL=https://192.168.50.6:8443
99
+ export UAA_CA_CERT_FILE=/path/to/ca.pem
100
+ export UAA_USERNAME=myuser
101
+ export UAA_PASSWORD=myuser_secret
102
+ ruby examples/password_grant_and_decode_token.rb
103
+ ```
104
+
105
+ The output will look similar to:
106
+
107
+ ```plain
108
+ uaa_options: {:ssl_ca_file=>"/var/folders/wd/xnncwqp96rj0v1y2nms64mq80000gn/T/tmp.R6wpXYdC/ca.pem"}
109
+
110
+ UAA server info: {"app"=>{"version"=>"4.19.0"}, "links"=>{"uaa"=>"https://192.168.50.6:8443", "passwd"=>"/forgot_password", "login"=>"https://192.168.50.6:8443", "register"=>"/create_account"}, "zone_name"=>"uaa", "entityID"=>"192.168.50.6:8443", "commit_id"=>"7897100", "idpDefinitions"=>{}, "prompts"=>{"username"=>["text", "Email"], "password"=>["password", "Password"]}, "timestamp"=>"2018-06-13T12:02:09-0700"}
111
+
112
+ Cookie#domain returns dot-less domain name now. Use Cookie#dot_domain if you need "." at the beginning.
113
+ Signing keys for access tokens: {"uaa-jwt-key-1"=>{"kty"=>"RSA", "e"=>"AQAB", "use"=>"sig", "kid"=>"uaa-jwt-key-1", "alg"=>"RS256", "value"=>"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8UNioYHjhyi1qSHrnBZ9\nKE96/1jLBOX2UTShGBo8jP7eDD6zUh5DNHNPAwD1V8gNI4wvNAm+zL1MrSEDWzn2\nPvCANd+XydoNVZU1zhqxvGhoxHmgAA3JbgSS3oLLNDG/HH8wEnjAxb+G1uh2EVSF\nAe/euQ/fEmY4e7uOG34h9WMX84tD1Sf/xvVoNGAL8bTwotzBLFZ12M3P70hrKDi5\n9wEBbY5bllvvNFyjZTYwMbw97RIOdg3FQkOABu8ENCqbPks5gqSpNV33ekaX4rAd\nwYdEX5iUzDBdMyD8jqUopuqTXqBKg2/ealGitXdbSIEAvcBgZWnn1j2vFp6OEYBB\n7wIDAQAB\n-----END PUBLIC KEY-----", "n"=>"APFDYqGB44cotakh65wWfShPev9YywTl9lE0oRgaPIz-3gw-s1IeQzRzTwMA9VfIDSOMLzQJvsy9TK0hA1s59j7wgDXfl8naDVWVNc4asbxoaMR5oAANyW4Ekt6CyzQxvxx_MBJ4wMW_htbodhFUhQHv3rkP3xJmOHu7jht-IfVjF_OLQ9Un_8b1aDRgC_G08KLcwSxWddjNz-9Iayg4ufcBAW2OW5Zb7zRco2U2MDG8Pe0SDnYNxUJDgAbvBDQqmz5LOYKkqTVd93pGl-KwHcGHRF-YlMwwXTMg_I6lKKbqk16gSoNv3mpRorV3W0iBAL3AYGVp59Y9rxaejhGAQe8"}}
114
+
115
+ Login prompts: {"username"=>["text", "Email"], "password"=>["password", "Password"]}
116
+
117
+ User 'myuser' password grant: #<CF::UAA::TokenInfo:0x00007fbad5a12c18 @info={"access_token"=>"eyJhbGciOiJSUzI1NiIsImtpZCI6InVhYS1qd3Qta2V5LTEiLCJ0eXAiOiJKV1QifQ.eyJqdGkiOiJlMTFlZmMwNjI1OGQ0MzA0YTc4ZGIyNzliYjJjMzQ1OCIsInN1YiI6IjM5NzhmZjRkLWQ3MzgtNGI4Yi05OTA4LTdhZTE0N2YzYzNiZSIsInNjb3BlIjpbIm9wZW5pZCJdLCJjbGllbnRfaWQiOiJkZWNvZGUtdG9rZW4tZGVtbyIsImNpZCI6ImRlY29kZS10b2tlbi1kZW1vIiwiYXpwIjoiZGVjb2RlLXRva2VuLWRlbW8iLCJncmFudF90eXBlIjoicGFzc3dvcmQiLCJ1c2VyX2lkIjoiMzk3OGZmNGQtZDczOC00YjhiLTk5MDgtN2FlMTQ3ZjNjM2JlIiwib3JpZ2luIjoidWFhIiwidXNlcl9uYW1lIjoibXl1c2VyIiwiZW1haWwiOiJteXVzZXJAZXhhbXBsZS5jb20iLCJhdXRoX3RpbWUiOjE1MzE2MzAxNDgsInJldl9zaWciOiI5M2E2NzkwNCIsImlhdCI6MTUzMTYzMDE0OCwiZXhwIjoxNTMxNjczMzQ4LCJpc3MiOiJodHRwczovLzE5Mi4xNjguNTAuNjo4NDQzL29hdXRoL3Rva2VuIiwiemlkIjoidWFhIiwiYXVkIjpbIm9wZW5pZCIsImRlY29kZS10b2tlbi1kZW1vIl19.qtbzxCOW5bebTgMLK-71_zxaT7l5PSmxhXcDtCeA64dZZ6-wXXmJivopm5PFEHnHiZwRpVe43jyEsbJGzBdl8GEsYQ9YIy51-4noby7ClziJv-6rSBYZnZuU5A234QRWclATGksOcz8Ft9PTIKGKLScyLhncwas7W0uiNJ87MFBGWY6Ovvl3Ac5-jHCqiRBXD6vUhzpfmy6_OUr53i9zJgtcQQWgDrOHxnFcRABZcDnhnWdcxh-Hbagtt9dQU46QgpqLJiUvAg-7ypZPGrxnr9UQEO2Q9GrolkbrSeUcfUOkgppxaA_0b6RYpgBR1qg-Ns6jGUxFgPs6Jj8pysfVmA", "token_type"=>"bearer", "refresh_token"=>"6701ddb9397840a1bd339e9f4314448f-r", "expires_in"=>43199, "scope"=>"openid", "jti"=>"e11efc06258d4304a78db279bb2c3458"}>
118
+
119
+ Auth header for resource server API calls: "bearer eyJhbGciOiJSUzI1NiIsImtpZCI6InVhYS1qd3Qta2V5LTEiLCJ0eXAiOiJKV1QifQ.eyJqdGkiOiJlMTFlZmMwNjI1OGQ0MzA0YTc4ZGIyNzliYjJjMzQ1OCIsInN1YiI6IjM5NzhmZjRkLWQ3MzgtNGI4Yi05OTA4LTdhZTE0N2YzYzNiZSIsInNjb3BlIjpbIm9wZW5pZCJdLCJjbGllbnRfaWQiOiJkZWNvZGUtdG9rZW4tZGVtbyIsImNpZCI6ImRlY29kZS10b2tlbi1kZW1vIiwiYXpwIjoiZGVjb2RlLXRva2VuLWRlbW8iLCJncmFudF90eXBlIjoicGFzc3dvcmQiLCJ1c2VyX2lkIjoiMzk3OGZmNGQtZDczOC00YjhiLTk5MDgtN2FlMTQ3ZjNjM2JlIiwib3JpZ2luIjoidWFhIiwidXNlcl9uYW1lIjoibXl1c2VyIiwiZW1haWwiOiJteXVzZXJAZXhhbXBsZS5jb20iLCJhdXRoX3RpbWUiOjE1MzE2MzAxNDgsInJldl9zaWciOiI5M2E2NzkwNCIsImlhdCI6MTUzMTYzMDE0OCwiZXhwIjoxNTMxNjczMzQ4LCJpc3MiOiJodHRwczovLzE5Mi4xNjguNTAuNjo4NDQzL29hdXRoL3Rva2VuIiwiemlkIjoidWFhIiwiYXVkIjpbIm9wZW5pZCIsImRlY29kZS10b2tlbi1kZW1vIl19.qtbzxCOW5bebTgMLK-71_zxaT7l5PSmxhXcDtCeA64dZZ6-wXXmJivopm5PFEHnHiZwRpVe43jyEsbJGzBdl8GEsYQ9YIy51-4noby7ClziJv-6rSBYZnZuU5A234QRWclATGksOcz8Ft9PTIKGKLScyLhncwas7W0uiNJ87MFBGWY6Ovvl3Ac5-jHCqiRBXD6vUhzpfmy6_OUr53i9zJgtcQQWgDrOHxnFcRABZcDnhnWdcxh-Hbagtt9dQU46QgpqLJiUvAg-7ypZPGrxnr9UQEO2Q9GrolkbrSeUcfUOkgppxaA_0b6RYpgBR1qg-Ns6jGUxFgPs6Jj8pysfVmA"
120
+
121
+ User info: {"user_id"=>"3978ff4d-d738-4b8b-9908-7ae147f3c3be", "user_name"=>"myuser", "name"=>"My User", "given_name"=>"My", "family_name"=>"User", "email"=>"myuser@example.com", "email_verified"=>true, "previous_logon_time"=>nil, "sub"=>"3978ff4d-d738-4b8b-9908-7ae147f3c3be"}
122
+
123
+ Decoded access token: {"jti"=>"e11efc06258d4304a78db279bb2c3458", "sub"=>"3978ff4d-d738-4b8b-9908-7ae147f3c3be", "scope"=>["openid"], "client_id"=>"decode-token-demo", "cid"=>"decode-token-demo", "azp"=>"decode-token-demo", "grant_type"=>"password", "user_id"=>"3978ff4d-d738-4b8b-9908-7ae147f3c3be", "origin"=>"uaa", "user_name"=>"myuser", "email"=>"myuser@example.com", "auth_time"=>1531630148, "rev_sig"=>"93a67904", "iat"=>1531630148, "exp"=>1531673348, "iss"=>"https://192.168.50.6:8443/oauth/token", "zid"=>"uaa", "aud"=>["openid", "decode-token-demo"]}
124
+ >>>>>>> 21ae635... new example script - password grant + decode using token keys
125
+ ```
28
126
 
29
127
  ## Tests
30
128
 
31
129
  Run the tests with rake:
32
130
 
33
- $ bundle exec rake test
131
+ ```plain
132
+ bundle exec rake test
133
+ ```
34
134
 
35
135
  Run the tests and see a fancy coverage report:
36
136
 
37
- $ bundle exec rake cov
137
+ ```plain
138
+ bundle exec rake cov
139
+ ```
38
140
 
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # uaa create-client decode-token-demo -s decode-token-demo -v \
4
+ # --authorized_grant_types password,refresh_token \
5
+ # --scope "openid" \
6
+ # --authorities uaa.none
7
+
8
+ require 'uaa'
9
+
10
+ url = ENV["UAA_URL"]
11
+ client, secret = "decode-token-demo", "decode-token-demo"
12
+ username, password = ENV["UAA_USERNAME"], ENV["UAA_PASSWORD"]
13
+
14
+ def show(title, object)
15
+ puts "#{title}: #{object.inspect}"
16
+ puts
17
+ end
18
+
19
+ uaa_options = {}
20
+ uaa_options[:ssl_ca_file] = ENV["UAA_CA_CERT_FILE"] if ENV["UAA_CA_CERT_FILE"]
21
+ show "uaa_options", uaa_options
22
+
23
+ uaa_info = CF::UAA::Info.new(url, uaa_options)
24
+ show "UAA server info", uaa_info.server
25
+
26
+ token_keys = uaa_info.validation_keys_hash
27
+ show "Signing keys for access tokens", token_keys
28
+
29
+ token_issuer = CF::UAA::TokenIssuer.new(url, client, secret, uaa_options)
30
+ show "Login prompts", token_issuer.prompts
31
+
32
+ token = token_issuer.owner_password_grant(username, password, "openid")
33
+ show "User '#{username}' password grant", token
34
+
35
+ auth_header = "bearer #{token.info["access_token"]}"
36
+ show "Auth header for resource server API calls", auth_header
37
+
38
+ userinfo = uaa_info.whoami(auth_header)
39
+ show "User info", userinfo
40
+
41
+ last_exception = nil
42
+ token_keys.each_pair do |keyname, token_key|
43
+ begin
44
+ token_coder = CF::UAA::TokenCoder.new(uaa_options.merge(pkey: token_key["value"], verify: true))
45
+ token_info = token_coder.decode(auth_header)
46
+ show "Decoded access token", token_info
47
+ last_exception = nil
48
+ rescue CF::UAA::Decode => e
49
+ last_exception = e
50
+ end
51
+ end
52
+ raise last_exception if last_exception
53
+
@@ -64,8 +64,8 @@ class TokenCoder
64
64
  def self.encode(token_body, options = {}, obsolete1 = nil, obsolete2 = nil)
65
65
  unless options.is_a?(Hash) && obsolete1.nil? && obsolete2.nil?
66
66
  # deprecated: def self.encode(token_body, skey, pkey = nil, algo = 'HS256')
67
- warn "#{self.class}##{__method__} is deprecated with these parameters. Please use options hash."
68
- options = {skey: options }
67
+ warn "WARNING: #{self.class}##{__method__} is deprecated with these parameters. Please use options hash."
68
+ options = {skey: options}
69
69
  options[:pkey], options[:algorithm] = obsolete1, obsolete2
70
70
  end
71
71
  options = normalize_options(options)
@@ -95,8 +95,8 @@ class TokenCoder
95
95
  def self.decode(token, options = {}, obsolete1 = nil, obsolete2 = nil)
96
96
  unless options.is_a?(Hash) && obsolete1.nil? && obsolete2.nil?
97
97
  # deprecated: def self.decode(token, skey = nil, pkey = nil, verify = true)
98
- warn "#{self.class}##{__method__} is deprecated with these parameters. Please use options hash."
99
- options = {skey: options }
98
+ warn "WARNING: #{self.class}##{__method__} is deprecated with these parameters. Please use options hash."
99
+ options = {skey: options}
100
100
  options[:pkey], options[:verify] = obsolete1, obsolete2
101
101
  end
102
102
  options = normalize_options(options)
@@ -106,10 +106,16 @@ class TokenCoder
106
106
  signing_input = [header_segment, payload_segment].join('.')
107
107
  header = Util.json_decode64(header_segment)
108
108
  payload = Util.json_decode64(payload_segment, (:sym if options[:symbolize_keys]))
109
- return payload unless options[:verify]
109
+ unless options[:verify]
110
+ warn "WARNING: Decoding token without verifying it was signed by its authoring UAA"
111
+ return payload
112
+ end
110
113
  raise SignatureNotAccepted, "Signature algorithm not accepted" unless
111
114
  options[:accept_algorithms].include?(algo = header["alg"])
112
- return payload if algo == 'none'
115
+ if algo == 'none'
116
+ warn "WARNING: Decoding token that explicitly states it has not been signed by an authoring UAA"
117
+ return payload
118
+ end
113
119
  signature = Util.decode64(crypto_segment)
114
120
  if ["HS256", "HS384", "HS512"].include?(algo)
115
121
  raise InvalidSignature, "Signature verification failed" unless
@@ -123,6 +129,17 @@ class TokenCoder
123
129
  payload
124
130
  end
125
131
 
132
+ # Decodes a JWT token to extract its expiry time
133
+ # @param [String] token A JWT token as returned by {TokenCoder.encode}
134
+ # @return [Integer] exp expiry timestamp
135
+ def self.decode_token_expiry(token)
136
+ segments = token.split('.')
137
+ raise InvalidTokenFormat, "Not enough or too many segments" unless [2,3].include? segments.length
138
+ header_segment, payload_segment, crypto_segment = segments
139
+ payload = Util.json_decode64(payload_segment, :sym)
140
+ payload[:exp]
141
+ end
142
+
126
143
  # Takes constant time to compare 2 strings (HMAC digests in this case)
127
144
  # to avoid timing attacks while comparing the HMAC digests
128
145
  # @param [String] a: the first digest to compare
@@ -78,7 +78,7 @@ class TokenIssuer
78
78
  headers['authorization'] = Http.basic_auth(@client_id, @client_secret)
79
79
  else
80
80
  headers['X-CF-ENCODED-CREDENTIALS'] = 'true'
81
- headers['authorization'] = Http.basic_auth(CGI.escape(@client_id), CGI.escape(@client_secret))
81
+ headers['authorization'] = Http.basic_auth(CGI.escape(@client_id || ''), CGI.escape(@client_secret || ''))
82
82
  end
83
83
  reply = json_parse_reply(@key_style, *request(@token_target, :post,
84
84
  '/oauth/token', Util.encode_form(params), headers))
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.0'
17
+ VERSION = '4.0.1'
18
18
  end
19
19
  end
@@ -183,6 +183,12 @@ describe TokenCoder do
183
183
  info["id"].should_not be_nil
184
184
  info["email"].should == "olds@vmware.com"
185
185
  end
186
+
187
+ it "decodes only the expiry_at time" do
188
+ exp = Time.now.to_i + 60
189
+ tkn = subject.encode({'foo' => "bar", 'exp' => exp })
190
+ TokenCoder.decode_token_expiry("bEaReR #{tkn}").should == exp
191
+ end
186
192
  end
187
193
 
188
194
  end
@@ -23,9 +23,12 @@ describe TokenIssuer do
23
23
 
24
24
  before do
25
25
  #Util.default_logger(:trace)
26
- @issuer = TokenIssuer.new('http://test.uaa.target', 'test_client', 'test!secret', options)
26
+ @issuer = TokenIssuer.new('http://test.uaa.target', client_id, client_secret, options)
27
27
  end
28
28
 
29
+ let(:client_id) { 'test_client' }
30
+ let(:client_secret) { 'test!secret' }
31
+
29
32
  subject { @issuer }
30
33
 
31
34
  describe 'initialize' do
@@ -106,6 +109,31 @@ describe TokenIssuer do
106
109
  token.info['expires_in'].should == 98765
107
110
  end
108
111
 
112
+ context "when client & client secret are nil" do
113
+ let(:client_id) { nil }
114
+ let(:client_secret) { nil }
115
+
116
+ it 'does not error' do
117
+ subject.set_request_handler do |url, method, body, headers|
118
+ headers['content-type'].should =~ /application\/x-www-form-urlencoded/
119
+ headers['accept'].should =~ /application\/json/
120
+ headers['X-CF-ENCODED-CREDENTIALS'].should == 'true'
121
+ headers['authorization'].should == 'Basic Og=='
122
+ url.should == 'http://test.uaa.target/oauth/token'
123
+ method.should == :post
124
+ reply = {access_token: 'test_access_token', token_type: 'BEARER',
125
+ scope: 'openid', expires_in: 98765}
126
+ [200, Util.json(reply), {'content-type' => 'application/json'}]
127
+ end
128
+ token = subject.owner_password_grant('joe+admin', "?joe's%password$@ ", 'openid')
129
+ token.should be_an_instance_of TokenInfo
130
+ token.info['access_token'].should == 'test_access_token'
131
+ token.info['token_type'].should =~ /^bearer$/i
132
+ token.info['scope'].should == 'openid'
133
+ token.info['expires_in'].should == 98765
134
+ end
135
+ end
136
+
109
137
  it 'gets a token with passcode' do
110
138
  subject.set_request_handler do |url, method, body, headers|
111
139
  headers['content-type'].should =~ /application\/x-www-form-urlencoded/
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.0
4
+ version: 4.0.1
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: 2022-01-21 00:00:00.000000000 Z
15
+ date: 2022-01-28 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/password_grant_and_decode_token.rb
240
241
  - lib/uaa.rb
241
242
  - lib/uaa/http.rb
242
243
  - lib/uaa/info.rb