Merge branch 'develop' into feature/bulk-confirmation
[akkoma] / lib / pleroma / web / oauth / oauth_controller.ex
index 7683589cf2b347fb6d604c1e7a5bca788710041f..a4152e84090426b8e396e1f10f4a68d3d6611baf 100644 (file)
@@ -76,6 +76,13 @@ defmodule Pleroma.Web.OAuth.OAuthController do
     available_scopes = (app && app.scopes) || []
     scopes = Scopes.fetch_scopes(params, available_scopes)
 
+    scopes =
+      if scopes == [] do
+        available_scopes
+      else
+        scopes
+      end
+
     # Note: `params` might differ from `conn.params`; use `@params` not `@conn.params` in template
     render(conn, Authenticator.auth_template(), %{
       response_type: params["response_type"],
@@ -112,7 +119,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
       redirect_uri = redirect_uri(conn, redirect_uri)
       url_params = %{access_token: token.token}
       url_params = Maps.put_if_present(url_params, :state, params["state"])
-      url = UriHelper.append_uri_params(redirect_uri, url_params)
+      url = UriHelper.modify_uri_params(redirect_uri, url_params)
       redirect(conn, external: url)
     else
       conn
@@ -138,7 +145,10 @@ defmodule Pleroma.Web.OAuth.OAuthController do
   def after_create_authorization(%Plug.Conn{} = conn, %Authorization{} = auth, %{
         "authorization" => %{"redirect_uri" => @oob_token_redirect_uri}
       }) do
-    render(conn, "oob_authorization_created.html", %{auth: auth})
+    # Enforcing the view to reuse the template when calling from other controllers
+    conn
+    |> put_view(OAuthView)
+    |> render("oob_authorization_created.html", %{auth: auth})
   end
 
   def after_create_authorization(%Plug.Conn{} = conn, %Authorization{} = auth, %{
@@ -151,7 +161,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
       redirect_uri = redirect_uri(conn, redirect_uri)
       url_params = %{code: auth.token}
       url_params = Maps.put_if_present(url_params, :state, auth_attrs["state"])
-      url = UriHelper.append_uri_params(redirect_uri, url_params)
+      url = UriHelper.modify_uri_params(redirect_uri, url_params)
       redirect(conn, external: url)
     else
       conn
@@ -190,7 +200,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
          {:mfa_required, user, auth, _},
          params
        ) do
-    {:ok, token} = MFA.Token.create_token(user, auth)
+    {:ok, token} = MFA.Token.create(user, auth)
 
     data = %{
       "mfa_token" => token.token,
@@ -260,11 +270,8 @@ defmodule Pleroma.Web.OAuth.OAuthController do
       ) do
     with {:ok, %User{} = user} <- Authenticator.get_user(conn),
          {:ok, app} <- Token.Utils.fetch_app(conn),
-         {:account_status, :active} <- {:account_status, User.account_status(user)},
-         {:ok, scopes} <- validate_scopes(app, params),
-         {:ok, auth} <- Authorization.create_authorization(app, user, scopes),
-         {:mfa_required, _, _, false} <- {:mfa_required, user, auth, MFA.require?(user)},
-         {:ok, token} <- Token.exchange_token(app, auth) do
+         requested_scopes <- Scopes.fetch_scopes(params, app.scopes),
+         {:ok, token} <- login(user, app, requested_scopes) do
       json(conn, OAuthView.render("token.json", %{user: user, token: token}))
     else
       error ->
@@ -337,6 +344,16 @@ defmodule Pleroma.Web.OAuth.OAuthController do
     )
   end
 
+  defp handle_token_exchange_error(%Plug.Conn{} = conn, {:account_status, :approval_pending}) do
+    render_error(
+      conn,
+      :forbidden,
+      "Your account is awaiting approval.",
+      %{},
+      "awaiting_approval"
+    )
+  end
+
   defp handle_token_exchange_error(%Plug.Conn{} = conn, _error) do
     render_invalid_credentials_error(conn)
   end
@@ -512,6 +529,8 @@ defmodule Pleroma.Web.OAuth.OAuthController do
     end
   end
 
+  defp do_create_authorization(conn, auth_attrs, user \\ nil)
+
   defp do_create_authorization(
          %Plug.Conn{} = conn,
          %{
@@ -521,19 +540,37 @@ defmodule Pleroma.Web.OAuth.OAuthController do
                "redirect_uri" => redirect_uri
              } = auth_attrs
          },
-         user \\ nil
+         user
        ) do
     with {_, {:ok, %User{} = user}} <-
            {:get_user, (user && {:ok, user}) || Authenticator.get_user(conn)},
          %App{} = app <- Repo.get_by(App, client_id: client_id),
          true <- redirect_uri in String.split(app.redirect_uris),
-         {:ok, scopes} <- validate_scopes(app, auth_attrs),
-         {:account_status, :active} <- {:account_status, User.account_status(user)},
-         {:ok, auth} <- Authorization.create_authorization(app, user, scopes) do
+         requested_scopes <- Scopes.fetch_scopes(auth_attrs, app.scopes),
+         {:ok, auth} <- do_create_authorization(user, app, requested_scopes) do
       {:ok, auth, user}
     end
   end
 
+  defp do_create_authorization(%User{} = user, %App{} = app, requested_scopes)
+       when is_list(requested_scopes) do
+    with {:account_status, :active} <- {:account_status, User.account_status(user)},
+         {:ok, scopes} <- validate_scopes(app, requested_scopes),
+         {:ok, auth} <- Authorization.create_authorization(app, user, scopes) do
+      {:ok, auth}
+    end
+  end
+
+  # Note: intended to be a private function but opened for AccountController that logs in on signup
+  @doc "If checks pass, creates authorization and token for given user, app and requested scopes."
+  def login(%User{} = user, %App{} = app, requested_scopes) when is_list(requested_scopes) do
+    with {:ok, auth} <- do_create_authorization(user, app, requested_scopes),
+         {:mfa_required, _, _, false} <- {:mfa_required, user, auth, MFA.require?(user)},
+         {:ok, token} <- Token.exchange_token(app, auth) do
+      {:ok, token}
+    end
+  end
+
   # Special case: Local MastodonFE
   defp redirect_uri(%Plug.Conn{} = conn, "."), do: auth_url(conn, :login)
 
@@ -545,17 +582,20 @@ defmodule Pleroma.Web.OAuth.OAuthController do
     do: put_session(conn, :registration_id, registration_id)
 
   defp build_and_response_mfa_token(user, auth) do
-    with {:ok, token} <- MFA.Token.create_token(user, auth) do
+    with {:ok, token} <- MFA.Token.create(user, auth) do
       MFAView.render("mfa_response.json", %{token: token, user: user})
     end
   end
 
-  @spec validate_scopes(App.t(), map()) ::
+  @spec validate_scopes(App.t(), map() | list()) ::
           {:ok, list()} | {:error, :missing_scopes | :unsupported_scopes}
-  defp validate_scopes(%App{} = app, params) do
-    params
-    |> Scopes.fetch_scopes(app.scopes)
-    |> Scopes.validate(app.scopes)
+  defp validate_scopes(%App{} = app, params) when is_map(params) do
+    requested_scopes = Scopes.fetch_scopes(params, app.scopes)
+    validate_scopes(app, requested_scopes)
+  end
+
+  defp validate_scopes(%App{} = app, requested_scopes) when is_list(requested_scopes) do
+    Scopes.validate(requested_scopes, app.scopes)
   end
 
   def default_redirect_uri(%App{} = app) do