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}.