summaryrefslogtreecommitdiff
path: root/lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex
blob: eba4523002efc2033ab02ea34c43d1eaa26aa3e8 (plain)
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-2020 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