defmodule PhilomenaWeb.TotpPlug do @moduledoc """ This plug ensures that a user session has a valid TOTP. ## Example plug PhilomenaWeb.TotpPlug """ alias PhilomenaWeb.Router.Helpers, as: Routes @doc false @spec init(any()) :: any() def init(opts), do: opts @doc false @spec call(Plug.Conn.t(), any()) :: Plug.Conn.t() def call(conn, _opts) do conn |> Pow.Plug.current_user() |> case do nil -> conn user -> maybe_require_totp_phase(user, conn) end end defp maybe_require_totp_phase(%{otp_required_for_login: nil}, conn), do: conn defp maybe_require_totp_phase(%{otp_required_for_login: false}, conn), do: conn defp maybe_require_totp_phase(_user, conn) do conn.private |> Map.get(:pow_session_metadata, []) |> Keyword.get(:valid_totp_at) |> case do nil -> conn |> Phoenix.Controller.redirect(to: Routes.session_totp_path(conn, :new)) |> Plug.Conn.halt() _valid_at -> conn end end @doc false @spec update_valid_totp_at_for_session(Plug.Conn.t(), map()) :: Plug.Conn.t() def update_valid_totp_at_for_session(conn, user) do metadata = conn.private |> Map.get(:pow_session_metadata, []) |> Keyword.put(:valid_totp_at, DateTime.utc_now()) config = Pow.Plug.fetch_config(conn) plug = Pow.Plug.get_plug(config) conn = Plug.Conn.put_private(conn, :pow_session_metadata, metadata) conn = conn |> Plug.Conn.put_private(:pow_persistent_session_metadata, session_metadata: Keyword.take(metadata, [:valid_totp_at]) ) |> PowPersistentSession.Plug.Cookie.create(user, config) plug.do_create(conn, user, config) end end