mirror of
https://gitlab.com/gitlab-org/gitlab-foss.git
synced 2025-07-25 16:03:48 +00:00
93 lines
5.1 KiB
Markdown
93 lines
5.1 KiB
Markdown
---
|
|
stage: none
|
|
group: unassigned
|
|
info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/development/development_processes/#development-guidelines-review.
|
|
title: 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`: the `SHA256` digests 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`:
|
|
|
|
```ruby
|
|
class User < ApplicationRecord
|
|
include TokenAuthenticatable
|
|
|
|
add_authentication_token_field :token_field, encrypted: :required
|
|
end
|
|
```
|
|
|
|
### Storage strategies
|
|
|
|
- `encrypted: :required`: Stores the encrypted token in the `token_field_encrypted` column.
|
|
The `token_field_encrypted` column needs to exist. We strongly encourage to use this strategy.
|
|
- `encrypted: :migrating`: Stores the encrypted and plaintext tokens in `token_field_encrypted` and `token_field`.
|
|
Always reads the plaintext token. This should be used while an attribute is transitioning to be encrypted.
|
|
Both `token_field` and `token_field_encrypted` columns need to exist.
|
|
- `encrypted: :optional`: Stores the encrypted token in the `token_field_encrypted` column.
|
|
Reads from `token_field_encrypted` first and fallbacks to `token_field`.
|
|
Nullifies the plaintext token in the `token_field` column when writing the encrypted token.
|
|
Both `token_field` and `token_field_encrypted` columns need to exist.
|
|
- `digest: true`: Stores the token's digest in the database.
|
|
The `token_field_digest` column 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.
|
|
|
|
{{< alert type="note" >}}
|
|
|
|
By default, the `SHA256` digest of the tokens are stored in the database, if no storage strategy is chosen.
|
|
|
|
{{< /alert >}}
|
|
|
|
{{< alert type="note" >}}
|
|
|
|
The `token_field_encrypted` column should always be indexed, because it is used to perform uniqueness checks and lookups on the token.
|
|
|
|
{{< /alert >}}
|
|
|
|
### Other options
|
|
|
|
- `unique: false`: Doesn't enforce token uniqueness and disables the generation of `find_by_token_field` (where `token_field` is the attribute name). Default is `true`.
|
|
- `format_with_prefix: :compute_token_prefix`: Allows to define a prefix for the token. The `#compute_token_prefix` method needs to return a `String`. 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 a `Time` object. Default is no expiration.
|
|
- `token_generator:` A proc that returns a token. If absent, a random token is generated using `Devise.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](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/cells/routable_tokens/#proposal).
|
|
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 returns `false`, a random token is generated using `Devise.friendly_token`.
|
|
- `payload:`: A `{ key => proc }` hash with allowed keys `c`, `o`, `g`, `p`,`u` which
|
|
[complies with the specification](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/cells/routable_tokens/#meaning-of-fields).
|
|
See an example in the [Routable Tokens design document](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/cells/routable_tokens/#integration-into-token-authenticatable).
|
|
- `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. Default `false`.
|
|
|
|
## Accessing and manipulating tokens
|
|
|
|
```ruby
|
|
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
|
|
```
|