91 lines
2.4 KiB
Elixir
91 lines
2.4 KiB
Elixir
|
|
defmodule BerrypodWeb.Auth.TotpVerification do
|
||
|
|
use BerrypodWeb, :live_view
|
||
|
|
|
||
|
|
alias Berrypod.Accounts
|
||
|
|
|
||
|
|
@impl true
|
||
|
|
def mount(_params, session, socket) do
|
||
|
|
user_id = session["totp_pending_user_id"]
|
||
|
|
remember_me = session["totp_pending_remember_me"]
|
||
|
|
|
||
|
|
if user_id do
|
||
|
|
{:ok,
|
||
|
|
socket
|
||
|
|
|> assign(:user_id, user_id)
|
||
|
|
|> assign(:remember_me, remember_me)
|
||
|
|
|> assign(:form, to_form(%{"code" => ""}, as: :totp))
|
||
|
|
|> assign(:trigger_submit, false)}
|
||
|
|
else
|
||
|
|
{:ok,
|
||
|
|
socket
|
||
|
|
|> put_flash(:error, "Session expired. Please log in again.")
|
||
|
|
|> redirect(to: ~p"/users/log-in")}
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
@impl true
|
||
|
|
def render(assigns) do
|
||
|
|
~H"""
|
||
|
|
<Layouts.app flash={@flash} current_scope={@current_scope}>
|
||
|
|
<div class="setup-page">
|
||
|
|
<div class="setup-header">
|
||
|
|
<.header>
|
||
|
|
Two-factor authentication
|
||
|
|
<:subtitle>
|
||
|
|
Enter the 6-digit code from your authenticator app.
|
||
|
|
</:subtitle>
|
||
|
|
</.header>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<.form
|
||
|
|
for={@form}
|
||
|
|
id="totp_form"
|
||
|
|
action={~p"/users/verify-totp"}
|
||
|
|
phx-submit="verify"
|
||
|
|
phx-trigger-action={@trigger_submit}
|
||
|
|
>
|
||
|
|
<input type="hidden" name="remember_me" value={to_string(@remember_me)} />
|
||
|
|
|
||
|
|
<.input
|
||
|
|
field={@form[:code]}
|
||
|
|
type="text"
|
||
|
|
label="Verification code"
|
||
|
|
placeholder="000000"
|
||
|
|
inputmode="numeric"
|
||
|
|
pattern="[0-9]*"
|
||
|
|
autocomplete="one-time-code"
|
||
|
|
maxlength="6"
|
||
|
|
autofocus
|
||
|
|
phx-mounted={JS.focus()}
|
||
|
|
/>
|
||
|
|
|
||
|
|
<.button variant="primary" class="admin-btn-block">
|
||
|
|
Verify <span aria-hidden="true">→</span>
|
||
|
|
</.button>
|
||
|
|
</.form>
|
||
|
|
|
||
|
|
<p class="setup-footer admin-text-tertiary">
|
||
|
|
Lost your device? Enter one of your backup codes instead.
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
</Layouts.app>
|
||
|
|
"""
|
||
|
|
end
|
||
|
|
|
||
|
|
@impl true
|
||
|
|
def handle_event("verify", %{"totp" => %{"code" => code}}, socket) do
|
||
|
|
user = Accounts.get_user!(socket.assigns.user_id)
|
||
|
|
|
||
|
|
case Accounts.verify_totp(user, code) do
|
||
|
|
:ok ->
|
||
|
|
{:noreply, assign(socket, :trigger_submit, true)}
|
||
|
|
|
||
|
|
:error ->
|
||
|
|
{:noreply,
|
||
|
|
socket
|
||
|
|
|> assign(:form, to_form(%{"code" => ""}, as: :totp))
|
||
|
|
|> put_flash(:error, "Invalid code. Please try again.")}
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|