lib/uaa/token_issuer.rb in cf-uaa-lib-4.0.2 vs lib/uaa/token_issuer.rb in cf-uaa-lib-4.0.3

- old
+ new

@@ -10,10 +10,11 @@ # subcomponents is subject to the terms and conditions of the # subcomponent's license, as noted in the LICENSE file. #++ require 'securerandom' +require "digest" require 'uaa/http' require 'cgi' module CF::UAA @@ -51,10 +52,11 @@ class TokenIssuer include Http private + @client_auth_method = 'client_secret_basic' def random_state; SecureRandom.hex end def parse_implicit_params(encoded_params, state) params = Util.decode_form(encoded_params) @@ -72,12 +74,19 @@ def request_token(params) if scope = Util.arglist(params.delete(:scope)) params[:scope] = Util.strlist(scope) end headers = {'content-type' => FORM_UTF8, 'accept' => JSON_UTF8} - if @basic_auth - headers['authorization'] = Http.basic_auth(@client_id, @client_secret) + if @client_auth_method == 'client_secret_basic' && @client_secret && @client_id + if @basic_auth + headers['authorization'] = Http.basic_auth(@client_id, @client_secret) + else + headers['X-CF-ENCODED-CREDENTIALS'] = 'true' + headers['authorization'] = Http.basic_auth(CGI.escape(@client_id), CGI.escape(@client_secret)) + end + elsif @client_id && params[:code_verifier] + params[:client_id] = @client_id else headers['X-CF-ENCODED-CREDENTIALS'] = 'true' headers['authorization'] = Http.basic_auth(CGI.escape(@client_id || ''), CGI.escape(@client_secret || '')) end reply = json_parse_reply(@key_style, *request(@token_target, :post, @@ -89,10 +98,14 @@ def authorize_path_args(response_type, redirect_uri, scope, state = random_state, args = {}) params = args.merge(client_id: @client_id, response_type: response_type, redirect_uri: redirect_uri, state: state) params[:scope] = scope = Util.strlist(scope) if scope = Util.arglist(scope) params[:nonce] = state + if not @code_verifier.nil? + params[:code_challenge] = get_challenge + params[:code_challenge_method] = 'S256' + end "/oauth/authorize?#{Util.encode_form(params)}" end def jkey(k) @key_style ? k : k.to_s end @@ -114,10 +127,15 @@ def initialize(target, client_id, client_secret = nil, options = {}) @target, @client_id, @client_secret = target, client_id, client_secret @token_target = options[:token_target] || target @key_style = options[:symbolize_keys] ? :sym : nil @basic_auth = options[:basic_auth] == true ? true : false + @client_auth_method = options[:client_auth_method] || 'client_secret_basic' + @code_verifier = options[:code_verifier] || nil + if @code_verifier.nil? && options[:use_pkce] && options[:use_pkce] == true + @code_verifier = get_verifier + end initialize_http_options(options) end # Allows an app to discover what credentials are required for # {#implicit_grant_with_creds}. @@ -233,11 +251,30 @@ authcode = params['code'] raise BadResponse unless params['state'] == ac_params['state'] && authcode rescue URI::InvalidURIError, ArgumentError, BadResponse raise BadResponse, "received invalid response from target #{@target}" end - request_token(grant_type: 'authorization_code', code: authcode, - redirect_uri: ac_params['redirect_uri']) + if not @code_verifier.nil? + request_token(grant_type: 'authorization_code', code: authcode, + redirect_uri: ac_params['redirect_uri'], code_verifier: @code_verifier) + else + request_token(grant_type: 'authorization_code', code: authcode, + redirect_uri: ac_params['redirect_uri']) + end + end + + # Generates a random verifier for PKCE usage + def get_verifier + if not @code_verifier.nil? + @verifier = @code_verifier + else + @verifier ||= SecureRandom.base64(96).tr("+/", "-_").tr("=", "") + end + end + + # Calculates the challenge from code_verifier + def get_challenge + @challenge ||= Digest::SHA256.base64digest(get_verifier).tr("+/", "-_").tr("=", "") end # Uses the instance client credentials in addition to the +username+ # and +password+ to get a token via the owner password grant. # See {http://tools.ietf.org/html/rfc6749#section-4.3}.