Class: Retailer::CallbackTokenService

Inherits:
Object
  • Object
show all
Defined in:
app/services/retailer/callback_token_service.rb

Overview

Generates and validates time-limited JWT tokens for Oxylabs callback authentication.
Tokens are embedded in the callback URL to prevent unauthorized submissions.

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)

Examples:

Generate a callback URL with token

Retailer::CallbackTokenService.callback_url(catalog_item_id: 123)

Validate a token

Retailer::CallbackTokenService.valid_token?(params[:token])

Constant Summary collapse

JWT_SECRET =
Rails.application.secret_key_base
JWT_ALGORITHM =
'HS256'
TOKEN_EXPIRY =
24.hours

Class Method Summary collapse

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

Parameters:

  • catalog_item_id (Integer, nil) (defaults to: nil)

    Optional catalog item ID to embed in URL

Returns:

  • (String)

    Complete callback URL



65
66
67
68
69
70
# File 'app/services/retailer/callback_token_service.rb', line 65

def callback_url(catalog_item_id: nil)
  token = generate_token
  url = "https://#{API_HOSTNAME_WITHOUT_PORT}/webhooks/v1/oxylabs?token=#{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

Parameters:

  • catalog_item_id (Integer, nil) (defaults to: nil)

    Optional catalog item ID to embed in URL

Returns:

  • (String)

    Complete dev callback URL



76
77
78
79
80
81
82
# File 'app/services/retailer/callback_token_service.rb', line 76

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=#{token}"
  url += "&catalog_item_id=#{catalog_item_id}" if catalog_item_id
  url
end

.generate_tokenString

Generate a JWT token for callback authentication

Returns:

  • (String)

    JWT token



25
26
27
28
29
30
31
32
33
34
# File 'app/services/retailer/callback_token_service.rb', line 25

def generate_token
  payload = {
    purpose: 'oxylabs_callback',
    exp: TOKEN_EXPIRY.from_now.to_i,
    iat: Time.current.to_i,
    jti: SecureRandom.uuid # Unique token ID
  }

  JWT.encode(payload, JWT_SECRET, JWT_ALGORITHM)
end

.valid_token?(token) ⇒ Boolean

Check if a token is valid

Parameters:

  • token (String)

    The JWT token to check

Returns:

  • (Boolean)


57
58
59
# File 'app/services/retailer/callback_token_service.rb', line 57

def valid_token?(token)
  validate_token(token).present?
end

.validate_token(token) ⇒ Hash?

Validate and decode a JWT token

Parameters:

  • token (String)

    The JWT token to validate

Returns:

  • (Hash, nil)

    The decoded payload if valid, nil otherwise



39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'app/services/retailer/callback_token_service.rb', line 39

def validate_token(token)
  return nil if token.blank?

  decoded_token = JWT.decode(token, JWT_SECRET, true, { algorithm: JWT_ALGORITHM })
  payload = decoded_token.first

  # Check if token is for oxylabs callback
  return nil unless payload['purpose'] == 'oxylabs_callback'

  payload
rescue JWT::DecodeError, JWT::ExpiredSignature => e
  Rails.logger.warn "[Oxylabs Callback] Token validation failed: #{e.class}"
  nil
end