BreakGlass.OtpStore (BreakGlassEx v0.1.0)

Copy Markdown View Source

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 string
  • verify/1 — validates the code against the stored OTP; always clears the store before returning
  • clear/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

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

clear()

@spec clear() :: :ok

Unconditionally removes the pending OTP record from the ETS table.

Returns :ok.

generate()

@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.

start_link(opts \\ [])

@spec start_link(keyword()) :: GenServer.on_start()

Starts the OtpStore GenServer and registers it under Elixir.BreakGlass.OtpStore.

verify(code)

@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