Protect your users from stolen credentials. Castle detects and mitigates account takeovers in web and mobile apps
Add this line to your Rack application's Gemfile:
gem 'castle-middleware', git: 'https://github.com/castle/castle-ruby-middleware.git'
And set your Castle credentials in an initializer:
Castle::Middleware.configure do |config|
config.app_id = '186593875646714'
config.api_secret = 'abcdefg123456789'
end
The middleware will insert itself into the Rack middleware stack.
By default the middleware will insert Castle.js into the HEAD tag on all your pages.
To start tracking events to Castle, you need to setup which routes should be mapped to which Castle security event.
# config/castle.yml
---
events:
$login.failed:
path: /session
method: POST
status: 401,
properties:
email: 'session.email' # Send user email extracted from params['session']['email']
$login.succeeded: # Remember to register the current user, see below
path: /session
method: POST
status: 302
$password_change.succeeded:
path: !ruby/regexp '\/users\/\d+\/account'
method: POST
status: 200
By default the middleware will look for a YAML configuration file located at config/castle.yml
. If you need to place this elsewhere this can be configured using the
file_path
option, eg.:
Castle::Middleware.configure do |config|
config.file_path = './castle_config.yml'
end
In some cases you might have multiple paths that should track the same event. Eg. if you have one login endpoint for API devices, and one for webapps. This is possible by specifying multiple paths as an array under each event definition:
events:
$login.failed:
- path: /session
method: POST
status: 401
properties:
email: 'session.email'
- path: /other_session
method: PUT
status: 300
properties:
email: 'session.email'
If you're using Devise as authentication solution, check out this example config for how to map the most common security events
Call identify
on the env['castle']
object to register the currently logged in user. This call will not issue an API request, but instead piggyback the information on the next server-side event.
class ApplicationController < ActionController::Base
before_action do
if current_user
env['castle'].identify(current_user.id, {
created_at: current_user.created_at,
email: current_user.email,
name: current_user.name
})
end
end
# ...
end
Castle::Middleware.configure do |config|
config.auto_insert_middleware = false
end
# config/application.rb
app.config.middleware.insert_after ActionDispatch::Flash, # Replace this if needed
Castle::Middleware::Tracking
app.config.middleware.insert_after ActionDispatch::Flash, # Replace this if needed
Castle::Middleware::Sensor
By default Castle sends tracking requests synchronously. To eg. use Sidekiq to send requests in a background worker you can override the transport method
# castle_worker.rb
class CastleWorker
include Sidekiq::Worker
def perform(params, context)
::Castle::Middleware.track(params, context)
end
end
# initializers/castle.rb
Castle::Middleware.configure do |config|
config.transport = lambda do |params, context|
CastleWorker.perform_async(params, context)
end
end
By default, all Castle exceptions are handled silently, but you can also create a custom error handler:
config.error_handler = lambda do |exception|
# Handle error from Castle
end
The gem is available as open source under the terms of the MIT License.