cover/Elixir.WalletAuth.Device.Device.html

1 defmodule WalletAuth.Device.Device do
2 @moduledoc """
3 Trusted device struct and lifecycle.
4
5 A trusted device is registered by a user after a successful OTP challenge.
6 It can be revoked by the user or by admin action.
7
8 Lifecycle: :pending_verification -> :trusted -> :revoked
9 """
10
11 alias WalletSharedKernel.TypedId
12
13 @enforce_keys [:device_id, :sub, :status, :registered_at]
14
:-(
defstruct [
15 :device_id,
16 :sub,
17 :device_fingerprint,
18 :user_agent,
19 :ip_address,
20 :name,
21 :registered_at,
22 :revoked_at,
23 :revoked_by,
24 status: :trusted
25 ]
26
27 @type t :: %__MODULE__{
28 device_id: String.t(),
29 sub: String.t(),
30 device_fingerprint: String.t() | nil,
31 user_agent: String.t() | nil,
32 ip_address: String.t() | nil,
33 name: String.t() | nil,
34 registered_at: DateTime.t(),
35 revoked_at: DateTime.t() | nil,
36 revoked_by: String.t() | nil,
37 status: :trusted | :revoked
38 }
39
40 @doc "Creates a new trusted device struct."
41 @spec new(sub :: String.t(), keyword()) :: t()
42
:-(
def new(sub, opts \\ []) do
43
:-(
%__MODULE__{
44 device_id: TypedId.generate("dev"),
45 sub: sub,
46 device_fingerprint: Keyword.get(opts, :device_fingerprint),
47 user_agent: Keyword.get(opts, :user_agent),
48 ip_address: Keyword.get(opts, :ip_address),
49 name: Keyword.get(opts, :name),
50 registered_at: DateTime.utc_now(),
51 revoked_at: nil,
52 revoked_by: nil,
53 status: :trusted
54 }
55 end
56
57 @doc "Returns a revoked copy of the device with actor traceability."
58 @spec revoke(t(), revoked_by :: String.t()) :: t()
59 def revoke(%__MODULE__{} = device, revoked_by) do
60
:-(
%{device | status: :revoked, revoked_at: DateTime.utc_now(), revoked_by: revoked_by}
61 end
62
63 @doc "Returns true if the device is trusted."
64 @spec trusted?(t()) :: boolean()
65
:-(
def trusted?(%__MODULE__{status: :trusted}), do: true
66
:-(
def trusted?(_), do: false
67 end
Line Hits Source