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:
-
digest: theSHA256digests of the token is stored in the database -
encrypted: the token is stored encrypted in the database using the AES 256 GCM algorithm -
insecure: the token is stored as-is (not encrypted nor digested) in the database. We strongly discourage the usage of this strategy.
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_encryptedcolumn. Thetoken_field_encryptedcolumn needs to exist. We strongly encourage to use this strategy. -
encrypted: :migrating: Stores the encrypted and plaintext tokens intoken_field_encryptedandtoken_field. Always reads the plaintext token. This should be used while an attribute is transitioning to be encrypted. Bothtoken_fieldandtoken_field_encryptedcolumns need to exist. -
encrypted: :optional: Stores the encrypted token in thetoken_field_encryptedcolumn. Reads fromtoken_field_encryptedfirst and fallbacks totoken_field. Nullifies the plaintext token in thetoken_fieldcolumn when writing the encrypted token. Bothtoken_fieldandtoken_field_encryptedcolumns need to exist. -
digest: true: Stores the token's digest in the database. Thetoken_field_digestcolumn needs to exist. -
insecure: true: Stores the token as-is (not encrypted nor digested) in the database. We strongly discourage the usage of this strategy.
By default, the SHA256 digest of the tokens are stored in the database, if no storage strategy is chosen.
Other options
-
unique: false: Doesn't enforce token uniqueness and disables the generation offind_by_token_field(wheretoken_fieldis the attribute name). Default istrue. -
format_with_prefix: :compute_token_prefix: Allows to define a prefix for the token. The#compute_token_prefixmethod 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_timemethod needs to return aTimeobject. 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,uwhich complies with the specification. See an example in the Routable Tokens design document.
-
-
require_prefix_for_validation:(only for the:encryptedstrategy): 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