From 6b86b95f9a446d83c0e7e893f90b7c6253365857 Mon Sep 17 00:00:00 2001 From: d036670 Date: Fri, 8 Sep 2023 14:52:07 +0200 Subject: [PATCH] feature: support client_secret_post client_secret_post means client_id and client_secret in body of request not in authorization header --- lib/uaa/token_issuer.rb | 3 ++ spec/token_issuer_spec.rb | 101 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/lib/uaa/token_issuer.rb b/lib/uaa/token_issuer.rb index ff5c8b8..99f294a 100644 --- a/lib/uaa/token_issuer.rb +++ b/lib/uaa/token_issuer.rb @@ -83,6 +83,9 @@ def request_token(params) headers['X-CF-ENCODED-CREDENTIALS'] = 'true' headers['authorization'] = Http.basic_auth(CGI.escape(@client_id), CGI.escape(@client_secret)) end + elsif @client_auth_method == 'client_secret_post' && @client_secret && @client_id + params[:client_id] = @client_id + params[:client_secret] = @client_secret elsif @client_id && params[:code_verifier] params[:client_id] = @client_id else diff --git a/spec/token_issuer_spec.rb b/spec/token_issuer_spec.rb index f5e26b9..2a37aa4 100644 --- a/spec/token_issuer_spec.rb +++ b/spec/token_issuer_spec.rb @@ -310,6 +310,41 @@ module CF::UAA end + + context 'with basic_auth using auth code grant' do + let(:options) { {basic_auth: true} } + + it 'basic_auth with authorization code' 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 == 'Basic dGVzdF9jbGllbnQ6dGVzdCFzZWNyZXQ=' + params = Util.decode_form(body) + params['code_verifier'].should_not + params['grant_type'].should == 'authorization_code' + url.should match 'http://test.uaa.target/oauth/token' + method.should == :post + reply = {access_token: 'test_access_token', token_type: 'BEARER', + scope: 'openid', expires_in: 98765} + [200, Util.json(reply), {'content-type' => 'application/json'}] + end + cburi = 'http://call.back/uri_path' + params = Util.decode_form(cburi[1]) + params['code_challenge'].should_not + params['code_challenge_method'].should_not + redir_uri = subject.authcode_uri(cburi) + state = /state=([^&]+)/.match(redir_uri)[1] + reply_query = "state=#{state}&code=kz8%2F5gQZ2pc%3D" + token = subject.authcode_grant(redir_uri, reply_query) + 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 == 'openid' + 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'} } @@ -324,6 +359,38 @@ module CF::UAA code_verifier.should == options[:code_verifier] code_challenge.should == 'TAnM2AKGgiQKOC16cRpMdF_55qwmz3B333cq6T18z0s' end + + let(:client_secret) { nil } + it 'public token request with pkce without client_secret' 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['code_verifier'].should_not + params['grant_type'].should == 'authorization_code' + 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: 'openid', expires_in: 98765} + [200, Util.json(reply), {'content-type' => 'application/json'}] + end + cburi = 'http://call.back/uri_path' + params = Util.decode_form(cburi[1]) + params['code_challenge'].should_not + params['code_challenge_method'].should_not + redir_uri = subject.authcode_uri(cburi) + state = /state=([^&]+)/.match(redir_uri)[1] + reply_query = "state=#{state}&code=kz8%2F5gQZ2pc%3D" + token = subject.authcode_grant(redir_uri, reply_query) + 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 == 'openid' + token.info['expires_in'].should == 98765 + end end context 'no pkce active as this is the default' do @@ -338,6 +405,40 @@ module CF::UAA end end + context 'with client_auth_method using client_secret_post' do + let(:options) { {client_auth_method: 'client_secret_post'} } + let(:client_secret) { 'body!secret' } + + 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['code_verifier'].should_not + params['grant_type'].should == 'authorization_code' + params['client_id'].should == 'test_client' + params['client_secret'].should == 'body!secret' + url.should match 'http://test.uaa.target/oauth/token' + method.should == :post + reply = {access_token: 'test_access_token', token_type: 'BEARER', + scope: 'openid', expires_in: 98765} + [200, Util.json(reply), {'content-type' => 'application/json'}] + end + cburi = 'http://call.back/uri_path' + redir_uri = subject.authcode_uri(cburi) + state = /state=([^&]+)/.match(redir_uri)[1] + reply_query = "state=#{state}&code=kz8%2F5gQZ2pc%3D" + token = subject.authcode_grant(redir_uri, reply_query) + 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 == 'openid' + token.info['expires_in'].should == 98765 + end + end + end end