Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Email 2FA update #155

Merged
merged 106 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
f49b445
Remove newsletter checkbox in settings
macifell Mar 19, 2024
4f9431b
Enable bigcommerce on staging
macifell Mar 26, 2024
7ff50b7
Merge branch 'newsletter-updates' into staging
macifell Mar 27, 2024
a6fc0de
hide phone/text 2fa options for admins
ah-s76 Mar 28, 2024
96c7b61
Merge branch 'ah/settings-restrictions' into staging
ah-s76 Mar 28, 2024
172c859
hide phone/text 2fa options for admins
ah-s76 Mar 28, 2024
d3beb63
assert
ah-s76 Apr 30, 2024
97da605
upgrade phoenix deps
ah-s76 Apr 30, 2024
ecb6492
Merge branch 'ah/phone-2fa' into staging
ah-s76 May 2, 2024
32f23fb
branch cleanup
ah-s76 May 2, 2024
2dcdbd2
Merge branch 'ah/phone-2fa' into staging
ah-s76 May 2, 2024
ae3d7b0
Don't allow newlines in email addresses
macifell May 6, 2024
422f729
Merge branch 'disallow-email-newlines' into staging
macifell May 6, 2024
9e7da1a
Restrict logout redirect uri
macifell May 7, 2024
02d28ce
Merge branch 'fix-logout-redirect' into staging
macifell May 7, 2024
8a42a35
more cleanup
ah-s76 May 13, 2024
8c993b1
Merge branch 'ah/phone-2fa' into staging
ah-s76 May 13, 2024
3fd8590
redirect to store home
ah-s76 May 15, 2024
cc1914b
Merge branch 'bc-redirect-home' into staging
ah-s76 May 15, 2024
b63dc76
format
ah-s76 May 15, 2024
15577f6
Merge branch 'bc-redirect-home' into staging
ah-s76 May 15, 2024
8a634c7
simplify phone assign
ah-s76 May 15, 2024
8ef8a93
Merge branch 'bc-redirect-home' into staging
ah-s76 May 15, 2024
eb2b81b
Handle bc checkout redirects
macifell May 21, 2024
436b6e1
Merge branch 'bc-checkout' into staging
macifell May 21, 2024
3975318
Fix ordering
macifell May 21, 2024
d32e4ec
Merge branch 'bc-checkout' into staging
macifell May 21, 2024
1dfa2e7
Remove checkout from session
macifell May 21, 2024
ed04981
Merge branch 'bc-checkout' into staging
macifell May 21, 2024
c588195
Fix query params
macifell May 21, 2024
6653c22
Merge branch 'bc-checkout' into staging
macifell May 21, 2024
b60167f
Fix bc session parameters
macifell May 21, 2024
2aebecc
Merge branch 'bc-checkout' into staging
macifell May 21, 2024
1ce16a9
Redirect bc to home page
macifell Sep 23, 2024
6f8beb5
Merge branch 'bc-home-redirect' into staging
macifell Sep 23, 2024
976cb88
Fix tests
macifell Sep 23, 2024
301af69
Merge branch 'bc-home-redirect' into staging
macifell Sep 23, 2024
fdad9ff
link existing customer
ah-s76 Nov 12, 2024
54110af
Merge branch 'ah/bc-customer-lookup' into staging
ah-s76 Nov 12, 2024
296131a
Update email 2fa and deactivate text/voice 2fa UI entry point
Jan 13, 2025
5b7fcea
Merge email_2fa
zephyranthes03 Jan 13, 2025
8afc785
Update aws-actions/amazon-ecs-deploy-task-definition@v2 from deploy-s…
zephyranthes03 Jan 14, 2025
f4a9e02
hide phone/text 2fa options for admins
zephyranthes03 Jan 14, 2025
2eab7fc
hide phone/text 2fa options for admins
zephyranthes03 Jan 14, 2025
f3fc59d
upgrade phoenix deps
ah-s76 Apr 30, 2024
cbed0c7
branch cleanup
ah-s76 May 2, 2024
6f3a257
Restrict logout redirect uri
zephyranthes03 Jan 14, 2025
798d299
more cleanup
ah-s76 May 13, 2024
3799c25
redirect to store home
zephyranthes03 Jan 14, 2025
4abf9c2
simplify phone assign
zephyranthes03 Jan 14, 2025
9479422
Handle bc checkout redirects
macifell May 21, 2024
29e4bd2
Fix ordering
zephyranthes03 Jan 14, 2025
e2ab3f6
Remove checkout from session
macifell May 21, 2024
218196f
Fix query params
zephyranthes03 Jan 14, 2025
ba88b3c
Fix bc session parameters
zephyranthes03 Jan 14, 2025
6ac4907
link existing customer
zephyranthes03 Jan 14, 2025
528ee25
Update email 2fa and deactivate text/voice 2fa UI entry point
zephyranthes03 Jan 14, 2025
8583bf0
Update aws-actions/amazon-ecs-deploy-task-definition@v2 from deploy-s…
zephyranthes03 Jan 14, 2025
bbac806
Merge branch 'email_2fa' of github.com:system76/recognizer into email…
zephyranthes03 Jan 14, 2025
9d15a3d
rebase patched manually
zephyranthes03 Jan 14, 2025
31c58b8
Update TWO_FACTOR_METHOD_EMAIL from bottle
zephyranthes03 Jan 16, 2025
b6d45e0
Update 2FA 1 minutes delay
zephyranthes03 Jan 17, 2025
4a7fc89
Blocking Resending problem
zephyranthes03 Jan 22, 2025
b48a954
Fix edge case for hotp() update
zephyranthes03 Jan 22, 2025
41c393b
Remove comment
zephyranthes03 Jan 22, 2025
543dbc4
Staing debugging - last_issue-time update issue
zephyranthes03 Jan 22, 2025
22c8111
Staing debugging - last_issue-time update issue
zephyranthes03 Jan 22, 2025
6253a04
Staing debugging - session update issue
zephyranthes03 Jan 23, 2025
858137b
Staing debugging - session update issue
zephyranthes03 Jan 23, 2025
61b7c9c
Staing debugging - session update issue
zephyranthes03 Jan 23, 2025
bc04b9b
Staing debugging - fix redirection issue
zephyranthes03 Jan 23, 2025
918ebe0
Remove comment/logs
zephyranthes03 Jan 23, 2025
bfb4a42
Remove comment/logs
zephyranthes03 Jan 23, 2025
59885a0
Update timeout 15mins
zephyranthes03 Jan 24, 2025
a126c41
Update timeout 15mins
zephyranthes03 Jan 24, 2025
c686730
Update timeout 15mins
zephyranthes03 Jan 24, 2025
577fae0
Update timeout 15mins
zephyranthes03 Jan 24, 2025
9bebc22
Update timeout 15mins/checkout edge point
zephyranthes03 Jan 24, 2025
ef98ba7
Update timeout 15mins/checkout edge point debuging
zephyranthes03 Jan 25, 2025
070f57e
Update timeout 15mins/checkout edge point debuging
zephyranthes03 Jan 25, 2025
7f3c045
Update timeout 15mins/checkout edge point debuging
zephyranthes03 Jan 25, 2025
6948343
Update timeout 15mins/checkout edge point debuging
zephyranthes03 Jan 25, 2025
32bad7b
Update timeout 15mins/checkout edge point debuging
zephyranthes03 Jan 25, 2025
ce82eea
Update timeout 15mins/checkout edge point debuging
zephyranthes03 Jan 25, 2025
bc21efa
Update timeout 15mins/checkout edge point debuging
zephyranthes03 Jan 25, 2025
e594e1a
Update timeout 15mins/checkout edge point debuging
zephyranthes03 Jan 27, 2025
310b426
Update sending issue
zephyranthes03 Jan 27, 2025
608ccd5
Update timeout resending issue
zephyranthes03 Jan 27, 2025
4c49edb
Clear debug message
zephyranthes03 Jan 27, 2025
261a4be
Update recovery code format
zephyranthes03 Jan 27, 2025
5316e8d
Update recovery code format
zephyranthes03 Jan 27, 2025
ffd4974
Update recovery code format
zephyranthes03 Jan 27, 2025
4b7bb68
Update recovery code format
zephyranthes03 Jan 27, 2025
6146eb7
mix format
zephyranthes03 Jan 28, 2025
7422553
Clear source
zephyranthes03 Jan 28, 2025
f23208e
Fix credo filtering
zephyranthes03 Jan 28, 2025
176245f
Fix credo filtering
zephyranthes03 Jan 28, 2025
c1647e5
Fix mix test
zephyranthes03 Jan 29, 2025
e1824da
Fix mix credo
zephyranthes03 Jan 29, 2025
adce8fa
Fix mix format
zephyranthes03 Jan 29, 2025
bb33098
Fix mix test cases
zephyranthes03 Jan 29, 2025
cdcd7d5
Fix mix test cases
zephyranthes03 Jan 29, 2025
8518a49
Update email login test cases
zephyranthes03 Jan 29, 2025
b41e091
Update deploy version cache v2->v4 deploy-task-definition@v1 -> v2
zephyranthes03 Jan 30, 2025
74e1a22
Update commented request/change timeout test conditions
zephyranthes03 Jan 30, 2025
68eda1e
mix format
zephyranthes03 Jan 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
mix local.hex --force

