oauth2 2.0.9 → 2.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/oauth2/error.rb CHANGED
@@ -1,55 +1,73 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OAuth2
4
+ # Represents an OAuth2 error condition.
5
+ #
6
+ # Wraps details from an OAuth2::Response or Hash payload returned by an
7
+ # authorization server, exposing error code and description per RFC 6749.
4
8
  class Error < StandardError
9
+ # @return [OAuth2::Response, Hash, Object] Original response or payload used to build the error
10
+ # @return [String] Raw body content (if available)
11
+ # @return [String, nil] Error code (e.g., 'invalid_grant')
12
+ # @return [String, nil] Human-readable description for the error
5
13
  attr_reader :response, :body, :code, :description
6
14
 
7
- # standard error codes include:
8
- # 'invalid_request', 'invalid_client', 'invalid_token', 'invalid_grant', 'unsupported_grant_type', 'invalid_scope'
9
- # response might be a Response object, or the response.parsed hash
15
+ # Create a new OAuth2::Error
16
+ #
17
+ # @param [OAuth2::Response, Hash, Object] response A Response or error payload
10
18
  def initialize(response)
11
19
  @response = response
12
20
  if response.respond_to?(:parsed)
13
21
  if response.parsed.is_a?(Hash)
14
- @code = response.parsed['error']
15
- @description = response.parsed['error_description']
22
+ @code = response.parsed["error"]
23
+ @description = response.parsed["error_description"]
16
24
  end
17
25
  elsif response.is_a?(Hash)
18
- @code = response['error']
19
- @description = response['error_description']
26
+ @code = response["error"]
27
+ @description = response["error_description"]
20
28
  end
21
29
  @body = if response.respond_to?(:body)
22
- response.body
23
- else
24
- @response
25
- end
30
+ response.body
31
+ else
32
+ @response
33
+ end
26
34
  message_opts = parse_error_description(@code, @description)
27
35
  super(error_message(@body, message_opts))
28
36
  end
29
37
 
30
38
  private
31
39
 
40
+ # Builds a multi-line error message including description and raw body.
41
+ #
42
+ # @param [String, #encode] response_body Response body content
43
+ # @param [Hash] opts Options including :error_description
44
+ # @return [String] Message suitable for StandardError
32
45
  def error_message(response_body, opts = {})
33
46
  lines = []
34
47
 
35
48
  lines << opts[:error_description] if opts[:error_description]
36
49
 
37
50
  error_string = if response_body.respond_to?(:encode) && opts[:error_description].respond_to?(:encoding)
38
- script_encoding = opts[:error_description].encoding
39
- response_body.encode(script_encoding, invalid: :replace, undef: :replace)
40
- else
41
- response_body
42
- end
51
+ script_encoding = opts[:error_description].encoding
52
+ response_body.encode(script_encoding, invalid: :replace, undef: :replace)
53
+ else
54
+ response_body
55
+ end
43
56
 
44
57
  lines << error_string
45
58
 
46
59
  lines.join("\n")
47
60
  end
48
61
 
62
+ # Formats the OAuth2 error code and description into a single string.
63
+ #
64
+ # @param [String, nil] code OAuth2 error code
65
+ # @param [String, nil] description OAuth2 error description
66
+ # @return [Hash] Options hash containing :error_description when present
49
67
  def parse_error_description(code, description)
50
68
  return {} unless code || description
51
69
 
52
- error_description = ''
70
+ error_description = ""
53
71
  error_description += "#{code}: " if code
54
72
  error_description += description if description
55
73
 
