reputable 0.1.2 → 0.1.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5cfe6bbd421f190d87e5f0be242142988e269d8df74186f85f66c8f5996f115f
4
- data.tar.gz: 4db1343a290ef95d775b0694a50d84d48c861af293bf0d3e4d388e44c031c9d0
3
+ metadata.gz: 0c39e960378d885e97cac40ebc6147ea8f41bc9276a24e341011edd7590090b9
4
+ data.tar.gz: '09a3302c4e84261dd2b6941d87747297d37aa6458bc23fdd181d05fe715124be'
5
5
  SHA512:
6
- metadata.gz: 211cb949190c13bd0f3d8f6c4f0296a84c92653e64c2971d5866e0606cc777ebfe356a8a73d10e5a269f6701105372f9c4c8203f90bf75f279958b385874c3ad
7
- data.tar.gz: 14dbcf3338f725de441618100e0b57e99314974e0ff89b787c45141dcec56fa6c230c9acaa69feaed42233a87cf10add2cc2ccca2e80883eddb656779821ace6
6
+ metadata.gz: aa66be8227fbe97f9b270ec901642c397f59ec9cd39e6a8dc16dcfe7ab8d09c3b7ce7984d3ddf66d00a74d319a3b05fa8167b8b1a45fca6c1da84a20917c80f3
7
+ data.tar.gz: 36166971d3eec60a629a03992968381d57f7b52b3cd43f8c9876ebbf99528e4ed4fe6370b1c41abdb904c7100d970b7e58d032228f4c7c22187c283466d1e0bf
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- reputable (0.1.2)
4
+ reputable (0.1.3)
5
5
  connection_pool (~> 2.2)
6
6
  redis (>= 4.0, < 6.0)
7
7
 
data/README.md CHANGED
@@ -135,6 +135,11 @@ Reputable.configure do |config|
135
135
  REMOTE_ADDR
136
136
  ]
137
137
 
138
+ # Verification Configuration
139
+ # Supports comma-separated list in REPUTABLE_TRUSTED_KEYS or single key in REPUTABLE_TRUSTED_KEY
140
+ config.trusted_keys = ENV['REPUTABLE_TRUSTED_KEYS']&.split(',') || ENV['REPUTABLE_TRUSTED_KEY']
141
+ config.verification_base_url = ENV['REPUTABLE_VERIFY_URL'] # Base URL of your Reputable API (e.g., https://dev-new-1.verify.reputable.click)
142
+
138
143
  # Error callback (optional)
