Class: Retailer::CallbackTokenService
- Inherits:
-
Object
- Object
- Retailer::CallbackTokenService
- Defined in:
- app/services/retailer/callback_token_service.rb
Overview
Generates and validates time-limited tokens for Oxylabs callback authentication.
Tokens are embedded in the callback URL to prevent unauthorized submissions.
Tokens are signed with ActiveSupport::MessageVerifier (Rails primitive)
under the namespaced verifier :oxylabs_callback and the explicit purpose
tag 'oxylabs_callback/v1' — defense in depth against a token signed for
a different feature being replayed against this webhook.
No legacy decoder: this service previously used JWT.encode / JWT.decode,
but those tokens cross neither verifier (different signature format). At
deploy time, any Oxylabs job already in flight will lose its callback
(the price-check itself completes on Oxylabs' side; we just never ingest
the WebhookLog). Recovery is to re-trigger the affected price-check from
the CatalogItem.
Uses API_HOSTNAME_WITHOUT_PORT constant for environment-aware URLs:
- Production: api.warmlyyours.com
- Staging: api.warmlyyours.ws
- Development: Uses dev tunnel (api-hostname.warmlyyours.dev)
Constant Summary collapse
- TOKEN_EXPIRY =
Token expiry.
24.hours
- PURPOSE =
Verifier purpose tag.
'oxylabs_callback/v1'
Class Method Summary collapse
-
.callback_url(catalog_item_id: nil) ⇒ String
Generate a callback URL for production/staging.
-
.dev_callback_url(catalog_item_id: nil) ⇒ String
Generate a dev callback URL for testing via Cloudflare tunnel.
-
.generate_token ⇒ String
Generate a verifier-signed token for callback authentication.
-
.valid_token?(token) ⇒ Boolean
Check if a token is valid.
-
.validate_token(token) ⇒ Hash?
Validate and decode a token.
Class Method Details
.callback_url(catalog_item_id: nil) ⇒ String
Generate a callback URL for production/staging.
Uses API_HOSTNAME_WITHOUT_PORT constant for environment-aware URLs.
63 64 65 66 67 68 |
# File 'app/services/retailer/callback_token_service.rb', line 63 def callback_url(catalog_item_id: nil) token = generate_token url = "https://#{API_HOSTNAME_WITHOUT_PORT}/webhooks/v1/oxylabs?token=#{CGI.escape(token)}" url += "&catalog_item_id=#{catalog_item_id}" if catalog_item_id url end |
.dev_callback_url(catalog_item_id: nil) ⇒ String
Generate a dev callback URL for testing via Cloudflare tunnel.
Uses hostname-specific subdomain: api-hostname.warmlyyours.dev
74 75 76 77 78 79 80 |
# File 'app/services/retailer/callback_token_service.rb', line 74 def dev_callback_url(catalog_item_id: nil) hostname = `hostname -s`.strip.downcase token = generate_token url = "https://api-#{hostname}.warmlyyours.dev/webhooks/v1/oxylabs?token=#{CGI.escape(token)}" url += "&catalog_item_id=#{catalog_item_id}" if catalog_item_id url end |
.generate_token ⇒ String
Generate a verifier-signed token for callback authentication.
38 39 40 41 |
# File 'app/services/retailer/callback_token_service.rb', line 38 def generate_token payload = { 'iat' => Time.current.to_i } verifier.generate(payload, expires_in: TOKEN_EXPIRY, purpose: PURPOSE) end |
.valid_token?(token) ⇒ Boolean
Check if a token is valid.
55 56 57 |
# File 'app/services/retailer/callback_token_service.rb', line 55 def valid_token?(token) validate_token(token).present? end |
.validate_token(token) ⇒ Hash?
Validate and decode a token.
46 47 48 49 50 |
# File 'app/services/retailer/callback_token_service.rb', line 46 def validate_token(token) return nil if token.blank? verifier.verified(token, purpose: PURPOSE) end |