@@ -0,0 +1,52 @@
1
+ module OAuth2
2
+ # Mixin that redacts sensitive instance variables in #inspect output.
3
+ #
4
+ # Classes include this module and declare which attributes should be filtered
5
+ # using {.filtered_attributes}. Any instance variable name that includes one of
6
+ # those attribute names will be shown as [FILTERED] in the object's inspect.
7
+ module FilteredAttributes
8
+ # Hook invoked when the module is included. Extends the including class with
9
+ # class-level helpers.
10
+ #
11
+ # @param [Class] base The including class
12
+ # @return [void]
13
+ def self.included(base)
14
+ base.extend(ClassMethods)
15
+ end
16
+
17
+ # Class-level helpers for configuring filtered attributes.
18
+ module ClassMethods
19
+ # Declare attributes that should be redacted in inspect output.
20
+ #
21
+ # @param [Array<Symbol, String>] attributes One or more attribute names
22
+ # @return [void]
23
+ def filtered_attributes(*attributes)
24
+ @filtered_attribute_names = attributes.map(&:to_sym)
25
+ end
26
+
27
+ # The configured attribute names to filter.
28
+ #
29
+ # @return [Array<Symbol>]
30
+ def filtered_attribute_names
31
+ @filtered_attribute_names || []
32
+ end
33
+ end
34
+
35
+ # Custom inspect that redacts configured attributes.
36
+ #
37
+ # @return [String]
38
+ def inspect
39
+ filtered_attribute_names = self.class.filtered_attribute_names
40
+ return super if filtered_attribute_names.empty?
41
+
42
+ inspected_vars = instance_variables.map do |var|
43
+ if filtered_attribute_names.any? { |filtered_var| var.to_s.include?(filtered_var.to_s) }
44
+ "#{var}=[FILTERED]"
45
+ else
46
+ "#{var}=#{instance_variable_get(var).inspect}"
47
+ end
48
+ end
49
+ "#<#{self.class}:#{object_id} #{inspected_vars.join(", ")}>"
50
+ end
51
+ end
52
+ end
@@ -1,37 +1,55 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json'
4
- require 'multi_xml'
5
- require 'rack'
3
+ require "json"
4
+ require "multi_xml"
5
+ require "rack"
6
6
 
7
7
  module OAuth2
8
- # OAuth2::Response class
8
+ # The Response class handles HTTP responses in the OAuth2 gem, providing methods
9
+ # to access and parse response data in various formats.
10
+ #
11
+ # @since 1.0.0
9
12
  class Response
13
+ # Default configuration options for Response instances
14
+ #
15
+ # @return [Hash] The default options hash
10
16
  DEFAULT_OPTIONS = {
11
17
  parse: :automatic,
12
18
  snaky: true,
19
+ snaky_hash_klass: SnakyHash::StringKeyed,
13
20
  }.freeze
21
+
22
+ # @return [Faraday::Response] The raw Faraday response object
14
23
  attr_reader :response
24
+
25
+ # @return [Hash] The options hash for this instance
15
26
  attr_accessor :options
16
27
 
17
- # Procs that, when called, will parse a response body according
18
- # to the specified format.
28
+ # @private
29
+ # Storage for response body parser procedures
30
+ #
31
+ # @return [Hash<Symbol, Proc>] Hash of parser procs keyed by format symbol
19
32
  @@parsers = {
20
33
  query: ->(body) { Rack::Utils.parse_query(body) },
21
34
  text: ->(body) { body },
22
35
  }
23
36
 
24
- # Content type assignments for various potential HTTP content types.
37
+ # @private
38
+ # Maps content types to parser symbols
39
+ #
40
+ # @return [Hash<String, Symbol>] Hash of content types mapped to parser symbols
25
41
  @@content_types = {
26
- 'application/x-www-form-urlencoded' => :query,
27
- 'text/plain' => :text,
42
+ "application/x-www-form-urlencoded" => :query,
43
+ "text/plain" => :text,
28
44
  }
29
45
 
30
46
  # Adds a new content type parser.
31
47
  #
32
- # @param [Symbol] key A descriptive symbol key such as :json or :query.
33
- # @param [Array] mime_types One or more mime types to which this parser applies.
34
- # @yield [String] A block returning parsed content.
48
+ # @param [Symbol] key A descriptive symbol key such as :json or :query
49
+ # @param [Array<String>, String] mime_types One or more mime types to which this parser applies
50
+ # @yield [String] Block that will be called to parse the response body
51
+ # @yieldparam [String] body The response body to parse
52
+ # @return [void]
35
53
  def self.register_parser(key, mime_types, &block)
36
54
  key = key.to_sym
37
55
  @@parsers[key] = block
@@ -43,38 +61,48 @@ module OAuth2
43
61
  # Initializes a Response instance
44
62
  #
45
63
  # @param [Faraday::Response] response The Faraday response instance
46
- # @param [Symbol] parse (:automatic) how to parse the response body. one of :query (for x-www-form-urlencoded),
47
- # :json, or :automatic (determined by Content-Type response header)
48
- # @param [true, false] snaky (true) Convert @parsed to a snake-case,
49
- # indifferent-access SnakyHash::StringKeyed, which is a subclass of Hashie::Mash (from hashie gem)?
50
- # @param [Hash] options all other options for initializing the instance
51
- def initialize(response, parse: :automatic, snaky: true, **options)
64
+ # @param [Symbol] parse (:automatic) How to parse the response body
65
+ # @param [Boolean] snaky (true) Whether to convert parsed response to snake_case using SnakyHash
66
+ # @param [Class, nil] snaky_hash_klass (nil) Custom class for snake_case hash conversion
67
+ # @param [Hash] options Additional options for the response
68
+ # @option options [Symbol] :parse (:automatic) Parse strategy (:query, :json, or :automatic)
69
+ # @option options [Boolean] :snaky (true) Enable/disable snake_case conversion
70
+ # @option options [Class] :snaky_hash_klass (SnakyHash::StringKeyed) Class to use for hash conversion
71
+ # @return [OAuth2::Response] The new Response instance
72
+ def initialize(response, parse: :automatic, snaky: true, snaky_hash_klass: nil, **options)
52
73
  @response = response
53
74
  @options = {
54
75
  parse: parse,
55
76
  snaky: snaky,
77
+ snaky_hash_klass: snaky_hash_klass,
56
78
  }.merge(options)
57
79
  end
58
80
 
59
81
  # The HTTP response headers
82
+ #
83
+ # @return [Hash] The response headers
60
84
  def headers
61
85
  response.headers
62
86
  end
63
87
 
64
88
  # The HTTP response status code
89
+ #
90
+ # @return [Integer] The response status code
65
91
  def status
66
92
  response.status
67
93
  end
68
94
 
69
95
  # The HTTP response body
96
+ #
97
+ # @return [String] The response body or empty string if nil
70
98
  def body
71
- response.body || ''
99
+ response.body || ""
72
100
  end
73
101
 
74
- # The {#response} {#body} as parsed by {#parser}.
102
+ # The parsed response body
75
103
  #
76
- # @return [Object] As returned by {#parser} if it is #call-able.
77
- # @return [nil] If the {#parser} is not #call-able.
104
+ # @return [Object, SnakyHash::StringKeyed] The parsed response body
105
+ # @return [nil] If no parser is available
78
106
  def parsed
79
107
  return @parsed if defined?(@parsed)
80
108
 
@@ -90,34 +118,37 @@ module OAuth2
90
118
  end
91
119
  end
92
120
 
93
- @parsed = SnakyHash::StringKeyed.new(@parsed) if options[:snaky] && @parsed.is_a?(Hash)
121
+ if options[:snaky] && @parsed.is_a?(Hash)
122
+ hash_klass = options[:snaky_hash_klass] || DEFAULT_OPTIONS[:snaky_hash_klass]
123
+ @parsed = hash_klass[@parsed]
124
+ end
94
125
 
95
126
  @parsed
96
127
  end
97
128
 
98
- # Attempts to determine the content type of the response.
129
+ # Determines the content type of the response
130
+ #
131
+ # @return [String, nil] The content type or nil if headers are not present
99
132
  def content_type
100
- return nil unless response.headers
133
+ return unless response.headers
101
134
 
102
- ((response.headers.values_at('content-type', 'Content-Type').compact.first || '').split(';').first || '').strip.downcase
135
+ ((response.headers.values_at("content-type", "Content-Type").compact.first || "").split(";").first || "").strip.downcase
103
136
  end
104
137
 
