diff --git a/lib/uaa/token_issuer.rb b/lib/uaa/token_issuer.rb index 99f294a..54c3656 100644 --- a/lib/uaa/token_issuer.rb +++ b/lib/uaa/token_issuer.rb @@ -75,6 +75,8 @@ def request_token(params) if scope = Util.arglist(params.delete(:scope)) params[:scope] = Util.strlist(scope) end + client_assertion = params[:client_assertion] + params.delete(:client_assertion) headers = {'content-type' => FORM_UTF8, 'accept' => JSON_UTF8} if @client_auth_method == 'client_secret_basic' && @client_secret && @client_id if @basic_auth @@ -88,6 +90,10 @@ def request_token(params) params[:client_secret] = @client_secret elsif @client_id && params[:code_verifier] params[:client_id] = @client_id + elsif client_assertion && @client_id && @client_secret.nil? + params[:client_id] = @client_id + params[:client_assertion] = client_assertion + params[:client_assertion_type] = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer' else headers['X-CF-ENCODED-CREDENTIALS'] = 'true' headers['authorization'] = Http.basic_auth(CGI.escape(@client_id || ''), CGI.escape(@client_secret || '')) @@ -129,6 +135,7 @@ def jkey(k) @key_style ? k : k.to_s end # * +:symbolize_keys+, if true, returned hash keys are symbols. def initialize(target, client_id, client_secret = nil, options = {}) @target, @client_id, @client_secret = target, client_id, client_secret + @client_assertion = options[:client_assertion] || nil @token_target = options[:token_target] || target @key_style = options[:symbolize_keys] ? :sym : nil @basic_auth = options[:basic_auth] == true ? true : false @@ -310,8 +317,8 @@ def owner_password_credentials_grant(credentials) # Uses the instance client credentials to get a token with a client # credentials grant. See http://tools.ietf.org/html/rfc6749#section-4.4 # @return [TokenInfo] - def client_credentials_grant(scope = nil) - request_token(grant_type: 'client_credentials', scope: scope) + def client_credentials_grant(scope = nil, client_assertion = nil) + request_token(grant_type: 'client_credentials', scope: scope, client_assertion: client_assertion) end # Uses the instance client credentials and the given +refresh_token+ to get diff --git a/spec/token_issuer_spec.rb b/spec/token_issuer_spec.rb index 2a37aa4..deeb19b 100644 --- a/spec/token_issuer_spec.rb +++ b/spec/token_issuer_spec.rb @@ -345,6 +345,37 @@ module CF::UAA end end + context 'with client_assertion using client credentials grant' do + let(:client_id) { 'test_client' } + let(:client_secret) { nil } + + it 'use client_secret_post in authorization code and expect client_id and secret in body' do + subject.set_request_handler do |url, method, body, headers| + headers['content-type'].should =~ /application\/x-www-form-urlencoded/ + headers['accept'].should =~ /application\/json/ + headers['X-CF-ENCODED-CREDENTIALS'].should_not + headers['authorization'].should_not + params = Util.decode_form(body) + params['grant_type'].should == 'client_credentials' + params['client_id'].should == 'test_client' + params['client_assertion'].should == 'any-jwt-token' + params['client_assertion_type'].should == 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer' + params['client_secret'].should_not + url.should match 'http://test.uaa.target/oauth/token' + method.should == :post + reply = {access_token: 'test_access_token', token_type: 'BEARER', + scope: 'logs.read', expires_in: 98765} + [200, Util.json(reply), {'content-type' => 'application/json'}] + end + token = subject.client_credentials_grant('logs.read', 'any-jwt-token') + token.should be_an_instance_of TokenInfo + token.info['access_token'].should == 'test_access_token' + token.info['token_type'].should =~ /^bearer$/i + token.info['scope'].should == 'logs.read' + token.info['expires_in'].should == 98765 + end + end + context 'pkce with own code verifier' do let(:options) { {basic_auth: false, code_verifier: 'umoq1e_4XMYXvfHlaO9mSlSI17OKfxnwfR5ZD-oYreFxyn8yQZ-ZHPZfUZ4n3WjY_tkOB_MAisSy4ddqsa6aoTU5ZOcX4ps3de933PczYlC8pZpKL8EQWaDZOnpOyB2W'} }