- name: Cache
uses: actions/cache@v2
uses: actions/cache@v4
id: cache
with:
key: elixir-${{ hashFiles('Dockerfile', 'mix.lock') }}-${{ github.ref }}-test
Expand Down Expand Up @@ -114,7 +114,7 @@ jobs:
mix local.hex --force

- name: Cache
uses: actions/cache@v2
uses: actions/cache@v4
id: cache
with:
key: elixir-${{ hashFiles('Dockerfile', 'mix.lock') }}-${{ github.ref }}-credo
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy-production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
image: ${{ steps.publish.outputs.image }}

- name: Deploy
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: ${{ steps.template.outputs.task-definition }}
service: production-system76-recognizer
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
image: ${{ steps.publish.outputs.image }}

- name: Deploy
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: ${{ steps.template.outputs.task-definition }}
service: staging-genesis76-recognizer
Expand Down
26 changes: 13 additions & 13 deletions lib/recognizer/accounts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -626,23 +626,21 @@ defmodule Recognizer.Accounts do
Sends a new notification message to the user to verify their _new_ two factor
settings.
"""
def send_new_two_factor_notification(user) do
def check_two_factor_notification_time(user) do
{:ok, attrs} = get_new_two_factor_settings(user)
send_new_two_factor_notification(user, attrs)
check_two_factor_notification_time(attrs, 100)
end

def send_new_two_factor_notification(user, attrs) do
def check_two_factor_notification_time(attrs, two_factor_issue_time) do
%{
notification_preference: %{two_factor: preference},
two_factor_seed: seed
notification_preference: %{two_factor: preference}
} = attrs

if preference != "app" do
token = Authentication.generate_token(seed)
Notification.deliver_two_factor_token(user, token, String.to_existing_atom(preference))
{:ok, two_factor_issue_time}
else
{:ok, nil}
end

:ok
end

@doc """
Expand All @@ -660,15 +658,17 @@ defmodule Recognizer.Accounts do
@doc """
Confirms the user's two factor settings and persists them to the database from our cache
"""
def confirm_and_save_two_factor_settings(code, user) do
with {:ok, %{two_factor_seed: seed} = attrs} <- get_new_two_factor_settings(user),
true <- Authentication.valid_token?(code, seed) do