105
- # Determines the parser (a Proc or other Object which responds to #call)
106
- # that will be passed the {#body} (and optional {#response}) to supply
107
- # {#parsed}.
138
+ # Determines the parser to be used for the response body
108
139
  #
109
- # The parser can be supplied as the +:parse+ option in the form of a Proc
110
- # (or other Object responding to #call) or a Symbol. In the latter case,
111
- # the actual parser will be looked up in {@@parsers} by the supplied Symbol.
140
+ # @note The parser can be supplied as the +:parse+ option in the form of a Proc
141
+ # (or other Object responding to #call) or a Symbol. In the latter case,
142
+ # the actual parser will be looked up in {@@parsers} by the supplied Symbol.
112
143
  #
113
- # If no +:parse+ option is supplied, the lookup Symbol will be determined
114
- # by looking up {#content_type} in {@@content_types}.
144
+ # @note If no +:parse+ option is supplied, the lookup Symbol will be determined
145
+ # by looking up {#content_type} in {@@content_types}.
115
146
  #
116
- # If {#parser} is a Proc, it will be called with no arguments, just
117
- # {#body}, or {#body} and {#response}, depending on the Proc's arity.
147
+ # @note If {#parser} is a Proc, it will be called with no arguments, just
148
+ # {#body}, or {#body} and {#response}, depending on the Proc's arity.
118
149
  #
119
- # @return [Proc, #call] If a parser was found.
120
- # @return [nil] If no parser was found.
150
+ # @return [Proc, #call] The parser proc or callable object
151
+ # @return [nil] If no suitable parser is found
121
152
  def parser
122
153
  return @parser if defined?(@parser)
123
154
 
@@ -133,16 +164,21 @@ module OAuth2
133
164
  end
134
165
  end
135
166
 
136
- OAuth2::Response.register_parser(:xml, ['text/xml', 'application/rss+xml', 'application/rdf+xml', 'application/atom+xml', 'application/xml']) do |body|
167
+ # Register XML parser
168
+ # @api private
169
+ OAuth2::Response.register_parser(:xml, ["text/xml", "application/rss+xml", "application/rdf+xml", "application/atom+xml", "application/xml"]) do |body|
137
170
  next body unless body.respond_to?(:to_str)
138
171
 
139
172
  MultiXml.parse(body)
140
173
  end
141
174
 
142
- OAuth2::Response.register_parser(:json, ['application/json', 'text/javascript', 'application/hal+json', 'application/vnd.collection+json', 'application/vnd.api+json', 'application/problem+json']) do |body|
175
+ # Register JSON parser
176
+ # @api private
177
+ OAuth2::Response.register_parser(:json, ["application/json", "text/javascript", "application/hal+json", "application/vnd.collection+json", "application/vnd.api+json", "application/problem+json"]) do |body|
143
178
  next body unless body.respond_to?(:to_str)
144
179
 
145
- body = body.dup.force_encoding(::Encoding::ASCII_8BIT) if body.respond_to?(:force_encoding)
180
+ body = body.dup.force_encoding(Encoding::ASCII_8BIT) if body.respond_to?(:force_encoding)
181
+ next body if body.respond_to?(:empty?) && body.empty?
146
182
 
147
- ::JSON.parse(body)
183
+ JSON.parse(body)
148
184
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'jwt'
3
+ require "jwt"
4
4
 
5
5
  module OAuth2
6
6
  module Strategy
@@ -34,7 +34,7 @@ module OAuth2
34
34
  #
35
35
  # @raise [NotImplementedError]
36
36
  def authorize_url
37
- raise(NotImplementedError, 'The authorization endpoint is not used in this strategy')
37
+ raise(NotImplementedError, "The authorization endpoint is not used in this strategy")
38
38
  end
39
39
 
40
40
  # Retrieve an access token given the specified client.
@@ -87,15 +87,18 @@ module OAuth2
87
87
 
88
88
  def build_request(assertion, request_opts = {})
89
89
  {
90
- grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
90
+ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
91
91
  assertion: assertion,
92
92
  }.merge(request_opts)
