This commit is contained in:
byte[] 2019-11-13 00:28:02 -05:00
parent f627677cb6
commit 7784c09b3e
4 changed files with 41 additions and 25 deletions

View file

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

View file

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

View file

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

View file

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