cover/Elixir.WalletAuth.Session.Session.html

1 defmodule WalletAuth.Session.Session do
2 @moduledoc """
3 Session struct and lifecycle helpers.
4
5 A session represents an authenticated user's active context, bound to a
6 device_id and backed by a refresh token.
7
8 Lifecycle:
9 :active -> :revoked
10
11 Per checklist Track D: `AuthSessionStarted` / `AuthSessionRevoked` event flow.
12 """
13
14 alias WalletSharedKernel.TypedId
15
16 @enforce_keys [:session_id, :sub, :device_id, :created_at, :status]
17
:-(
defstruct [
18 :session_id,
19 :sub,
20 :device_id,
21 :user_agent,
22 :ip_address,
23 :created_at,
24 :revoked_at,
25 status: :active
26 ]
27
28 @type t :: %__MODULE__{
29 session_id: String.t(),
30 sub: String.t(),
31 device_id: String.t() | nil,
32 user_agent: String.t() | nil,
33 ip_address: String.t() | nil,
34 created_at: DateTime.t(),
35 revoked_at: DateTime.t() | nil,
36 status: :active | :revoked
37 }
38
39 @doc "Creates a new active session struct."
40 @spec new(sub :: String.t(), keyword()) :: t()
41 12 def new(sub, opts \\ []) do
42 14 %__MODULE__{
43 session_id: TypedId.generate("sess"),
44 sub: sub,
45 device_id: Keyword.get(opts, :device_id),
46 user_agent: Keyword.get(opts, :user_agent),
47 ip_address: Keyword.get(opts, :ip_address),
48 created_at: DateTime.utc_now(),
49 revoked_at: nil,
50 status: :active
51 }
52 end
53
54 @doc "Returns a revoked copy of the session."
55 @spec revoke(t()) :: t()
56 def revoke(%__MODULE__{} = session) do
57 5 %{session | status: :revoked, revoked_at: DateTime.utc_now()}
58 end
59
60 @doc "Returns true if the session is active."
61 @spec active?(t()) :: boolean()
62 1 def active?(%__MODULE__{status: :active}), do: true
63 2 def active?(_), do: false
64 end
Line Hits Source