93
93
  end
94
94
 
95
95
  def build_assertion(claims, encoding_opts)
96
- raise ArgumentError.new(message: 'Please provide an encoding_opts hash with :algorithm and :key') if !encoding_opts.is_a?(Hash) || (%i[algorithm key] - encoding_opts.keys).any?
96
+ raise ArgumentError.new(message: "Please provide an encoding_opts hash with :algorithm and :key") if !encoding_opts.is_a?(Hash) || (%i[algorithm key] - encoding_opts.keys).any?
97
97
 
98
- JWT.encode(claims, encoding_opts[:key], encoding_opts[:algorithm])
98
+ headers = {}
99
+ headers[:kid] = encoding_opts[:kid] if encoding_opts.key?(:kid)
100
+
101
+ JWT.encode(claims, encoding_opts[:key], encoding_opts[:algorithm], headers)
99
102
  end
100
103
  end
101
104
  end
@@ -4,13 +4,23 @@ module OAuth2
4
4
  module Strategy
5
5
  # The Authorization Code Strategy
6
6
  #
7
+ # OAuth 2.1 notes:
8
+ # - PKCE is required for all OAuth clients using the authorization code flow (especially public clients).
9
+ # This library does not enforce PKCE generation/verification; implement PKCE in your application when required.
10
+ # - Redirect URIs must be compared using exact string matching by the Authorization Server.
11
+ # This client forwards redirect_uri but does not perform server-side validation.
12
+ #
13
+ # References:
14
+ # - OAuth 2.1 draft: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13
15
+ # - OAuth for native apps (RFC 8252) and PKCE (RFC 7636)
16
+ #
7
17
  # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.1
8
18
  class AuthCode < Base
9
19
  # The required query parameters for the authorize URL
10
20
  #
11
21
  # @param [Hash] params additional query parameters
12
22
  def authorize_params(params = {})
13
- params.merge('response_type' => 'code', 'client_id' => @client.id)
23
+ params.merge("response_type" => "code", "client_id" => @client.id)
14
24
  end
15
25
 
16
26
  # The authorization URL endpoint of the provider
@@ -28,7 +38,7 @@ module OAuth2
28
38
  # @param [Hash] opts access_token_opts, @see Client#get_token
29
39
  # @note that you must also provide a :redirect_uri with most OAuth 2.0 providers
30
40
  def get_token(code, params = {}, opts = {})
31
- params = {'grant_type' => 'authorization_code', 'code' => code}.merge(@client.redirection_params).merge(params)
41
+ params = {"grant_type" => "authorization_code", "code" => code}.merge(@client.redirection_params).merge(params)
32
42
  params_dup = params.dup
33
43
  params.each_key do |key|
34
44
  params_dup[key.to_s] = params_dup.delete(key) if key.is_a?(Symbol)
@@ -40,7 +50,7 @@ module OAuth2
40
50
  private
41
51
 
42
52
  def assert_valid_params(params)
43
- raise(ArgumentError, 'client_secret is not allowed in authorize URL query params') if params.key?(:client_secret) || params.key?('client_secret')
53
+ raise(ArgumentError, "client_secret is not allowed in authorize URL query params") if params.key?(:client_secret) || params.key?("client_secret")
44
54
  end
45
55
  end
46
56
  end
@@ -10,7 +10,7 @@ module OAuth2
10
10
  #
11
11
  # @raise [NotImplementedError]
12
12
  def authorize_url
13
- raise(NotImplementedError, 'The authorization endpoint is not used in this strategy')
13
+ raise(NotImplementedError, "The authorization endpoint is not used in this strategy")
14
14
  end
15
15
 
16
16
  # Retrieve an access token given the specified client.
@@ -18,7 +18,7 @@ module OAuth2
18
18
  # @param [Hash] params additional params
19
19
  # @param [Hash] opts options
20
20
  def get_token(params = {}, opts = {})
21
- params = params.merge('grant_type' => 'client_credentials')
21
+ params = params.merge("grant_type" => "client_credentials")
22
22
  @client.get_token(params, opts)
