mauth-client 6.4.3 → 7.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +6 -3
- data/.ruby-version +1 -1
- data/.travis.yml +3 -11
- data/Appraisals +1 -5
- data/CHANGELOG.md +15 -0
- data/Gemfile +16 -0
- data/README.md +81 -44
- data/Rakefile +20 -12
- data/UPGRADE_GUIDE.md +21 -0
- data/doc/mauth-client_CLI.md +1 -11
- data/examples/Gemfile +0 -1
- data/examples/README.md +14 -13
- data/examples/get_country_info.rb +44 -0
- data/exe/mauth-client +1 -23
- data/gemfiles/faraday_1.x.gemfile +17 -1
- data/gemfiles/faraday_2.x.gemfile +16 -0
- data/lib/mauth/client/{local_authenticator.rb → authenticator.rb} +124 -3
- data/lib/mauth/client/security_token_cacher.rb +20 -13
- data/lib/mauth/client.rb +21 -101
- data/lib/mauth/config_env.rb +81 -0
- data/lib/mauth/private_key_helper.rb +30 -0
- data/lib/mauth/version.rb +1 -1
- data/mauth-client.gemspec +5 -17
- metadata +30 -198
- data/.fossa.yml +0 -14
- data/doc/mauth.yml.md +0 -84
- data/examples/Gemfile.lock +0 -69
- data/examples/config.yml +0 -12
- data/examples/get_user_info.rb +0 -58
- data/gemfiles/faraday_0.x.gemfile +0 -7
- data/lib/mauth/client/authenticator_base.rb +0 -133
- data/lib/mauth/client/remote_authenticator.rb +0 -85
- data/lib/mauth/dice_bag/mauth.rb.dice +0 -12
- data/lib/mauth/dice_bag/mauth.yml.dice +0 -18
- data/lib/mauth/dice_bag/mauth_key.dice +0 -1
- data/lib/mauth/dice_bag/mauth_templates.rb +0 -21
@@ -1,133 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# methods common to RemoteRequestAuthenticator and LocalAuthenticator
|
4
|
-
|
5
|
-
module MAuth
|
6
|
-
class Client
|
7
|
-
module AuthenticatorBase
|
8
|
-
ALLOWED_DRIFT_SECONDS = 300
|
9
|
-
|
10
|
-
# takes an incoming request or response object, and returns whether
|
11
|
-
# the object is authentic according to its signature.
|
12
|
-
def authentic?(object)
|
13
|
-
log_authentication_request(object)
|
14
|
-
begin
|
15
|
-
authenticate!(object)
|
16
|
-
true
|
17
|
-
rescue InauthenticError, MAuthNotPresent, MissingV2Error
|
18
|
-
false
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# raises InauthenticError unless the given object is authentic. Will only
|
23
|
-
# authenticate with v2 if the environment variable V2_ONLY_AUTHENTICATE
|
24
|
-
# is set. Otherwise will fall back to v1 when v2 authentication fails
|
25
|
-
def authenticate!(object)
|
26
|
-
case object.protocol_version
|
27
|
-
when 2
|
28
|
-
begin
|
29
|
-
authenticate_v2!(object)
|
30
|
-
rescue InauthenticError => e
|
31
|
-
raise e if v2_only_authenticate?
|
32
|
-
raise e if disable_fallback_to_v1_on_v2_failure?
|
33
|
-
|
34
|
-
object.fall_back_to_mws_signature_info
|
35
|
-
raise e unless object.signature
|
36
|
-
|
37
|
-
log_authentication_request(object)
|
38
|
-
authenticate_v1!(object)
|
39
|
-
logger.warn('Completed successful authentication attempt after fallback to v1')
|
40
|
-
end
|
41
|
-
when 1
|
42
|
-
if v2_only_authenticate?
|
43
|
-
# If v2 is required but not present and v1 is present we raise MissingV2Error
|
44
|
-
msg = 'This service requires mAuth v2 mcc-authentication header but only v1 x-mws-authentication is present'
|
45
|
-
logger.error(msg)
|
46
|
-
raise MissingV2Error, msg
|
47
|
-
end
|
48
|
-
|
49
|
-
authenticate_v1!(object)
|
50
|
-
else
|
51
|
-
sub_str = v2_only_authenticate? ? '' : 'X-MWS-Authentication header is blank, '
|
52
|
-
msg = "Authentication Failed. No mAuth signature present; #{sub_str}MCC-Authentication header is blank."
|
53
|
-
logger.warn("mAuth signature not present on #{object.class}. Exception: #{msg}")
|
54
|
-
raise MAuthNotPresent, msg
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
private
|
59
|
-
|
60
|
-
# NOTE: This log is likely consumed downstream and the contents SHOULD NOT
|
61
|
-
# be changed without a thorough review of downstream consumers.
|
62
|
-
def log_authentication_request(object)
|
63
|
-
object_app_uuid = object.signature_app_uuid || '[none provided]'
|
64
|
-
object_token = object.signature_token || '[none provided]'
|
65
|
-
logger.info(
|
66
|
-
'Mauth-client attempting to authenticate request from app with mauth' \
|
67
|
-
" app uuid #{object_app_uuid} to app with mauth app uuid #{client_app_uuid}" \
|
68
|
-
" using version #{object_token}."
|
69
|
-
)
|
70
|
-
end
|
71
|
-
|
72
|
-
def log_inauthentic(object, message)
|
73
|
-
logger.error("mAuth signature authentication failed for #{object.class}. Exception: #{message}")
|
74
|
-
end
|
75
|
-
|
76
|
-
def time_within_valid_range!(object, time_signed, now = Time.now)
|
77
|
-
return if (-ALLOWED_DRIFT_SECONDS..ALLOWED_DRIFT_SECONDS).cover?(now.to_i - time_signed)
|
78
|
-
|
79
|
-
msg = "Time verification failed. #{time_signed} not within #{ALLOWED_DRIFT_SECONDS} of #{now}"
|
80
|
-
log_inauthentic(object, msg)
|
81
|
-
raise InauthenticError, msg
|
82
|
-
end
|
83
|
-
|
84
|
-
# V1 helpers
|
85
|
-
def authenticate_v1!(object)
|
86
|
-
time_valid_v1!(object)
|
87
|
-
token_valid_v1!(object)
|
88
|
-
signature_valid_v1!(object)
|
89
|
-
end
|
90
|
-
|
91
|
-
def time_valid_v1!(object)
|
92
|
-
if object.x_mws_time.nil?
|
93
|
-
msg = 'Time verification failed. No x-mws-time present.'
|
94
|
-
log_inauthentic(object, msg)
|
95
|
-
raise InauthenticError, msg
|
96
|
-
end
|
97
|
-
time_within_valid_range!(object, object.x_mws_time.to_i)
|
98
|
-
end
|
99
|
-
|
100
|
-
def token_valid_v1!(object)
|
101
|
-
return if object.signature_token == MWS_TOKEN
|
102
|
-
|
103
|
-
msg = "Token verification failed. Expected #{MWS_TOKEN}; token was #{object.signature_token}"
|
104
|
-
log_inauthentic(object, msg)
|
105
|
-
raise InauthenticError, msg
|
106
|
-
end
|
107
|
-
|
108
|
-
# V2 helpers
|
109
|
-
def authenticate_v2!(object)
|
110
|
-
time_valid_v2!(object)
|
111
|
-
token_valid_v2!(object)
|
112
|
-
signature_valid_v2!(object)
|
113
|
-
end
|
114
|
-
|
115
|
-
def time_valid_v2!(object)
|
116
|
-
if object.mcc_time.nil?
|
117
|
-
msg = 'Time verification failed. No MCC-Time present.'
|
118
|
-
log_inauthentic(object, msg)
|
119
|
-
raise InauthenticError, msg
|
120
|
-
end
|
121
|
-
time_within_valid_range!(object, object.mcc_time.to_i)
|
122
|
-
end
|
123
|
-
|
124
|
-
def token_valid_v2!(object)
|
125
|
-
return if object.signature_token == MWSV2_TOKEN
|
126
|
-
|
127
|
-
msg = "Token verification failed. Expected #{MWSV2_TOKEN}; token was #{object.signature_token}"
|
128
|
-
log_inauthentic(object, msg)
|
129
|
-
raise InauthenticError, msg
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
@@ -1,85 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# methods for remotely authenticating a request by sending it to the mauth service
|
4
|
-
|
5
|
-
module MAuth
|
6
|
-
class Client
|
7
|
-
module RemoteRequestAuthenticator
|
8
|
-
private
|
9
|
-
|
10
|
-
# takes an incoming request object (no support for responses currently), and errors if the
|
11
|
-
# object is not authentic according to its signature
|
12
|
-
def signature_valid_v1!(object)
|
13
|
-
unless object.is_a?(MAuth::Request)
|
14
|
-
raise ArgumentError,
|
15
|
-
"Remote Authenticator can only authenticate requests; received #{object.inspect}"
|
16
|
-
end
|
17
|
-
|
18
|
-
authentication_ticket = {
|
19
|
-
'verb' => object.attributes_for_signing[:verb],
|
20
|
-
'app_uuid' => object.signature_app_uuid,
|
21
|
-
'client_signature' => object.signature,
|
22
|
-
'request_url' => object.attributes_for_signing[:request_url],
|
23
|
-
'request_time' => object.x_mws_time,
|
24
|
-
'b64encoded_body' => Base64.encode64(object.attributes_for_signing[:body] || '')
|
25
|
-
}
|
26
|
-
make_mauth_request(authentication_ticket)
|
27
|
-
end
|
28
|
-
|
29
|
-
def signature_valid_v2!(object)
|
30
|
-
unless object.is_a?(MAuth::Request)
|
31
|
-
msg = "Remote Authenticator can only authenticate requests; received #{object.inspect}"
|
32
|
-
raise ArgumentError, msg
|
33
|
-
end
|
34
|
-
|
35
|
-
authentication_ticket = {
|
36
|
-
verb: object.attributes_for_signing[:verb],
|
37
|
-
app_uuid: object.signature_app_uuid,
|
38
|
-
client_signature: object.signature,
|
39
|
-
request_url: object.attributes_for_signing[:request_url],
|
40
|
-
request_time: object.mcc_time,
|
41
|
-
b64encoded_body: Base64.encode64(object.attributes_for_signing[:body] || ''),
|
42
|
-
query_string: object.attributes_for_signing[:query_string],
|
43
|
-
token: object.signature_token
|
44
|
-
}
|
45
|
-
make_mauth_request(authentication_ticket)
|
46
|
-
end
|
47
|
-
|
48
|
-
def make_mauth_request(authentication_ticket)
|
49
|
-
begin
|
50
|
-
request_body = JSON.generate(authentication_ticket: authentication_ticket)
|
51
|
-
response = mauth_connection.post("/mauth/#{mauth_api_version}/authentication_tickets.json", request_body)
|
52
|
-
rescue ::Faraday::ConnectionFailed, ::Faraday::TimeoutError => e
|
53
|
-
msg = "mAuth service did not respond; received #{e.class}: #{e.message}"
|
54
|
-
logger.error("Unable to authenticate with MAuth. Exception #{msg}")
|
55
|
-
raise UnableToAuthenticateError, msg
|
56
|
-
end
|
57
|
-
case response.status
|
58
|
-
when 200..299
|
59
|
-
nil
|
60
|
-
when 412, 404
|
61
|
-
# the mAuth service responds with 412 when the given request is not authentically signed.
|
62
|
-
# older versions of the mAuth service respond with 404 when the given app_uuid
|
63
|
-
# does not exist, which is also considered to not be authentically signed. newer
|
64
|
-
# versions of the service respond 412 in all cases, so the 404 check may be removed
|
65
|
-
# when the old version of the mAuth service is out of service.
|
66
|
-
raise InauthenticError, "The mAuth service responded with #{response.status}: #{response.body}"
|
67
|
-
else
|
68
|
-
mauth_service_response_error(response)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def mauth_connection
|
73
|
-
@mauth_connection ||= begin
|
74
|
-
require 'faraday'
|
75
|
-
|
76
|
-
::Faraday.new(mauth_baseurl,
|
77
|
-
faraday_options.merge(headers: { 'Content-Type' => 'application/json' })) do |builder|
|
78
|
-
builder.use MAuth::Faraday::MAuthClientUserAgent
|
79
|
-
builder.adapter ::Faraday.default_adapter
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
@@ -1,12 +0,0 @@
|
|
1
|
-
<%= warning.as_yaml_comment %>
|
2
|
-
|
3
|
-
MAUTH_CONF = MAuth::Client.default_config
|
4
|
-
require 'mauth/rack'
|
5
|
-
# ResponseSigner OPTIONAL; only use if you are registered in mauth service
|
6
|
-
Rails.application.config.middleware.insert_after Rack::Runtime, MAuth::Rack::ResponseSigner, MAUTH_CONF
|
7
|
-
if Rails.env.test? || Rails.env.development?
|
8
|
-
require 'mauth/fake/rack'
|
9
|
-
Rails.application.config.middleware.insert_after MAuth::Rack::ResponseSigner, MAuth::Rack::RequestAuthenticationFaker, MAUTH_CONF
|
10
|
-
else
|
11
|
-
Rails.application.config.middleware.insert_after MAuth::Rack::ResponseSigner, MAuth::Rack::RequestAuthenticatorNoAppStatus, MAUTH_CONF
|
12
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
<%= warning.as_yaml_comment %>
|
2
|
-
|
3
|
-
common: &common
|
4
|
-
mauth_baseurl: <%= configured.mauth_url! || 'http://localhost:7000' %>
|
5
|
-
mauth_api_version: v1
|
6
|
-
app_uuid: <%= configured.mauth_app_uuid! || 'fb17460e-9868-11e1-8399-0090f5ccb4d3' %>
|
7
|
-
private_key_file: config/mauth_key
|
8
|
-
v2_only_authenticate: <%= configured.v2_only_authenticate || 'false' %>
|
9
|
-
v2_only_sign_requests: <%= configured.v2_only_sign_requests || 'false' %>
|
10
|
-
disable_fallback_to_v1_on_v2_failure: <%= configured.disable_fallback_to_v1_on_v2_failure || 'false' %>
|
11
|
-
v1_only_sign_requests: <%= configured.v1_only_sign_requests || 'true' %>
|
12
|
-
|
13
|
-
production:
|
14
|
-
<<: *common
|
15
|
-
development:
|
16
|
-
<<: *common
|
17
|
-
test:
|
18
|
-
<<: *common
|
@@ -1 +0,0 @@
|
|
1
|
-
<%= ensure_is_private_key(configured.mauth_private_key! || generate_private_key.to_s) %>
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'dice_bag'
|
4
|
-
|
5
|
-
class MauthTemplate < DiceBag::AvailableTemplates
|
6
|
-
def templates
|
7
|
-
['mauth.yml.dice', 'mauth_key.dice'].map do |template|
|
8
|
-
File.join(File.dirname(__FILE__), template)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
class MauthInitializerTemplate < DiceBag::AvailableTemplates
|
14
|
-
def templates_location
|
15
|
-
'config/initializers'
|
16
|
-
end
|
17
|
-
|
18
|
-
def templates
|
19
|
-
[File.join(File.dirname(__FILE__), 'mauth.rb.dice')] if Object.const_defined?(:Rails)
|
20
|
-
end
|
21
|
-
end
|