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