GenServer that owns the :break_glass_otp ETS table.
Maintains at most one pending OTP record at any time. Generating a new code atomically invalidates any previously issued code.
ETS Table
Table name: :break_glass_otp
Record format: {:pending_otp, code :: String.t(), issued_at :: integer()}
Access: :protected
verify/1 reads ETS directly, then always calls
GenServer.call(__MODULE__, :clear) before returning, regardless of the
outcome. This prevents replay attacks.
OTP TTL is 600 seconds. The TTL check precedes code comparison so an expired
code always returns {:error, :expired}.
Functions
generate/0— produces a 6-digit zero-padded OTP, stores it with a fresh issue timestamp, and returns the code stringverify/1— validates the code against the stored OTP; always clears the store before returningclear/0— unconditionally removes the pending OTP record
Summary
Functions
Returns a specification to start this module under a supervisor.
Unconditionally removes the pending OTP record from the ETS table.
Generates a 6-digit zero-padded OTP code, stores it with a fresh issue timestamp, and returns the code string.
Starts the OtpStore GenServer and registers it under Elixir.BreakGlass.OtpStore.
Verifies code against the stored OTP.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec clear() :: :ok
Unconditionally removes the pending OTP record from the ETS table.
Returns :ok.
@spec generate() :: String.t()
Generates a 6-digit zero-padded OTP code, stores it with a fresh issue timestamp, and returns the code string.
Any previously stored OTP is atomically replaced.
@spec start_link(keyword()) :: GenServer.on_start()
Starts the OtpStore GenServer and registers it under Elixir.BreakGlass.OtpStore.
@spec verify(code :: String.t()) :: :ok | {:error, :invalid} | {:error, :expired} | {:error, :no_pending_otp}
Verifies code against the stored OTP.
Reads ETS directly for the lookup, then always calls
GenServer.call(__MODULE__, :clear) before returning (regardless of the
outcome) to prevent replay attacks.
Returns:
:ok— code matches and has not expired{:error, :expired}— OTP TTL (600 s) has elapsed{:error, :invalid}— code does not match{:error, :no_pending_otp}— no OTP has been generated yet