139
144
  config.on_error = ->(error, context) {
140
145
  # Report to your error tracking service
@@ -359,6 +364,78 @@ Reputable.lookup_reputation(:ip, request.ip)
359
364
 
360
365
  ---
361
366
 
367
+ ## User Verification & Trust Flow
368
+
369
+ When you identify a suspicious user (e.g., high risk score or specific tag), you can redirect them to the Reputable verification page. This page performs advanced browser checks and challenges (CAPTCHA) if necessary.
370
+
371
+ ### 1. Generating a Signed Redirect URL
372
+
373
+ To effectively hand off verification handling to Reputable, generate a signed verification URL:
374
+
375
+ ```ruby
376
+ # In your controller
377
+ if suspicious_activity_detected?
378
+ redirect_url = Reputable.verification_url(
379
+ return_url: request.original_url, # Where to send them back after verification
380
+ failure_url: root_url, # Optional: where to send if they fail/garbage token
381
+ session_id: session.id # Optional: link specific session
382
+ )
383
+
384
+ redirect_to redirect_url
385
+ end
386
+ ```
387
+
388
+ ### 2. Handling the Return Redirect
389
+
390
+ When the user passes verification (or is determined to be already trusted/clean), they are immediately redirected back to your `return_url` with signed parameters.
391
+
392
+ **Middleware (Automatic Handling):**
393
+ If you are using `Reputable::Middleware` (recommended), this is handled automatically. The middleware detects the return parameters, verifies the signature, and sets `env['reputable.verified'] = true`.
394
+
395
+ ```ruby
396
+ # In your controller
397
+ if request.env['reputable.verified']
398
+ # User just passed verification!
399
+ # You might want to graduate them to trusted status locally
400
+ trust_current_ip(reason: 'interactive_verification')
401
+ end
402
+ ```
403
+
404
+ **Manual Verification:**
405
+ If you need to verify manually (e.g., custom controller logic):
406
+
407
+ ```ruby
408
+ # In your controller action (the return_url target)
409
+ if params[:reputable_signature]
410
+ if Reputable.verify_redirect_return(params)
411
+ # Signature is VALID. Meaning Reputable actually sent this user back.
412
+
413
+ # Inspect outcomes
414
+ status = params[:reputable_status] # 'pass', 'fail'
415
+ outcome = params[:reputable_outcome] # 'trusted_verified', 'trusted_behavior', 'unknown'
416
+ country = params[:reputable_country] # 'US', 'DE', etc.
417
+
418
+ if status == 'pass'
419
+ # Grant access
420
+ end
421
+ else
422
+ # Invalid signature - potential tampering attempt!
423
+ render plain: "Verification failed", status: 403
424
+ end
425
+ end
426
+ ```
427
+
428
+ **Return Parameters:**
429
+ The return URL will contain:
430
+ - `reputable_status`: `pass` or `fail`
431
+ - `reputable_session_id`: The session ID you provided
432
+ - `reputable_outcome`: The specific reputation outcome (e.g., `trusted_verified`)
433
+ - `reputable_ignore_analytics`: 'true'/'false' flag
434
+ - `reputable_country`: ISO country code
435
+ - `reputable_signature`: HMAC-SHA256 signature of the above
436
+
437
+ ---
438
+
362
439
  ## Resilience & Failsafe Features
363
440
 
364
441
  ### Never Breaks Your App
@@ -14,7 +14,7 @@ module Reputable
14
14
  :default_ttls, :pool_size, :pool_timeout,
15
15
  :connect_timeout, :read_timeout, :write_timeout,
16
16
  :ssl_params, :trusted_proxies, :ip_header_priority,
17
- :on_error, :secret_key, :verification_base_url
17
+ :on_error, :trusted_keys, :verification_base_url
18
18
 
19
19
  # Default TTLs in seconds (0 = forever)
20
20
  DEFAULT_TTLS = {
@@ -63,8 +63,34 @@ module Reputable
63
63
  @trusted_proxies = nil # Additional trusted proxy IPs/ranges
64
64
  @ip_header_priority = DEFAULT_IP_HEADERS.dup
65
65
  @on_error = nil # Optional error callback: ->(error, context) { ... }
66
- @secret_key = ENV["REPUTABLE_SECRET_KEY"]
67
- @verification_base_url = ENV.fetch("REPUTABLE_VERIFY_URL", "https://api.reputable.click/_reputable/verify")
66
+
67
+ # Support multiple trusted keys (comma-separated), fallback to old secret key env var
68
+ @trusted_keys = []
69
+ if ENV["REPUTABLE_TRUSTED_KEYS"]
70
+ @trusted_keys = ENV["REPUTABLE_TRUSTED_KEYS"].split(",").map(&:strip)
71
+ elsif ENV["REPUTABLE_TRUSTED_KEY"]
72
+ @trusted_keys = [ENV["REPUTABLE_TRUSTED_KEY"]]
73
+ elsif ENV["REPUTABLE_SECRET_KEY"]
74
+ @trusted_keys = [ENV["REPUTABLE_SECRET_KEY"]]
75
+ end
76
+ @verification_base_url = ENV.fetch("REPUTABLE_VERIFY_URL", "https://api.reputable.click")
77
+ end
78
+
79
+ # Alias for backward compatibility
80
+ def secret_key
81
+ @trusted_keys.first
82
+ end
83
+
84
+ def secret_key=(key)
85
+ @trusted_keys = [key]
86
+ end
87
+
88
+ def trusted_keys=(keys)
89
+ @trusted_keys = Array(keys)
90
+ end
91
+
92
+ def trusted_keys
93
+ @trusted_keys
68
94
  end
69
95
 
70
96
  # Check if Reputable is enabled (can be disabled via ENV)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Reputable
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.4"
5
5
  end
data/lib/reputable.rb CHANGED
@@ -127,18 +127,24 @@ module Reputable
127
127
 
128
128
  # Generate a signed verification URL
129
129
  def verification_url(return_url:, failure_url: nil, session_id: nil)
130
- secret = configuration.secret_key
131
- unless secret
132
- logger&.warn "Reputable: Missing secret_key, cannot generate verification URL"
133
- return return_url
130
+ keys = configuration.trusted_keys
131
+ if keys.nil? || keys.empty?
132
+ logger&.warn "Reputable: Missing trusted_keys, cannot generate verification URL"
133
+ return return_url
134
134
  end
135
-
135
+
136
+ # Use the first key for signing new requests
137
+ secret = keys.first
138
+
136
139
  base_url = configuration.verification_base_url
137
-
140
+ # Ensure base_url doesn't have a trailing slash, then append the verify path
141
+ base_url = base_url.chomp("/")
142
+ verify_url = "#{base_url}/_reputable/verify"
143
+
138
144
  # JWT Header
139
145
  header = { alg: "HS256", typ: "JWT" }
140
146
  encoded_header = base64url_encode(JSON.generate(header))
141
-
147
+
142
148
  # JWT Payload
143
149
  payload = {
144
150
  returnUrl: return_url,
@@ -147,15 +153,15 @@ module Reputable
147
153
  iat: Time.now.to_i
148
154
  }
149
155
  encoded_payload = base64url_encode(JSON.generate(payload))
150
-
156
+
151
157
  # Signature
152
158
  data = "#{encoded_header}.#{encoded_payload}"
153
159
  signature = OpenSSL::HMAC.digest("SHA256", secret, data)
154
160
  encoded_signature = base64url_encode(signature)
155
-
161
+
156
162
  token = "#{data}.#{encoded_signature}"
157
-
158
- "#{base_url}?token=#{token}"
163
+
164
+ "#{verify_url}?token=#{token}"
159
165
  end
160
166
 
161
167
  # Verify the signature of a redirect return
@@ -165,19 +171,35 @@ module Reputable
165
171
  status = params["reputable_status"]
166
172
  session_id = params["reputable_session_id"]
167
173
  signature = params["reputable_signature"]
174
+ outcome = params["reputable_outcome"]
175
+ ignore_analytics = params["reputable_ignore_analytics"]
176
+ country = params["reputable_country"] || ""
168
177
 
169
178
  return false unless status && session_id && signature
170
179
 
171
- secret = configuration.secret_key
172
- unless secret
173
- logger&.warn "Reputable: Missing secret_key, cannot verify redirect"
180
+ keys = configuration.trusted_keys
181
+ if keys.nil? || keys.empty?
182
+ logger&.warn "Reputable: Missing trusted_keys, cannot verify redirect"
174
183
  return false
175
184
  end
176
185
 
177
- data = "#{status}:#{session_id}"
178
- expected_signature = OpenSSL::HMAC.hexdigest("SHA256", secret, data)
186
+ # Reconstruct data string: status:sessionId:outcome:ignoreAnalytics:country
187
+ # Note: optional params default to empty strings if missing in reconstruction logic on server
188
+ data_parts = [
189
+ status,
190
+ session_id,
191
+ outcome || "",
192
+ ignore_analytics.nil? ? "" : ignore_analytics,
193
+ country
194
+ ]
195
+
196
+ data = data_parts.join(":")
179
197
 
180
- secure_compare(expected_signature, signature)
198
+ # Iterate through all trusted keys to find a match
199
+ keys.any? do |key|
200
+ expected_signature = OpenSSL::HMAC.hexdigest("SHA256", key, data)
201
+ secure_compare(expected_signature, signature)
202
+ end
181
203
  end
182
204
 
183
205
  private
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reputable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Reputable
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-12-25 00:00:00.000000000 Z
11
+ date: 2025-12-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis