Track Devise login activity
๐ Battle-tested at Instacart
Add this line to your applicationโs Gemfile:
gem "authtrail"
To encrypt email and IP addresses with Lockbox, install Lockbox and Blind Index and run:
rails generate authtrail:install --encryption=lockbox
rails db:migrate
To use Active Record encryption (Rails 7+, experimental), run:
rails generate authtrail:install --encryption=activerecord
rails db:migrate
If you prefer not to encrypt data, run:
rails generate authtrail:install --encryption=none
rails db:migrate
To enable geocoding, see the Geocoding section.
A LoginActivity
record is created every time a user tries to login. You can then use this information to detect suspicious behavior. Data includes:
scope
- Devise scopestrategy
- Devise strategyidentity
- email addresssuccess
- whether the login succeededfailure_reason
- if the login faileduser
- the user if the login succeededcontext
- controller and actionip
- IP addressuser_agent
andreferrer
- from browsercity
,region
,country
,latitude
, andlongitude
- from IPcreated_at
- time of event
Exclude certain attempts from tracking - useful if you run acceptance tests
AuthTrail.exclude_method = lambda do |data|
data[:identity] == "[email protected]"
end
Add or modify data - also add new fields to the login_activities
table if needed
AuthTrail.transform_method = lambda do |data, request|
data[:request_id] = request.request_id
end
Store the user on failed attempts
AuthTrail.transform_method = lambda do |data, request|
data[:user] ||= User.find_by(email: data[:identity])
end
Write data somewhere other than the login_activities
table
AuthTrail.track_method = lambda do |data|
# code
end
Use a custom identity method
AuthTrail.identity_method = lambda do |request, opts, user|
if user
user.email
else
request.params.dig(opts[:scope], :email)
end
end
Associate login activity with your user model
class User < ApplicationRecord
has_many :login_activities, as: :user # use :user no matter what your model name
end
The LoginActivity
model uses a polymorphic association so it can be associated with different user models.
AuthTrail uses Geocoder for geocoding. We recommend configuring local geocoding or load balancer geocoding so IP addresses are not sent to a 3rd party service. If you do use a 3rd party service and adhere to GDPR, be sure to add it to your subprocessor list.
To enable geocoding, add this line to your applicationโs Gemfile:
gem "geocoder"
And update config/initializers/authtrail.rb
:
AuthTrail.geocode = true
Geocoding is performed in a background job so it doesnโt slow down web requests. Set the job queue with:
AuthTrail.job_queue = :low_priority
For privacy and performance, we recommend geocoding locally.
For city-level geocoding, download the GeoLite2 City database.
Add this line to your applicationโs Gemfile:
gem "maxminddb"
And create config/initializers/geocoder.rb
with:
Geocoder.configure(
ip_lookup: :geoip2,
geoip2: {
file: "path/to/GeoLite2-City.mmdb"
}
)
For country-level geocoding, install the geoip-database
package. Itโs preinstalled on Heroku. For Ubuntu, use:
sudo apt-get install geoip-database
Add this line to your applicationโs Gemfile:
gem "geoip"
And create config/initializers/geocoder.rb
with:
Geocoder.configure(
ip_lookup: :maxmind_local,
maxmind_local: {
file: "/usr/share/GeoIP/GeoIP.dat",
package: :country
}
)
Some load balancers can add geocoding information to request headers.
AuthTrail.geocode = false
AuthTrail.transform_method = lambda do |data, request|
data[:country] = request.headers["<country-header>"]
data[:region] = request.headers["<region-header>"]
data[:city] = request.headers["<city-header>"]
end
Check out this example
Delete older data with:
LoginActivity.where("created_at < ?", 2.years.ago).in_batches.delete_all
Delete data for a specific user with:
LoginActivity.where(user_id: 1, user_type: "User").in_batches.delete_all
We recommend using this in addition to Deviseโs Lockable
module and Rack::Attack.
Check out Hardening Devise and Secure Rails for more best practices.
There are two notable changes to geocoding:
-
Geocoding is now disabled by default (this was already the case for new installations with 0.3.0+). Check out the instructions for how to enable it (you may need to create
config/initializers/authtrail.rb
). -
The
geocoder
gem is now an optional dependency. To use geocoding, add it to your Gemfile:
gem "geocoder"
To store latitude and longitude, create a migration with:
add_column :login_activities, :latitude, :float
add_column :login_activities, :longitude, :float
View the changelog
Everyone is encouraged to help improve this project. Here are a few ways you can help:
- Report bugs
- Fix bugs and submit pull requests
- Write, clarify, or fix documentation
- Suggest or add new features
To get started with development and testing:
git clone https://github.com/ankane/authtrail.git
cd authtrail
bundle install
bundle exec rake test