1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
|
# frozen_string_literal: true
module HTTPX
module Plugins
#
# This plugin adds a shim +authorization+ method to the session, which will fill
# the HTTP Authorization header, and another, +bearer_auth+, which fill the "Bearer " prefix
# in its value.
#
# https://gitlab.com/os85/httpx/wikis/Auth#auth
#
module Auth
def self.subplugins
{
retries: AuthRetries,
}
end
# adds support for the following options:
#
# :auth_header_value :: the token to use as a string, or a callable which returns a string when called.
# :auth_header_type :: the authentication type to use in the "authorization" header value (i.e. "Bearer", "Digest"...)
# :generate_auth_value_on_retry :: callable which returns whether the request should regenerate the auth_header_value
# when the request is retried (this option will only work if the session also loads the
# <tt>:retries</tt> plugin).
module OptionsMethods
def option_auth_header_value(value)
value
end
def option_auth_header_type(value)
value
end
def option_generate_auth_value_on_retry(value)
raise TypeError, "`:generate_auth_value_on_retry` must be a callable" unless value.respond_to?(:call)
value
end
end
module InstanceMethods
def initialize(*)
super
@auth_header_value = nil
@skip_auth_header_value = false
end
def authorization(token = nil, auth_header_type: nil, &blk)
with(auth_header_type: auth_header_type, auth_header_value: token || blk)
end
def bearer_auth(token = nil, &blk)
authorization(token, auth_header_type: "Bearer", &blk)
end
def skip_auth_header
@skip_auth_header_value = true
yield
ensure
@skip_auth_header_value = false
end
def reset_auth_header_value!
@auth_header_value = nil
end
private
def send_request(request, *)
return super if @skip_auth_header_value || request.authorized?
@auth_header_value ||= generate_auth_token
request.authorize(@auth_header_value) if @auth_header_value
super
end
def generate_auth_token
return unless (auth_value = @options.auth_header_value)
auth_value = auth_value.call(self) if dynamic_auth_token?(auth_value)
auth_value
end
def dynamic_auth_token?(auth_header_value)
auth_header_value&.respond_to?(:call)
end
end
module RequestMethods
def initialize(*)
super
@auth_token_value = nil
end
def authorized?
!@auth_token_value.nil?
end
def unauthorize!
return unless (auth_value = @auth_token_value)
@headers.get("authorization").delete(auth_value)
@auth_token_value = nil
end
def authorize(auth_value)
if (auth_type = @options.auth_header_type)
auth_value = "#{auth_type} #{auth_value}"
end
@headers.add("authorization", auth_value)
@auth_token_value = auth_value
end
end
module AuthRetries
module InstanceMethods
private
def retryable_request?(request, response, options)
super || auth_error?(response, options)
end
def retryable_response?(response, options)
auth_error?(response, options) || super
end
def prepare_to_retry(request, response)
super
return unless auth_error?(response, request.options) ||
(@options.generate_auth_value_on_retry && @options.generate_auth_value_on_retry.call(response))
request.unauthorize!
@auth_header_value = generate_auth_token
end
def auth_error?(response, options)
response.is_a?(Response) && response.status == 401 && dynamic_auth_token?(options.auth_header_value)
end
end
end
end
register_plugin :auth, Auth
end
end
|