YipyipExAuth v0.1.0-alpha.1 YipyipExAuth.SessionStore behaviour View Source
Behaviour definition of a persistent session store, to be implemented by the application. The implementation is expected to handle cleanup of expired entries.
Reference Redis implementation (requires Redix
and assumes a module has been configured for it according to its documentation):
defmodule MyAppWeb.RedisSessionStore do
@behaviour YipyipExAuth.SessionStore
alias MyApp.Redix
@key_prefix "SESSION_STORE_"
@impl true
def get(session_id, user_id) do
["GET", session_key(session_id, user_id)]
|> Redix.command()
|> case do
{:ok, session} when not is_nil(session) -> deserialize(session)
{:ok, nil} -> nil
error -> error
end
end
@impl true
def upsert(%{id: session_id, user_id: user_id} = session, ttl) do
["SETEX", session_key(session_id, user_id), Integer.to_string(ttl), serialize(session)]
|> Redix.command()
|> case do
{:ok, _} -> :ok
error -> error
end
end
@impl true
def delete(session_id, user_id) do
["DEL", session_key(session_id, user_id)]
|> Redix.command()
|> case do
{:ok, _} -> :ok
error -> error
end
end
def get_all(user_id) do
with {:ok, keys} when keys != [] <- find_session_keys(user_id),
{:ok, values} <- Redix.command(["MGET" | keys]) do
Enum.map(values, &deserialize/1)
else
{:ok, []} -> []
other -> other
end
end
def delete_all(user_id) do
with {:ok, keys} when keys != [] <- find_session_keys(user_id),
{:ok, _count} <- Redix.command(["DEL" | keys]) do
:ok
else
{:ok, []} -> :ok
other -> other
end
end
#####################
# Private functions #
#####################
defp serialize(session), do: :erlang.term_to_binary(session)
defp deserialize(binary), do: :erlang.binary_to_term(binary)
# keys as IO lists to prevent unnecessary string concatenation overhead
defp session_key(session_id, user_id), do: [@key_prefix, ".", user_id, ".", session_id]
# SCAN is not atomic. New sessions created during the scan may not be found.
defp find_session_keys(user_id, scan_iteration \\ nil, results \\ [])
defp find_session_keys(_user_id, "0", results) do
{:ok, Enum.uniq(results)}
end
defp find_session_keys(user_id, scan_iteration, results) do
["SCAN", scan_iteration, "MATCH", session_key("*", user_id)]
|> Redix.command()
|> case do
{:ok, [scan_iteration | [partial_results]]} ->
find_session_keys(user_id, scan_iteration, partial_results ++ results)
error ->
error
end
end
end
Link to this section Summary
Callbacks
Delete session with id session_id
for user with id user_id
.
Get session with id session_id
for user with id user_id
.
Insert or update Elixir.YipyipExAuth.Models.Session session
, with time-to-live ttl
.
Link to this section Callbacks
Delete session with id session_id
for user with id user_id
.
Implementations may choose to ignore user_id
, since session_id
is unique by itself.
get(session_id, user_id)
View Sourceget(session_id :: binary(), user_id :: binary()) :: YipyipExAuth.Models.Session.t() | nil | {:error, binary()}
Get session with id session_id
for user with id user_id
.
Implementations may choose to ignore user_id
, since session_id
is unique by itself.
upsert(session, ttl)
View Sourceupsert(session :: YipyipExAuth.Models.Session.t(), ttl :: integer()) :: :ok | {:error, binary()}
Insert or update Elixir.YipyipExAuth.Models.Session session
, with time-to-live ttl
.
The session_id
and user_id
are taken from the session
struct.
Implementations may choose to ignore user_id
, since session_id
is unique by itself.