View Source Charon.SessionStore.Behaviour behaviour (Charon v3.2.0)

Behaviour definition of a persistent session store. Clients should not use the implementation directly, but should use Charon.SessionStore.

Implementation guidelines

Implementations are expected to store sessions by ID, user ID and session type. For the optional callbacks get_all/3 and delete_all/3, sessions should be retrievable by user ID and session type only.

Implementations should return the session exactly as it went in (they don't have to take care of struct version upgrades).

Implementations should handle cleanup of expired entries, but may define additional functions and instructions to take care of such things (like a cleanup/0 that should run periodically).

Implementations should implement optimistic locking with respect to the :lock_version field of the session struct. On an update, the lock version of the updated session should match the lock version of the existing session. If that is the case, the implementation should increase :lock_version and proceed with the update. If not, the implementation should abort the update and return {:error, :conflict}.

Summary

Callbacks

Delete session of type type with id session_id for user with id user_id.

Delete all sessions of type type for the user with id user_id.

Get session of type type with id session_id for user with id user_id. Must not return sessions that have expired, or that can't be refreshed anymore because the refresh token has expired.

Get all sessions of type type for the user with id user_id. Must not return sessions that have expired, or that can't be refreshed anymore because the refresh token has expired.

Insert or update session. If the implementation supports optimistic locking, it will return {:error, :conflict} on an optimistic locking conflict.

Callbacks

delete(session_id, user_id, type, config)

@callback delete(
  session_id :: binary(),
  user_id :: binary() | integer(),
  type :: atom(),
  config :: Charon.Config.t()
) :: :ok | {:error, binary()}

Delete session of type type with id session_id for user with id user_id.

Implementations may choose to ignore user_id, since session_id is unique by itself.

delete_all(user_id, type, config)

(optional)
@callback delete_all(
  user_id :: binary() | integer(),
  type :: atom(),
  config :: Charon.Config.t()
) ::
  :ok | {:error, binary()}

Delete all sessions of type type for the user with id user_id.

get(session_id, user_id, type, config)

@callback get(
  session_id :: binary(),
  user_id :: binary() | integer(),
  type :: atom(),
  config :: Charon.Config.t()
) :: Charon.Models.Session.t() | nil | {:error, binary()}

Get session of type type with id session_id for user with id user_id. Must not return sessions that have expired, or that can't be refreshed anymore because the refresh token has expired.

Implementations may choose to ignore user_id, since session_id is unique by itself.

get_all(user_id, type, config)

(optional)
@callback get_all(
  user_id :: binary() | integer(),
  type :: atom(),
  config :: Charon.Config.t()
) ::
  [Charon.Models.Session.t()] | {:error, binary()}

Get all sessions of type type for the user with id user_id. Must not return sessions that have expired, or that can't be refreshed anymore because the refresh token has expired.

upsert(session, config)

@callback upsert(session :: Charon.Models.Session.t(), config :: Charon.Config.t()) ::
  :ok | {:error, :conflict | binary()}

Insert or update session. If the implementation supports optimistic locking, it will return {:error, :conflict} on an optimistic locking conflict.

Values session_id, user_id and type are taken from the session struct. Implementations may choose to ignore user_id, since session_id is unique by itself.

Implementations may assume that Charon.SessionPlugs.upsert_session/3 will ensure that:

  • :refreshed_at contains the current unix timestamp
  • :refresh_expires_at never exceeds :expires_at