class OAuth2::Client
The OAuth2::Client
class
Constants
- RESERVED_PARAM_KEYS
- RESERVED_REQ_KEYS
Attributes
Public Class Methods
Source
# File lib/oauth2/client.rb 50 def initialize(client_id, client_secret, options = {}, &block) 51 opts = options.dup 52 @id = client_id 53 @secret = client_secret 54 @site = opts.delete(:site) 55 ssl = opts.delete(:ssl) 56 warn("OAuth2::Client#initialize argument `extract_access_token` will be removed in oauth2 v3. Refactor to use `access_token_class`.") if opts[:extract_access_token] 57 @options = { 58 authorize_url: "oauth/authorize", 59 revoke_url: "oauth/revoke", 60 token_url: "oauth/token", 61 token_method: :post, 62 auth_scheme: :basic_auth, 63 connection_opts: {}, 64 connection_build: block, 65 max_redirects: 5, 66 raise_errors: true, 67 logger: ::Logger.new($stdout), 68 access_token_class: AccessToken, 69 }.merge(opts) 70 @options[:connection_opts][:ssl] = ssl if ssl 71 end
Initializes a new OAuth2::Client
instance using the Client
ID and Client
Secret registered to your application.
@param [String] client_id the client_id value @param [String] client_secret the client_secret value @param [Hash] options the options to configure the client @option options [String] :site the OAuth2
provider site host @option options [String] :authorize_url (‘/oauth/authorize’) absolute or relative URL path to the Authorization endpoint @option options [String] :revoke_url (‘/oauth/revoke’) absolute or relative URL path to the Revoke endpoint @option options [String] :token_url (‘/oauth/token’) absolute or relative URL path to the Token endpoint @option options [Symbol] :token_method (:post) HTTP method to use to request token (:get, :post, :post_with_query_string) @option options [Symbol] :auth_scheme (:basic_auth) the authentication scheme (:basic_auth, :request_body, :tls_client_auth, :private_key_jwt) @option options [Hash] :connection_opts ({}) Hash of connection options to pass to initialize Faraday @option options [Boolean] :raise_errors (true) whether to raise an OAuth2::Error
on responses with 400+ status codes @option options [Integer] :max_redirects (5) maximum number of redirects to follow @option options [Logger] :logger (::Logger.new($stdout)) Logger instance for HTTP request/response output; requires OAUTH_DEBUG to be true @option options [Class] :access_token_class (AccessToken
) class to use for access tokens; you can subclass OAuth2::AccessToken
, @version 2.0+ @option options [Hash] :ssl SSL options for Faraday
@yield [builder] The Faraday connection builder
Public Instance Methods
Source
# File lib/oauth2/client.rb 314 def assertion 315 @assertion ||= OAuth2::Strategy::Assertion.new(self) 316 end
The Assertion strategy
This allows for assertion-based authentication where an identity provider asserts the identity of the user or client application seeking access.
@see datatracker.ietf.org/doc/html/rfc7521 @see datatracker.ietf.org/doc/html/draft-ietf-oauth-assertions-01#section-4.1
@return [OAuth2::Strategy::Assertion] the initialized Assertion strategy
Source
# File lib/oauth2/client.rb 280 def auth_code 281 @auth_code ||= OAuth2::Strategy::AuthCode.new(self) 282 end
The Authorization Code strategy
@see datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.1
Source
# File lib/oauth2/client.rb 301 def client_credentials 302 @client_credentials ||= OAuth2::Strategy::ClientCredentials.new(self) 303 end
The Client
Credentials strategy
@see datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.4
Source
# File lib/oauth2/client.rb 85 def connection 86 @connection ||= 87 Faraday.new(site, options[:connection_opts]) do |builder| 88 oauth_debug_logging(builder) 89 if options[:connection_build] 90 options[:connection_build].call(builder) 91 else 92 builder.request(:url_encoded) # form-encode POST params 93 builder.adapter(Faraday.default_adapter) # make requests with Net::HTTP 94 end 95 end 96 end
The Faraday connection object
@return [Faraday::Connection] the initialized Faraday connection
Source
# File lib/oauth2/client.rb 208 def get_token(params, access_token_opts = {}, extract_access_token = nil, &block) 209 warn("OAuth2::Client#get_token argument `extract_access_token` will be removed in oauth2 v3. Refactor to use `access_token_class` on #initialize.") if extract_access_token 210 extract_access_token ||= options[:extract_access_token] 211 req_opts = params_to_req_opts(params) 212 response = request(http_method, token_url, req_opts, &block) 213 214 # In v1.4.x, the deprecated extract_access_token option retrieves the token from the response. 215 # We preserve this behavior here, but a custom access_token_class that implements #from_hash 216 # should be used instead. 217 if extract_access_token 218 parse_response_legacy(response, access_token_opts, extract_access_token) 219 else 220 parse_response(response, access_token_opts) 221 end 222 end
Retrieves an access token from the token endpoint using the specified parameters
@param [Hash] params a Hash of params for the token endpoint
* params can include a 'headers' key with a Hash of request headers * params can include a 'parse' key with the Symbol name of response parsing strategy (default: :automatic) * params can include a 'snaky' key to control snake_case conversion (default: false)
@param [Hash] access_token_opts options that will be passed to the AccessToken
initialization @param [Proc] extract_access_token (deprecated) a proc that can extract the access token from the response
@yield [opts] The block is passed the options being used to make the request @yieldparam [Hash] opts options being passed to the http library
@return [AccessToken, nil] the initialized AccessToken
instance, or nil if token extraction fails
and raise_errors is false
@note The extract_access_token parameter is deprecated and will be removed in oauth2 v3.
Use access_token_class on initialization instead.
@example
client.get_token( 'grant_type' => 'authorization_code', 'code' => 'auth_code_value', 'headers' => {'Authorization' => 'Basic ...'} )
Source
# File lib/oauth2/client.rb 270 def http_method 271 http_meth = options[:token_method].to_sym 272 return :post if http_meth == :post_with_query_string 273 274 http_meth 275 end
The HTTP Method of the request
@return [Symbol] HTTP verb, one of [:get, :post, :put, :delete]
Source
# File lib/oauth2/client.rb 287 def implicit 288 @implicit ||= OAuth2::Strategy::Implicit.new(self) 289 end
The Implicit strategy
@see datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-26#section-4.2
Source
# File lib/oauth2/client.rb 294 def password 295 @password ||= OAuth2::Strategy::Password.new(self) 296 end
The Resource Owner Password Credentials strategy
@see datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.3
Source
# File lib/oauth2/client.rb 335 def redirection_params 336 if options[:redirect_uri] 337 {"redirect_uri" => options[:redirect_uri]} 338 else 339 {} 340 end 341 end
The redirect_uri parameters, if configured
The redirect_uri query parameter is OPTIONAL (though encouraged) when requesting authorization. If it is provided at authorization time it MUST also be provided with the token exchange request.
Providing :redirect_uri to the OAuth2::Client
instantiation will take care of managing this.
@api semipublic
@see datatracker.ietf.org/doc/html/rfc6749#section-4.1 @see datatracker.ietf.org/doc/html/rfc6749#section-4.1.3 @see datatracker.ietf.org/doc/html/rfc6749#section-4.2.1 @see datatracker.ietf.org/doc/html/rfc6749#section-10.6
@return [Hash] the params to add to a request or URL
Source
# File lib/oauth2/client.rb 146 def request(verb, url, req_opts = {}, &block) 147 response = execute_request(verb, url, req_opts, &block) 148 status = response.status 149 150 case status 151 when 301, 302, 303, 307 152 req_opts[:redirect_count] ||= 0 153 req_opts[:redirect_count] += 1 154 return response if req_opts[:redirect_count] > options[:max_redirects] 155 156 if status == 303 157 verb = :get 158 req_opts.delete(:body) 159 end 160 location = response.headers["location"] 161 if location 162 full_location = response.response.env.url.merge(location) 163 request(verb, full_location, req_opts) 164 else 165 error = Error.new(response) 166 raise(error, "Got #{status} status code, but no Location header was present") 167 end 168 when 200..299, 300..399 169 # on non-redirecting 3xx statuses, return the response 170 response 171 when 400..599 172 if req_opts.fetch(:raise_errors, options[:raise_errors]) 173 error = Error.new(response) 174 raise(error) 175 end 176 177 response 178 else 179 error = Error.new(response) 180 raise(error, "Unhandled status code value of #{status}") 181 end 182 end
Makes a request relative to the specified site root.
Updated HTTP 1.1 specification (IETF RFC 7231) relaxed the original constraint (IETF RFC 2616),
allowing the use of relative URLs in Location headers.
@see datatracker.ietf.org/doc/html/rfc7231#section-7.1.2
@param [Symbol] verb one of [:get, :post, :put, :delete] @param [String] url URL path of request @param [Hash] req_opts the options to make the request with @option req_opts [Hash] :params additional query parameters for the URL of the request @option req_opts [Hash, String] :body the body of the request @option req_opts [Hash] :headers http request headers @option req_opts [Boolean] :raise_errors whether to raise an OAuth2::Error
on 400+ status
code response for this request. Overrides the client instance setting.
@option req_opts [Symbol] :parse @see Response::initialize @option req_opts [Boolean] :snaky (true) @see Response::initialize
@yield [req] The block is passed the request being made, allowing customization @yieldparam [Faraday::Request] req The request object that can be modified @see Faraday::Connection#run_request
@return [OAuth2::Response] the response from the request
Source
# File lib/oauth2/client.rb 257 def revoke_token(token, token_type_hint = nil, params = {}, &block) 258 params[:token_method] ||= :post_with_query_string 259 req_opts = params_to_req_opts(params) 260 req_opts[:params] ||= {} 261 req_opts[:params][:token] = token 262 req_opts[:params][:token_type_hint] = token_type_hint if token_type_hint 263 264 request(http_method, revoke_url, req_opts, &block) 265 end
Makes a request to revoke a token at the authorization server
@param [String] token The token to be revoked @param [String, nil] token_type_hint A hint about the type of the token being revoked (e.g., ‘access_token’ or ‘refresh_token’) @param [Hash] params additional parameters for the token revocation @option params [Symbol] :parse (:automatic) parsing strategy for the response @option params [Boolean] :snaky (true) whether to convert response keys to snake_case @option params [Symbol] :token_method (:post_with_query_string) overrides OAuth2::Client#options[:token_method]
@option params [Hash] :headers Additional request headers
@yield [req] The block is passed the request being made, allowing customization @yieldparam [Faraday::Request] req The request object that can be modified
@return [OAuth2::Response] OAuth2::Response
instance
@api public
@note If the token passed to the request
is an access token, the server MAY revoke the respective refresh token as well.
@note If the token passed to the request
is a refresh token and the authorization server supports the revocation of access tokens, then the authorization server SHOULD also invalidate all access tokens based on the same authorization grant
@note If the server responds with HTTP status code 503, your code must
assume the token still exists and may retry after a reasonable delay. The server may include a "Retry-After" header in the response to indicate how long the service is expected to be unavailable to the requesting client.
@see datatracker.ietf.org/doc/html/rfc7009 @see datatracker.ietf.org/doc/html/rfc7009#section-2.1
Source
# File lib/oauth2/client.rb 119 def revoke_url(params = nil) 120 connection.build_url(options[:revoke_url], params).to_s 121 end
The revoke endpoint URL of the OAuth2
provider
@param [Hash, nil] params additional query parameters @return [String] the constructed revoke URL
Source
# File lib/oauth2/client.rb 77 def site=(value) 78 @connection = nil 79 @site = value 80 end
Set the site host
@param [String] value the OAuth2
provider site host @return [String] the site host value
Source
# File lib/oauth2/client.rb 111 def token_url(params = nil) 112 connection.build_url(options[:token_url], params).to_s 113 end
The token endpoint URL of the OAuth2
provider
@param [Hash, nil] params additional query parameters @return [String] the constructed token URL
Private Instance Methods
Source
# File lib/oauth2/client.rb 467 def authenticator 468 Authenticator.new(id, secret, options[:auth_scheme]) 469 end
Returns the authenticator object
@return [Authenticator] the initialized Authenticator
Source
# File lib/oauth2/client.rb 533 def build_access_token(response, access_token_opts, access_token_class) 534 access_token_class.from_hash(self, response.parsed.merge(access_token_opts)).tap do |access_token| 535 access_token.response = response if access_token.respond_to?(:response=) 536 end 537 end
Creates an access token instance from response data using the specified token class
@param [OAuth2::Response] response the OAuth2::Response
from the token endpoint @param [Hash] access_token_opts additional options to pass to the AccessToken
initialization @param [Class] access_token_class the class that should be used to create access token instances
@return [AccessToken] an initialized AccessToken
instance with response data
@note If the access token class responds to response=, the full response object will be set
@api private
Source
# File lib/oauth2/client.rb 552 def build_access_token_legacy(response, access_token_opts, extract_access_token) 553 extract_access_token.call(self, response.parsed.merge(access_token_opts)) 554 rescue 555 # An error will be raised by the called if nil is returned and options[:raise_errors] is truthy, so this rescue is but temporary. 556 # Unfortunately, it does hide the real error, but this is deprecated legacy code, 557 # and this was effectively the long-standing pre-existing behavior, so there is little point in changing it. 558 nil 559 end
Builds an access token using a legacy extraction proc
@deprecated Use {#build_access_token} instead
@param [OAuth2::Response] response the OAuth2::Response
from the token endpoint @param [Hash] access_token_opts additional options to pass to the access token extraction @param [Proc] extract_access_token a proc that takes client and token hash as arguments
and returns an access token instance
@return [AccessToken, nil] the access token instance if extraction succeeds,
nil if any error occurs during extraction
@api private
Source
# File lib/oauth2/client.rb 440 def execute_request(verb, url, opts = {}) 441 url = connection.build_url(url).to_s 442 # See: Hash#partition https://bugs.ruby-lang.org/issues/16252 443 req_opts, oauth_opts = opts. 444 partition { |k, _v| RESERVED_REQ_KEYS.include?(k.to_s) }. 445 map { |p| Hash[p] } 446 447 begin 448 response = connection.run_request(verb, url, req_opts[:body], req_opts[:headers]) do |req| 449 req.params.update(req_opts[:params]) if req_opts[:params] 450 yield(req) if block_given? 451 end 452 rescue Faraday::ConnectionFailed => e 453 raise ConnectionError, e 454 rescue Faraday::TimeoutError => e 455 raise TimeoutError, e 456 end 457 458 parse = oauth_opts.key?(:parse) ? oauth_opts.delete(:parse) : Response::DEFAULT_OPTIONS[:parse] 459 snaky = oauth_opts.key?(:snaky) ? oauth_opts.delete(:snaky) : Response::DEFAULT_OPTIONS[:snaky] 460 461 Response.new(response, parse: parse, snaky: snaky) 462 end
Executes an HTTP request with error handling and response processing
@param [Symbol] verb the HTTP method to use (:get, :post, :put, :delete) @param [String] url the URL for the request @param [Hash] opts the request options @option opts [Hash] :body the request body @option opts [Hash] :headers the request headers @option opts [Hash] :params the query parameters to append to the URL @option opts [Symbol, nil] :parse (:automatic) parsing strategy for the response @option opts [Boolean] :snaky (true) whether to convert response keys to snake_case
@yield [req] gives access to the request object before sending @yieldparam [Faraday::Request] req the request object that can be modified
@return [OAuth2::Response] the response wrapped in an OAuth2::Response
object
@raise [OAuth2::ConnectionError] when there’s a network error @raise [OAuth2::TimeoutError] when the request times out
@api private
Source
# File lib/oauth2/client.rb 561 def oauth_debug_logging(builder) 562 builder.response(:logger, options[:logger], bodies: true) if OAuth2::OAUTH_DEBUG 563 end
Source
# File lib/oauth2/client.rb 357 def params_to_req_opts(params) 358 parse, snaky, snaky_hash_klass, token_method, params, headers = parse_snaky_params_headers(params) 359 req_opts = { 360 raise_errors: options[:raise_errors], 361 token_method: token_method || options[:token_method], 362 parse: parse, 363 snaky: snaky, 364 snaky_hash_klass: snaky_hash_klass, 365 } 366 if req_opts[:token_method] == :post 367 # NOTE: If proliferation of request types continues, we should implement a parser solution for Request, 368 # just like we have with Response. 369 req_opts[:body] = if headers["Content-Type"] == "application/json" 370 params.to_json 371 else 372 params 373 end 374 375 req_opts[:headers] = {"Content-Type" => "application/x-www-form-urlencoded"} 376 else 377 req_opts[:params] = params 378 req_opts[:headers] = {} 379 end 380 req_opts[:headers].merge!(headers) 381 req_opts 382 end
Processes request parameters and transforms them into request options
@param [Hash] params the request parameters to process @option params [Symbol] :parse (:automatic) parsing strategy for the response @option params [Boolean] :snaky (true) whether to convert response keys to snake_case @option params [Class] :snaky_hash_klass (SnakyHash::StringKeyed) class to use for snake_case hash conversion @option params [Symbol] :token_method (:post) HTTP method to use for token request @option params [Hash] :headers Additional HTTP headers for the request
@return [Hash] the processed request options
@api private
Source
# File lib/oauth2/client.rb 508 def parse_response(response, access_token_opts) 509 access_token_class = options[:access_token_class] 510 data = response.parsed 511 512 unless data.is_a?(Hash) && !data.empty? 513 return unless options[:raise_errors] 514 515 error = Error.new(response) 516 raise(error) 517 end 518 519 build_access_token(response, access_token_opts, access_token_class) 520 end
Parses the OAuth response and builds an access token using the configured access token class
@param [OAuth2::Response] response the OAuth2::Response
from the token endpoint @param [Hash] access_token_opts options to pass to the AccessToken
initialization
@return [AccessToken] the initialized AccessToken
instance
@raise [OAuth2::Error] if the response is empty/invalid and the raise_errors option is true
@api private
Source
# File lib/oauth2/client.rb 485 def parse_response_legacy(response, access_token_opts, extract_access_token) 486 access_token = build_access_token_legacy(response, access_token_opts, extract_access_token) 487 488 return access_token if access_token 489 490 if options[:raise_errors] 491 error = Error.new(response) 492 raise(error) 493 end 494 495 nil 496 end
Parses the OAuth response and builds an access token using legacy extraction method
@deprecated Use {#parse_response} instead
@param [OAuth2::Response] response the OAuth2::Response
from the token endpoint @param [Hash] access_token_opts options to pass to the AccessToken
initialization @param [Proc] extract_access_token proc to extract the access token from response
@return [AccessToken, nil] the initialized AccessToken
if successful, nil if extraction fails
and raise_errors option is false
@raise [OAuth2::Error] if response indicates an error and raise_errors option is true
@api private
Source
# File lib/oauth2/client.rb 402 def parse_snaky_params_headers(params) 403 params = params.map do |key, value| 404 if RESERVED_PARAM_KEYS.include?(key) 405 [key.to_sym, value] 406 else 407 [key, value] 408 end 409 end.to_h 410 parse = params.key?(:parse) ? params.delete(:parse) : Response::DEFAULT_OPTIONS[:parse] 411 snaky = params.key?(:snaky) ? params.delete(:snaky) : Response::DEFAULT_OPTIONS[:snaky] 412 snaky_hash_klass = params.key?(:snaky_hash_klass) ? params.delete(:snaky_hash_klass) : Response::DEFAULT_OPTIONS[:snaky_hash_klass] 413 token_method = params.delete(:token_method) if params.key?(:token_method) 414 params = authenticator.apply(params) 415 # authenticator may add :headers, and we separate them from params here 416 headers = params.delete(:headers) || {} 417 [parse, snaky, snaky_hash_klass, token_method, params, headers] 418 end
Processes and transforms parameters for OAuth requests
@param [Hash] params the input parameters to process @option params [Symbol] :parse (:automatic) parsing strategy for the response @option params [Boolean] :snaky (true) whether to convert response keys to snake_case @option params [Class] :snaky_hash_klass (SnakyHash::StringKeyed) class to use for snake_case hash conversion @option params [Symbol] :token_method overrides the default token method for this request @option params [Hash] :headers HTTP headers for the request
@return [Array<(Symbol, Boolean, Class, Symbol, Hash, Hash)>] Returns an array containing:
- parse strategy (Symbol) - snaky flag for response key transformation (Boolean) - hash class for snake_case conversion (Class) - token method override (Symbol, nil) - processed parameters (Hash) - HTTP headers (Hash)
@api private