1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.TwoFactorAuthenticationController do
@moduledoc "The module represents actions to manage MFA"
use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
alias Pleroma.MFA
alias Pleroma.MFA.TOTP
alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.Plugs.OAuthScopesPlug
plug(OAuthScopesPlug, %{scopes: ["read:security"]} when action in [:settings])
plug(
OAuthScopesPlug,
%{scopes: ["write:security"]} when action in [:setup, :confirm, :disable, :backup_codes]
)
@doc """
Gets user multi factor authentication settings
## Endpoint
GET /api/pleroma/accounts/mfa
"""
def settings(%{assigns: %{user: user}} = conn, _params) do
json(conn, %{settings: MFA.mfa_settings(user)})
end
@doc """
Prepare setup mfa method
## Endpoint
GET /api/pleroma/accounts/mfa/setup/[:method]
"""
def setup(%{assigns: %{user: user}} = conn, %{"method" => "totp"} = _params) do
with {:ok, user} <- MFA.setup_totp(user),
%{secret: secret} = _ <- user.multi_factor_authentication_settings.totp do
provisioning_uri = TOTP.provisioning_uri(secret, "#{user.email}")
json(conn, %{provisioning_uri: provisioning_uri, key: secret})
else
{:error, message} ->
json_response(conn, :unprocessable_entity, %{error: message})
end
end
def setup(conn, _params) do
json_response(conn, :bad_request, %{error: "undefined method"})
end
@doc """
Confirms setup and enable mfa method
## Endpoint
POST /api/pleroma/accounts/mfa/confirm/:method
- params:
`code` - confirmation code
`password` - current password
"""
def confirm(
%{assigns: %{user: user}} = conn,
%{"method" => "totp", "password" => _, "code" => _} = params
) do
with {:ok, _user} <- Utils.confirm_current_password(user, params["password"]),
{:ok, _user} <- MFA.confirm_totp(user, params) do
json(conn, %{})
else
{:error, message} ->
json_response(conn, :unprocessable_entity, %{error: message})
end
end
def confirm(conn, _) do
json_response(conn, :bad_request, %{error: "undefined mfa method"})
end
@doc """
Disable mfa method and disable mfa if need.
"""
def disable(%{assigns: %{user: user}} = conn, %{"method" => "totp"} = params) do
with {:ok, user} <- Utils.confirm_current_password(user, params["password"]),
{:ok, _user} <- MFA.disable_totp(user) do
json(conn, %{})
else
{:error, message} ->
json_response(conn, :unprocessable_entity, %{error: message})
end
end
def disable(%{assigns: %{user: user}} = conn, %{"method" => "mfa"} = params) do
with {:ok, user} <- Utils.confirm_current_password(user, params["password"]),
{:ok, _user} <- MFA.disable(user) do
json(conn, %{})
else
{:error, message} ->
json_response(conn, :unprocessable_entity, %{error: message})
end
end
def disable(conn, _) do
json_response(conn, :bad_request, %{error: "undefined mfa method"})
end
@doc """
Generates backup codes.
## Endpoint
GET /api/pleroma/accounts/mfa/backup_codes
## Response
### Success
`{codes: [codes]}`
### Error
`{error: [error_message]}`
"""
def backup_codes(%{assigns: %{user: user}} = conn, _params) do
with {:ok, codes} <- MFA.generate_backup_codes(user) do
json(conn, %{codes: codes})
else
{:error, message} ->
json_response(conn, :unprocessable_entity, %{error: message})
end
end
end
|