Using the TokenAuthenticatable
concern
The TokenAuthenticatable
module is a concern that provides token-based authentication functionality for ActiveRecord
models.
It allows you to define authentication tokens for your models.
Overview
This module provides a flexible way to add token-based authentication to your models.
It supports three storage strategies:
-
insecure
: the token is stored as-is (not encrypted) in the database -
digest
: theSHA256
digests of the token is stored in the database -
encrypted
: the token is stored encrypted in the database using the AES 256 GCM algorithm
It also supports several options for each storage strategies.
Usage
To define a token_field
attribute in your model, include the module and call add_authentication_token_field
:
class User < ApplicationRecord
include TokenAuthenticatable
add_authentication_token_field :token_field, encrypted: :required
end
Storage strategies
-
encrypted: :required
: Stores the encrypted token in thetoken_field_encrypted
column. Thetoken_field_encrypted
column needs to exist. We strongly encourage to use this strategy. -
encrypted: :migrating
: Stores the encrypted and plaintext tokens respectively intoken_field_encrypted
andtoken_field
. Always reads the plaintext token. This should be used while an attribute is transitioning to be encrypted. Bothtoken_field
andtoken_field_encrypted
columns need to exist. -
encrypted: :optional
: Stores the encrypted token in thetoken_field_encrypted
column. Reads fromtoken_field_encrypted
first and fallbacks totoken_field
. Nullifies the plaintext token in thetoken_field
column when writing the encrypted token. Bothtoken_field
andtoken_field_encrypted
columns need to exist. -
digest: true
: Stores the token’s digest in the database. Thetoken_field_digest
column needs to exist.
By default, tokens are stored as-is (not encrypted).
Other options
-
unique: false
: Doesn’t enforce token uniqueness and disables the generation offind_by_token_field
(wheretoken_field
is the attribute name). Default istrue
. -
format_with_prefix: :compute_token_prefix
: Allows to define a prefix for the token. The#compute_token_prefix
method needs to return aString
. Default is no prefix. -
expires_at: :compute_token_expiration_time
: Allows to define a time when the token should expire. The#compute_token_expiration_time
method needs to return aTime
object. Default is no expiration. -
token_generator:
A proc that returns a token. If absent, a random token is generated usingDevise.friendly_token
. -
routable_token:
: A hash allowing to define “routable” parts that should be encoded in the token. This follows the Routable Tokens design document. Supported keys are:-
if:
: a proc receiving the token owner record. The proc usually has a feature flag check, and/or other checks. If the proc returnsfalse
, a random token is generated usingDevise.friendly_token
. -
payload:
: A{ key => proc }
hash with allowed keysc
,o
,g
,p
,u
which complies with the specification. See an example in the Routable Tokens design document.
-
-
require_prefix_for_validation:
(only for the:encrypted
strategy): Checks that the token prefix matches the expected prefix. If the prefix doesn’t match, it behaves as if the token isn’t set. Defaultfalse
.
Accessing and manipulating tokens
user = User.new
user.token_field # Retrieves the token
user.set_token_field('new_token') # Sets a new token
user.ensure_token_field # Generates a token if not present
user.ensure_token_field! # Generates a token if not present
user.reset_token_field! # Resets the token and saves the model with #save!
user.token_field_matches?(other_token) # Securely compares the token with another
user.token_field_expires_at # Returns the expiration time
user.token_field_expired? # Checks if the token has expired
user.token_field_with_expiration # Returns a API::Support::TokenWithExpiration object, useful for API response