# `Charon.SessionStore.RedisStore`
[🔗](https://github.com/weareyipyip/charon/blob/v4.3.0/lib/charon/session_store/redis_store.ex#L2)

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 {: .info}
>
> This module requires Redis >= 8.0.0 or Valkey >= 9.0.0 or another Redis-compatible key-value store with support for [HSETEX](https://redis.io/docs/latest/commands/hsetex/) and related Redis 8 commands, 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 to `Redix.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

Serialized sessions are signed using HMAC to prevent tampering if Redis is compromised.
The signing key is derived from the Charon base secret by default (see `default_signing_key/1`).
Override via the `:get_signing_key` config option when rotating secrets to maintain access to existing sessions.

## Implementation details

RedisStore expires sessions based on `:refresh_expires_at` rather than `:expires_at`.
This allows "infinite lifetime" sessions (via periodic refresh) while still pruning unused sessions.
For this reason, setting `:refresh_ttl` to a limited value (no more than six months recommended) is important.

[Redis functions](https://redis.io/docs/latest/develop/programmability/functions-intro/)
are used to implement optimistic locking and to ensure all operations
use only a single round-trip to Redis.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `default_signing_key`

```elixir
@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_link`

```elixir
@spec start_link(keyword()) :: :ignore | {:error, any()} | {:ok, pid()}
```

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 to `Redix.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`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
