diff --git a/lib/philomena/users/user.ex b/lib/philomena/users/user.ex index 54212193..bf86de64 100644 --- a/lib/philomena/users/user.ex +++ b/lib/philomena/users/user.ex @@ -4,7 +4,8 @@ defmodule Philomena.Users.User do use Ecto.Schema use Pow.Ecto.Schema, - password_hash_methods: {&Password.hash_pwd_salt/1, &Password.verify_pass/2} + password_hash_methods: {&Password.hash_pwd_salt/1, &Password.verify_pass/2}, + password_min_length: 6 use Pow.Extension.Ecto.Schema, extensions: [PowResetPassword, PowPersistentSession] @@ -128,44 +129,43 @@ defmodule Philomena.Users.User do }) end - def consume_totp_token_changeset(user, token) do + def consume_totp_token_changeset(changeset, params) do + changeset = change(changeset, %{}) + user = changeset.data + token = extract_token(params) + cond do totp_valid?(user, token) -> - user - |> change(%{consumed_timestep: token}) + changeset + |> change(%{consumed_timestep: String.to_integer(token)}) backup_code_valid?(user, token) -> - user + changeset |> change(%{otp_backup_codes: remove_backup_code(user, token)}) true -> - user + changeset |> add_error(:consumed_timestep, "invalid token") end end - def totp_changeset(user, params, backup_codes) do - token = - case params do - %{"user" => %{"twofactor_token" => t}} -> - to_string(t) - - _ -> - "" - end + def totp_changeset(changeset, params, backup_codes) do + changeset = change(changeset, %{}) + user = changeset.data + token = extract_token(params) case user.otp_required_for_login do true -> # User wants to disable TOTP - user - |> pow_current_password_changeset(params) + changeset + |> pow_password_changeset(params) |> consume_totp_token_changeset(token) |> disable_totp_changeset() - false -> + _falsy -> # User wants to enable TOTP - user - |> pow_current_password_changeset(params) + changeset + |> pow_password_changeset(params) |> consume_totp_token_changeset(token) |> enable_totp_changeset(backup_codes) end @@ -226,6 +226,12 @@ defmodule Philomena.Users.User do }) end + defp extract_token(%{"user" => %{"twofactor_token" => t}}), + do: to_string(t) + + defp extract_token(_params), + do: "" + defp totp_valid?(user, token), do: :pot.valid_totp(token, totp_secret(user), window: 60) @@ -234,4 +240,4 @@ defmodule Philomena.Users.User do defp remove_backup_code(user, token), do: user.otp_backup_codes |> Enum.reject(&Password.verify_pass(token, &1)) -end +end \ No newline at end of file diff --git a/lib/philomena_web/router.ex b/lib/philomena_web/router.ex index b561baff..edf178e9 100644 --- a/lib/philomena_web/router.ex +++ b/lib/philomena_web/router.ex @@ -34,18 +34,20 @@ defmodule PhilomenaWeb.Router do end scope "/", PhilomenaWeb do - pipe_through [:browser, :ensure_totp] + pipe_through [:browser, :protected] # Additional routes for TOTP scope "/registration", Registration, as: :registration do - pipe_through :protected resources "/totp", TotpController, only: [:edit, :update], singleton: true end scope "/session", Session, as: :session do - pipe_through :protected resources "/totp", TotpController, only: [:new, :create], singleton: true end + end + + scope "/", PhilomenaWeb do + pipe_through [:browser, :ensure_totp] get "/", ActivityController, :index diff --git a/lib/philomena_web/templates/layout/_header.html.slime b/lib/philomena_web/templates/layout/_header.html.slime index cdd81bea..6165ec20 100644 --- a/lib/philomena_web/templates/layout/_header.html.slime +++ b/lib/philomena_web/templates/layout/_header.html.slime @@ -37,7 +37,7 @@ header.header span.hide-limited-desktop< Filters .dropdown.header__dropdown a.header__link.header__link-user href="/" - /= user_avatar(@current_user, 'avatar--28px'.freeze, @current_user.name) + = render PhilomenaWeb.UserAttributionView, "_user_avatar.html", object: %{user: @current_user}, class: "avatar--28px" span.header__link-user__dropdown-arrow.hide-mobile data-click-preventdefault="true" nav.dropdown__content.dropdown__content-right.hide-mobile.js-burger-links a.header__link href="/profiles" diff --git a/lib/philomena_web/templates/session/totp/new.html.slime b/lib/philomena_web/templates/session/totp/new.html.slime new file mode 100644 index 00000000..8c8439b3 --- /dev/null +++ b/lib/philomena_web/templates/session/totp/new.html.slime @@ -0,0 +1,8 @@ +h1 Two Factor Authentication + += form_for @changeset, Routes.session_totp_path(@conn, :create), [as: :user, method: "post"], fn f -> + .field + h4 Please enter your 2FA code + = text_input f, :twofactor_token, class: "input", placeholder: "6-digit code", required: true, autofocus: true, autocomplete: "off" + + = submit "Sign in", class: "button"