def confirm_and_save_two_factor_settings(code, counter, user) do
with {:ok, %{notification_preference: %{two_factor: preference}, two_factor_seed: seed} = attrs} <-
get_new_two_factor_settings(user),
true <- Authentication.valid_token?(preference, code, counter, seed) do
user
|> Repo.preload([:notification_preference, :recovery_codes])
|> User.two_factor_changeset(attrs)
|> Repo.update()
else
_ -> :error
_ -> {:error, nil}
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/recognizer/ecto_enums.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import EctoEnum

defenum(Recognizer.TwoFactorPreference, ["app", "text", "voice"])
defenum(Recognizer.TwoFactorPreference, ["app", "text", "voice", "email"])

defenum(Recognizer.UserType, ["individual", "business"])
defenum(Recognizer.OAuthService, ["facebook", "github", "google"])
1 change: 1 addition & 0 deletions lib/recognizer/notifications/account.ex
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ defmodule Recognizer.Notifications.Account do

def two_factor_method(:text), do: :TWO_FACTOR_METHOD_SMS
def two_factor_method(:voice), do: :TWO_FACTOR_METHOD_VOICE
def two_factor_method(:email), do: :TWO_FACTOR_METHOD_EMAIL

@doc """
Deliver user recovery code used notification.
Expand Down
37 changes: 32 additions & 5 deletions lib/recognizer_web/authentication.ex
Original file line number Diff line number Diff line change
Expand Up @@ -180,15 +180,42 @@ defmodule RecognizerWeb.Authentication do
@doc """
Generate a Time Based One Time Password
"""
def generate_token(%{two_factor_seed: two_factor_seed}), do: generate_token(two_factor_seed)
def generate_token(two_factor_seed), do: :pot.totp(two_factor_seed, addwindow: 1)
def generate_token(preference, counter, %{two_factor_seed: two_factor_seed}),
do: generate_token(preference, counter, two_factor_seed)

def generate_token(preference, counter, two_factor_seed) do
if preference == :app || preference == "app" do
generate_token_app(two_factor_seed)
else
generate_token_external(two_factor_seed, counter)
end
end

def generate_token_app(two_factor_seed), do: :pot.totp(two_factor_seed, interval: 30)

def generate_token_external(two_factor_seed, counter), do: :pot.hotp(two_factor_seed, counter)

@doc """
Validate a user provided token is valid
"""
def valid_token?(token, %{two_factor_seed: two_factor_seed}), do: valid_token?(token, two_factor_seed)
def valid_token?(_token, nil), do: false
def valid_token?(token, two_factor_seed), do: :pot.valid_totp(token, two_factor_seed, window: 1, addwindow: 1)

def valid_token?(preference, token, counter, %{two_factor_seed: two_factor_seed}),
do: valid_token?(preference, token, counter, two_factor_seed)

def valid_token?(preference, token, counter, two_factor_seed) do
if preference == :app || preference == "app" do
valid_token_app?(token, two_factor_seed)
else
valid_token_external?(token, two_factor_seed, counter)
end
end

def valid_token_app?(token, two_factor_seed), do: :pot.valid_totp(token, two_factor_seed, interval: 30)

def valid_token_external?(token, two_factor_seed, counter) do
# :pot.valid_hotp(token, two_factor_seed, last: counter)
token == :pot.hotp(two_factor_seed, counter)
end

defp config(key) do
Application.get_env(:recognizer, __MODULE__)[key]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule RecognizerWeb.Accounts.Api.UserSettingsTwoFactorController do
alias RecognizerWeb.{Authentication, ErrorView}

@one_minute 60_000
@one_day 86_400_000
@one_hour 3_600_000

plug Hammer.Plug,
[
Expand All @@ -14,13 +14,17 @@ defmodule RecognizerWeb.Accounts.Api.UserSettingsTwoFactorController do
]
when action in [:send]

# when action in [:send, :update]

plug Hammer.Plug,
[
rate_limit: {"api:two_factor_daily", @one_day, 6},
rate_limit: {"api:two_factor_hour", @one_hour, 6},
by: {:conn, &get_user_id_from_request/1}
]
when action in [:send]

# when action in [:send, :update]

def show(conn, _params) do
user = Authentication.fetch_current_user(conn)

Expand Down Expand Up @@ -48,8 +52,9 @@ defmodule RecognizerWeb.Accounts.Api.UserSettingsTwoFactorController do

def update(conn, %{"verification" => code}) do
user = Authentication.fetch_current_user(conn)
counter = get_session(conn, :two_factor_issue_time)

case Accounts.confirm_and_save_two_factor_settings(code, user) do
case Accounts.confirm_and_save_two_factor_settings(code, counter, user) do
{:ok, updated_user} ->
render(conn, "show.json", user: updated_user)

Expand All @@ -67,11 +72,34 @@ defmodule RecognizerWeb.Accounts.Api.UserSettingsTwoFactorController do
def send(conn, _params) do
user = Authentication.fetch_current_user(conn)

with {:ok, settings} <- Accounts.get_new_two_factor_settings(user),
:ok <- Accounts.send_new_two_factor_notification(user, settings) do
conn
|> put_status(202)
|> render("show.json", settings: settings, user: user)
with {:ok, settings} <- Accounts.get_new_two_factor_settings(user) do
# Get or initialize the two_factor_issue_time from the session

conn =
case get_session(conn, :two_factor_issue_time) do
nil -> put_session(conn, :two_factor_issue_time, System.system_time(:second))
_ -> conn
end

issue_time = get_session(conn, :two_factor_issue_time)

case Accounts.check_two_factor_notification_time(settings, issue_time) do
{:ok, updated_issue_time} when not is_nil(updated_issue_time) ->
conn
|> put_session(:two_factor_issue_time, updated_issue_time)
|> put_status(202)
|> render("show.json", settings: settings, user: user)

{:ok, nil} ->
conn
|> put_status(202)
|> render("show.json", settings: settings, user: user)

{:error, reason} ->
conn
|> put_status(400)
|> json(%{error: reason})
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ defmodule RecognizerWeb.Accounts.Prompt.TwoFactorController do
def update(conn, params) do
user = conn.assigns.user
two_factor_code = Map.get(params, "two_factor_code", "")
counter = get_session(conn, :two_factor_issue_time)

case Accounts.confirm_and_save_two_factor_settings(two_factor_code, user) do
case Accounts.confirm_and_save_two_factor_settings(two_factor_code, counter, user) do
{:ok, updated_user} ->
Authentication.log_in_user(conn, updated_user, params)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule RecognizerWeb.Accounts.Prompt.VerificationController do
alias RecognizerWeb.Authentication

@one_minute 60_000
@one_day 86_400_000
@one_hour 3_600_000

plug :ensure_user

Expand All @@ -16,13 +16,17 @@ defmodule RecognizerWeb.Accounts.Prompt.VerificationController do
]
when action in [:resend]

# when action in [:resend, :new]

plug Hammer.Plug,
[
rate_limit: {"user:verification_daily", @one_day, 6},
rate_limit: {"user:verification_hour", @one_hour, 6},
by: {:conn, &get_user_id_from_unverified_request/1}
]
when action in [:resend]

# when action in [:resend, :new]

def new(%{assigns: %{user: %{verified_at: nil} = user}} = conn, _params) do
render(conn, "new.html", resend?: false, email: user.email)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ defmodule RecognizerWeb.Accounts.UserResetPasswordController do

plug Hammer.Plug,
[
rate_limit: {"user:reset_password_daily", @one_day, 10},
rate_limit: {"user:reset_password_daily", @one_day, 6},
by: {:conn, &get_email_from_request/1}
]
when action in [:create]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ defmodule RecognizerWeb.Accounts.UserSessionController do
conn
|> put_session(:two_factor_user_id, user.id)
|> put_session(:two_factor_sent, false)
|> put_session(:two_factor_issue_time, System.system_time(:second))
|> redirect(to: Routes.user_two_factor_path(conn, :new))

{:oauth, _user} ->
Expand Down
Loading