View Source Charon.SessionStore.RedisStore (Charon v3.2.0)
A persistent session store based on Redis, which implements behaviour Charon.SessionStore
.
In addition to the required callbacks, this store also provides get_all/3
and delete_all/3
(for a user) functions.
Redis requirements
This module needs a Redis >= 7.0.0 instance and needs permissions to create Redis functions. The optimistic-locking functionality of the store was not designed with a Redis cluster in mind and will behave unpredictably when used with a distributed Redis deployment. Using a failover-replica should be fine, however. The Redis functions are registered with a name that includes the hashed Charon version, to make sure that the called function matches the expected code, and multiple Charon deployments can share a Redis instance.
Config
Additional config is required for this module (see Charon.SessionStore.RedisStore.Config
):
Charon.Config.from_enum(
...,
optional_modules: %{
Charon.SessionStore.RedisStore => %{
key_prefix: "charon_",
get_signing_key: &RedisStore.default_signing_key/1
}
}
)
The following options are supported:
:key_prefix
(optional). A string prefix for the Redis keys that are sessions.:get_signing_key
(optional). A getter/1 that returns the key that is used to sign and verify serialized session binaries.
Initialize store
RedisStore uses a connection pool and has to register Redis functions on startup. In order to initialize it, you must add RedisStore to your application's supervision tree.
# in application.ex
def start(_, ) do
redix_opts = [host: "localhost", port: 6379, password: "supersecret", database: 0]
children = [
...
{Charon.SessionStore.RedisStore, pool_size: 15, redix_opts: redix_opts},
...
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
Options
:name
(default module name) name of the supervisor:pool_size
(default 10) total number of (local) normal workers:pool_max_overflow
(default 5) max number of (local) extra workers in case of high load:redix_opts
passed toRedix.start_link/1
The pool and its config options are local to the Elixir/OTP node,
so if you use multiple nodes, the total connection count to Redis maxes out at
(pool_size + pool_max_overflow) * number_of_nodes
.
Session signing
In order to offer some defense-in-depth against a compromised Redis server, the serialized sessions are signed using a HMAC. The key is derived from the Charon base secret, but can be overridden in the config. This is not usually necessary, except when you're changing the base secret and still want to access your sessions. It is debatable, of course, in case your Redis server is compromised, if your application server holding the signing key can still be considered secure. However, given that there are no real drawbacks to using signed session binaries, since the performance cost is negligible, no extra config is needed, it is easy to implement, and defense-in-depth is a good guiding principle, RedisStore implements this feature anyway.
Implementation details
RedisStore stores sessions until their associated refresh token(s) expire.
That means that :refresh_expires_at
is used to determine if a session is "alive",
not :expires_at
.
This makes sense, because a session without a valid refresh token is effectively useless,
and it means we can support "infinite lifetime" sessions while still pruning sessions that aren't used.
This is one reason to set :refresh_ttl
to a limited value, no more than six months is recommended.
Last but not least, Redis 7 functions are used to implement some features, specifically optimistic locking, and to ensure all callbacks use only a single round-trip to the Redis instance.
Summary
Functions
Returns a specification to start this module under a supervisor.
Get the default session signing key that is used if config option :get_signing_key
is not set explicitly.
Start the RedisStore supervisor, which registeres all required Redis functions and initiates the connection pool.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor
.
@spec default_signing_key(Charon.Config.t()) :: binary()
Get the default session signing key that is used if config option :get_signing_key
is not set explicitly.
Start the RedisStore supervisor, which registeres all required Redis functions and initiates the connection pool.
Options
:name
(default module name) name of the supervisor:pool_size
(default 10) total number of (local) normal workers:pool_max_overflow
(default 5) max number of (local) extra workers in case of high load:redix_opts
passed toRedix.start_link/1
The pool and its config options are local to the Elixir/OTP node,
so if you use multiple nodes, the total connection count to Redis maxes out at
(pool_size + pool_max_overflow) * number_of_nodes
.