23
23
  end
24
24
  end
@@ -4,13 +4,21 @@ module OAuth2
4
4
  module Strategy
5
5
  # The Implicit Strategy
6
6
  #
7
+ # IMPORTANT (OAuth 2.1): The Implicit grant (response_type=token) is omitted from the OAuth 2.1 draft specification.
8
+ # It remains here for backward compatibility with OAuth 2.0 providers. Prefer the Authorization Code flow with PKCE.
9
+ #
10
+ # References:
11
+ # - OAuth 2.1 draft: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13
12
+ # - Why drop implicit: https://aaronparecki.com/2019/12/12/21/its-time-for-oauth-2-dot-1
13
+ # - Background: https://fusionauth.io/learn/expert-advice/oauth/differences-between-oauth-2-oauth-2-1/
14
+ #
7
15
  # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-26#section-4.2
8
16
  class Implicit < Base
9
17
  # The required query parameters for the authorize URL
10
18
  #
11
19
  # @param [Hash] params additional query parameters
12
20
  def authorize_params(params = {})
13
- params.merge('response_type' => 'token', 'client_id' => @client.id)
21
+ params.merge("response_type" => "token", "client_id" => @client.id)
14
22
  end
15
23
 
16
24
  # The authorization URL endpoint of the provider
@@ -25,13 +33,13 @@ module OAuth2
25
33
  #
26
34
  # @raise [NotImplementedError]
27
35
  def get_token(*)
28
- raise(NotImplementedError, 'The token is accessed differently in this strategy')
36
+ raise(NotImplementedError, "The token is accessed differently in this strategy")
29
37
  end
30
38
 
31
39
  private
32
40
 
33
41
  def assert_valid_params(params)
34
- raise(ArgumentError, 'client_secret is not allowed in authorize URL query params') if params.key?(:client_secret) || params.key?('client_secret')
42
+ raise(ArgumentError, "client_secret is not allowed in authorize URL query params") if params.key?(:client_secret) || params.key?("client_secret")
35
43
  end
36
44
  end
37
45
  end
@@ -4,13 +4,21 @@ module OAuth2
4
4
  module Strategy
5
5
  # The Resource Owner Password Credentials Authorization Strategy
6
6
  #
7
+ # IMPORTANT (OAuth 2.1): The Resource Owner Password Credentials grant is omitted in OAuth 2.1.
8
+ # It remains here for backward compatibility with OAuth 2.0 providers. Prefer Authorization Code + PKCE.
9
+ #
10
+ # References:
11
+ # - OAuth 2.1 draft: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13
12
+ # - Okta explainer: https://developer.okta.com/blog/2019/12/13/oauth-2-1-how-many-rfcs
13
+ # - FusionAuth blog: https://fusionauth.io/blog/2020/04/15/whats-new-in-oauth-2-1
14
+ #
7
15
  # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.3
8
16
  class Password < Base
9
17
  # Not used for this strategy
10
18
  #
11
19
  # @raise [NotImplementedError]
12
20
  def authorize_url
13
- raise(NotImplementedError, 'The authorization endpoint is not used in this strategy')
21
+ raise(NotImplementedError, "The authorization endpoint is not used in this strategy")
14
22
  end
15
23
 
16
24
  # Retrieve an access token given the specified End User username and password.
@@ -19,9 +27,11 @@ module OAuth2
19
27
  # @param [String] password the End User password
20
28
  # @param [Hash] params additional params
21
29
  def get_token(username, password, params = {}, opts = {})
22
- params = {'grant_type' => 'password',
23
- 'username' => username,
24
- 'password' => password}.merge(params)
30
+ params = {
31
+ "grant_type" => "password",
32
+ "username" => username,
33
+ "password" => password,
34
+ }.merge(params)
25
35
  @client.get_token(params, opts)
26
36
  end
27
37
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module OAuth2
4
4
  module Version
5
- VERSION = '2.0.9'.freeze
5
+ VERSION = "2.0.17"
6
6
  end
7
7
  end
