Ruby SDK for Castle

Build Status Coverage Status Gem Version

Castle analyzes user behavior in web and mobile apps to stop fraud before it happens.

Installation

Add the castle-rb gem to your Gemfile

gem 'castle-rb'

Configuration

Framework configuration

Load and configure the library with your Castle API secret in an initializer or similar.

Castle.api_secret = 'YOUR_API_SECRET'

A Castle client instance will be made available as castle in your

  • Rails controllers when you add require 'castle/support/rails'

  • Padrino controllers when you add require 'castle/support/padrino'

  • Sinatra app when you add require 'castle/support/sinatra' (and additionally explicitly add register Sinatra::Castle to your Sinatra::Base class if you have a modular application)

require 'castle/support/sinatra'

class ApplicationController < Sinatra::Base
  register Sinatra::Castle
end
  • Hanami when you add require 'castle/support/hanami' and include Castle::Hanami to your Hanami application
require 'castle/support/hanami'

module Web
  class Application < Hanami::Application
    include Castle::Hanami
  end
end

Client configuration

Castle.configure do |config|
  # Same as setting it through Castle.api_secret
  config.api_secret = 'secret'

  # For authenticate method you can set failover strategies: allow(default), deny, challenge, throw
  config.failover_strategy = :deny

  # Castle::RequestError is raised when timing out in milliseconds (default: 1000 milliseconds)
  config.request_timeout = 1500

  # Base Castle API url
  # config.base_url = "https://api.castle.io/v1"

  # Logger (need to respond to info method) - logs Castle API requests and responses
  # config.logger = Logger.new(STDOUT)

  # Allowlisted and Denylisted headers are case insensitive and allow to use _ and - as a separator, http prefixes are removed
  # Allowlisted headers
  # By default, the SDK sends all HTTP headers, except for Cookie and Authorization.
  # If you decide to use a allowlist, the SDK will:
  # - always send the User-Agent header
  # - send scrubbed values of non-allowlisted headers
  # - send proper values of allowlisted headers.
  # @example
  #   config.allowlisted = ['X_HEADER']
  #   # will send { 'User-Agent' => 'Chrome', 'X_HEADER' => 'proper value', 'Any-Other-Header' => true }
  #
  # We highly suggest using denylist instead of allowlist, so that Castle can use as many data points
  # as possible to secure your users. If you want to use the allowlist, this is the minimal
  # amount of headers we recommend:
  config.allowlisted = Castle::Configuration::DEFAULT_ALLOWLIST

  # Denylisted headers take precedence over allowlisted elements
  # We always denylist Cookie and Authentication headers. If you use any other headers that
  # might contain sensitive information, you should denylist them.
  config.denylisted = ['HTTP-X-header']

  # Castle needs the original IP of the client, not the IP of your proxy or load balancer.
  # The SDK will only trust the proxy chain as defined in the configuration.
  # We try to fetch the client IP based on X-Forwarded-For or Remote-Addr headers in that order,
  # but sometimes the client IP may be stored in a different header or order.
  # The SDK can be configured to look for the client IP address in headers that you specify.

  # Sometimes, Cloud providers do not use consistent IP addresses to proxy requests.
  # In this case, the client IP is usually preserved in a custom header. Example:
  # Cloudflare preserves the client request in the 'Cf-Connecting-Ip' header.
  # It would be used like so: config.ip_headers=['Cf-Connecting-Ip']
  config.ip_headers = []

  # If the specified header or X-Forwarded-For default contains a proxy chain with public IP addresses,
  # then you must choose only one of the following (but not both):
  # 1. The trusted_proxies value must match the known proxy IPs. This option is preferable if the IP is static.
  # 2. The trusted_proxy_depth value must be set to the number of known trusted proxies in the chain (see below).
  # This option is preferable if the IPs are ephemeral, but the depth is consistent.

  # Additionally to make X-Forwarded-For and other headers work better discovering client ip address,
  # and not the address of a reverse proxy server, you can define trusted proxies
  # which will help to fetch proper ip from those headers

  # In order to extract the client IP of the X-Forwarded-For header
  # and not the address of a reverse proxy server, you must define all trusted public proxies
  # you can achieve this by listing all the proxies ip defined by string or regular expressions
  # in the trusted_proxies setting
  config.trusted_proxies = []

  # or by providing number of trusted proxies used in the chain
  config.trusted_proxy_depth = 0

  # note that you must pick one approach over the other.

  # If there is no possibility to define options above and there is no other header that holds the client IP,
  # then you may set trust_proxy_chain = true to trust all of the proxy IPs in X-Forwarded-For
  config.trust_proxy_chain = false
  # *Warning*: this mode is highly promiscuous and could lead to wrongly trusting a spoofed IP if the request passes through a malicious proxy

  # *Note: the default list of proxies that are always marked as "trusted" can be found in: Castle::Configuration::TRUSTED_PROXIES
end

Multi-environment configuration

It is also possible to define multiple configs within one application.

# Initialize new instance of Castle::Configuration
config =
  Castle::Configuration.new.tap do |c|
    # and set any attribute
    c.api_secret = 'YOUR_API_SECRET'
  end

After a successful setup, you can pass the config to any API command as follows:

::Castle::API::GetDevice.call(device_token: device_token, config: config)

Usage

See documentation for how to use this SDK with the Castle APIs

Exceptions

Castle::Error will be thrown if the Castle API returns a 400 or a 500 level HTTP response. You can also choose to catch a more finegrained error.