Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ Metrics/ParameterLists:

Metrics/AbcSize:
Max: 30
Exclude:
- 'test/**/*'

# A Sorbet `sig` is a line of its own, so a fully annotated class runs roughly
# half again as long as the same code untyped. These limits are set for annotated
Expand Down
4 changes: 2 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ gem 'minitest', group: :development
gem 'pry'
gem 'rake', group: :development
gem 'reline'
gem 'rexml', '>= 3.4.2'
gem 'rubocop', group: :development
gem 'rubocop-graphql', group: :development
gem 'rubocop-minitest', group: :development
Expand All @@ -19,6 +20,5 @@ gem 'sorbet', group: :development
gem 'sorbet-runtime'
gem 'tapioca', group: :development
gem 'thor', '>= 1.4.0'
gem 'webmock', group: :development
gem 'rexml', '>= 3.4.2'
gem 'uri', '>= 1.0.4'
gem 'webmock', group: :development
16 changes: 12 additions & 4 deletions lib/fragment_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -305,17 +305,25 @@ def parse_oauth_url(oauth_url)
raise ArgumentError, "oauth_url must be an http or https URL, got #{oauth_url.inspect}"
end

sig { returns(Token) }
def create_token
uri = @oauth_url
post = Net::HTTP::Post.new(uri.request_uri)
# RFC 6749 §4.4.2 and §2.3.1: client_credentials over
# application/x-www-form-urlencoded, with the client id and secret as HTTP Basic.
sig { returns(Net::HTTP::Post) }
def token_request
post = Net::HTTP::Post.new(@oauth_url.request_uri)
post.basic_auth(@client_id, @client_secret)
post.content_type = 'application/x-www-form-urlencoded'
post.body = URI.encode_www_form(
grant_type: 'client_credentials',
scope: @oauth_scope,
client_id: @client_id
)
post
end

sig { returns(Token) }
def create_token
uri = @oauth_url
post = token_request

begin
http = Net::HTTP.new(uri.host, uri.port)
Expand Down
2 changes: 1 addition & 1 deletion test/conformance_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ def test_key_order_is_canonical
end

FragmentClient::TypedEntries.reset!
FragmentGraphQl.reset_operations!
FragmentGraphQl.reset_operations!
end
end

Expand Down
28 changes: 15 additions & 13 deletions test/unit_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -286,40 +286,41 @@ def test_token_request_conforms_to_oauth2_and_http_specs
# of the target URI as the request-target."
token_request_path = captured_request_paths.first
assert_equal '/oauth2/token', token_request_path,
"Token request must use origin-form request-target (RFC 7230 §5.3.1), got: #{token_request_path}"
"Token request must use origin-form request-target (RFC 7230 §5.3.1), got: #{token_request_path}"

# RFC 6749 §4.4.2: "The client makes a request to the token endpoint by
# adding the following parameters using the 'application/x-www-form-urlencoded'
# format [...] grant_type: REQUIRED. Value MUST be set to 'client_credentials'."
body_params = URI.decode_www_form(captured_auth_request.body).to_h
assert_equal 'client_credentials', body_params['grant_type'],
'grant_type must be client_credentials (RFC 6749 §4.4.2)'
'grant_type must be client_credentials (RFC 6749 §4.4.2)'

# RFC 6749 §4.4.2: "scope: OPTIONAL."
refute_nil body_params['scope'],
'scope parameter should be present when configured (RFC 6749 §4.4.2)'
'scope parameter should be present when configured (RFC 6749 §4.4.2)'

# RFC 6749 §4.4.2 requires the token request entity-body to use the
# application/x-www-form-urlencoded format per Appendix B, and the
# section's example request explicitly sets this Content-Type.
content_type = captured_auth_request.headers['Content-Type']
assert_match(/\Aapplication\/x-www-form-urlencoded\b/, content_type,
'Token request Content-Type should be application/x-www-form-urlencoded (RFC 6749 §4.4.2)')
assert_match(%r{\Aapplication/x-www-form-urlencoded\b}, content_type,
'Token request Content-Type should be application/x-www-form-urlencoded (RFC 6749 §4.4.2)')

# RFC 6749 §2.3.1: "Clients in possession of a client password MAY use
# the HTTP Basic authentication scheme [...] The client identifier is [...]
# used as the username; the client password [...] used as the password."
auth_header = captured_auth_request.headers['Authorization']
assert_match(/\ABasic /, auth_header,
'Must use HTTP Basic authentication (RFC 6749 §2.3.1)')
'Must use HTTP Basic authentication (RFC 6749 §2.3.1)')
decoded_credentials = Base64.decode64(auth_header.sub('Basic ', ''))
client_id, client_secret = decoded_credentials.split(':', 2)
assert_equal 'test_client_id', client_id,
'Basic auth username must be client_id (RFC 6749 §2.3.1)'
'Basic auth username must be client_id (RFC 6749 §2.3.1)'
assert_equal 'test_client_secret', client_secret,
'Basic auth password must be client_secret (RFC 6749 §2.3.1)'
'Basic auth password must be client_secret (RFC 6749 §2.3.1)'
ensure
verbose, $VERBOSE = $VERBOSE, nil
verbose = $VERBOSE
$VERBOSE = nil
Net::HTTP::Post.remove_method(:initialize)
$VERBOSE = verbose
end
Expand All @@ -340,9 +341,10 @@ def test_token_request_uses_origin_form_with_custom_oauth_url
oauth_url: 'https://auth.us-east-1.fragment.dev/oauth2/token')

assert_equal '/oauth2/token', captured_request_paths.first,
'Custom oauth_url must also use origin-form request-target (RFC 7230 §5.3.1)'
'Custom oauth_url must also use origin-form request-target (RFC 7230 §5.3.1)'
ensure
verbose, $VERBOSE = $VERBOSE, nil
verbose = $VERBOSE
$VERBOSE = nil
Net::HTTP::Post.remove_method(:initialize)
$VERBOSE = verbose
end
Expand Down Expand Up @@ -434,8 +436,8 @@ def test_token_request_uses_appendix_b_form_encoding_utf8
body_params = URI.decode_www_form(captured_auth_request.body).to_h
assert_equal 'client_credentials', body_params['grant_type']
assert_equal oauth_scope, body_params['scope'],
'scope should round-trip via x-www-form-urlencoded UTF-8 encoding'
'scope should round-trip via x-www-form-urlencoded UTF-8 encoding'
assert_equal client_id, body_params['client_id'],
'client_id should round-trip via x-www-form-urlencoded UTF-8 encoding'
'client_id should round-trip via x-www-form-urlencoded UTF-8 encoding'
end
end
Loading