data/lib/oauth2.rb CHANGED
@@ -1,40 +1,81 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # includes modules from stdlib
4
- require 'cgi'
5
- require 'time'
4
+ require "cgi"
5
+ require "time"
6
6
 
7
7
  # third party gems
8
- require 'snaky_hash'
9
- require 'version_gem'
8
+ require "snaky_hash"
9
+ require "version_gem"
10
10
 
11
11
  # includes gem files
12
- require 'oauth2/version'
13
- require 'oauth2/error'
14
- require 'oauth2/authenticator'
15
- require 'oauth2/client'
16
- require 'oauth2/strategy/base'
17
- require 'oauth2/strategy/auth_code'
18
- require 'oauth2/strategy/implicit'
19
- require 'oauth2/strategy/password'
20
- require 'oauth2/strategy/client_credentials'
21
- require 'oauth2/strategy/assertion'
22
- require 'oauth2/access_token'
23
- require 'oauth2/response'
12
+ require_relative "oauth2/version"
13
+ require_relative "oauth2/filtered_attributes"
14
+ require_relative "oauth2/error"
15
+ require_relative "oauth2/authenticator"
16
+ require_relative "oauth2/client"
17
+ require_relative "oauth2/strategy/base"
18
+ require_relative "oauth2/strategy/auth_code"
19
+ require_relative "oauth2/strategy/implicit"
20
+ require_relative "oauth2/strategy/password"
21
+ require_relative "oauth2/strategy/client_credentials"
22
+ require_relative "oauth2/strategy/assertion"
23
+ require_relative "oauth2/access_token"
24
+ require_relative "oauth2/response"
24
25
 
25
26
  # The namespace of this library
27
+ #
28
+ # This module is the entry point and top-level namespace for the oauth2 gem.
29
+ # It exposes configuration, constants, and requires the primary public classes.
26
30
  module OAuth2
27
- DEFAULT_CONFIG = SnakyHash::SymbolKeyed.new(silence_extra_tokens_warning: false)
31
+ # When true, enables verbose HTTP logging via Faraday's logger middleware.
32
+ # Controlled by the OAUTH_DEBUG environment variable. Any case-insensitive
33
+ # value equal to "true" will enable debugging.
34
+ #
35
+ # @return [Boolean]
36
+ OAUTH_DEBUG = ENV.fetch("OAUTH_DEBUG", "false").casecmp("true").zero?
37
+
38
+ # Default configuration values for the oauth2 library.
39
+ #
40
+ # @example Toggle warnings
41
+ # OAuth2.configure do |config|
42
+ # config[:silence_extra_tokens_warning] = false
43
+ # config[:silence_no_tokens_warning] = false
44
+ # end
45
+ #
46
+ # @return [SnakyHash::SymbolKeyed] A mutable Hash-like config with symbol keys
47
+ DEFAULT_CONFIG = SnakyHash::SymbolKeyed.new(
48
+ silence_extra_tokens_warning: true,
49
+ silence_no_tokens_warning: true,
50
+ )
51
+
52
+ # The current runtime configuration for the library.
53
+ #
54
+ # @return [SnakyHash::SymbolKeyed]
28
55
  @config = DEFAULT_CONFIG.dup
56
+
29
57
  class << self
30
- attr_accessor :config
58
+ # Access the current configuration.
59
+ #
60
+ # Prefer using {OAuth2.configure} to mutate configuration.
61
+ #
62
+ # @return [SnakyHash::SymbolKeyed]
63
+ attr_reader :config
31
64
  end
65
+
66
+ # Configure global library behavior.
67
+ #
68
+ # Yields the mutable configuration object so callers can update settings.
69
+ #
70
+ # @yieldparam [SnakyHash::SymbolKeyed] config the configuration object
71
+ # @return [void]
32
72
  def configure
33
73
  yield @config
34
74
  end
35
75
  module_function :configure
36
76
  end
37
77
 
78
+ # Extend OAuth2::Version with VersionGem helpers to provide semantic version helpers.
38
79
  OAuth2::Version.class_eval do
39
80
  extend VersionGem::Basic
40
81
  end