+ conn
+ |> put_flash(:error, dgettext("errors", "Unlisted redirect_uri."))
+ |> redirect(external: redirect_uri(conn, redirect_uri))
+ end
+ end
+
+ defp handle_create_authorization_error(
+ %Plug.Conn{} = conn,
+ {:error, scopes_issue},
+ %{"authorization" => _} = params
+ )
+ when scopes_issue in [:unsupported_scopes, :missing_scopes] do
+ # Per https://github.com/tootsuite/mastodon/blob/
+ # 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L39
+ conn
+ |> put_flash(:error, dgettext("errors", "This action is outside the authorized scopes"))
+ |> put_status(:unauthorized)
+ |> authorize(params)
+ end
+
+ defp handle_create_authorization_error(
+ %Plug.Conn{} = conn,
+ {:auth_active, false},
+ %{"authorization" => _} = params
+ ) do
+ # Per https://github.com/tootsuite/mastodon/blob/
+ # 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L76
+ conn
+ |> put_flash(:error, dgettext("errors", "Your login is missing a confirmed e-mail address"))
+ |> put_status(:forbidden)
+ |> authorize(params)
+ end
+
+ defp handle_create_authorization_error(%Plug.Conn{} = conn, error, %{"authorization" => _}) do
+ Authenticator.handle_error(conn, error)
+ end
+
+ @doc "Renew access_token with refresh_token"
+ def token_exchange(
+ %Plug.Conn{} = conn,
+ %{"grant_type" => "refresh_token", "refresh_token" => token} = _params
+ ) do
+ with {:ok, app} <- Token.Utils.fetch_app(conn),
+ {:ok, %{user: user} = token} <- Token.get_by_refresh_token(app, token),
+ {:ok, token} <- RefreshToken.grant(token) do
+ response_attrs = %{created_at: Token.Utils.format_created_at(token)}
+
+ json(conn, Token.Response.build(user, token, response_attrs))
+ else
+ _error -> render_invalid_credentials_error(conn)
+ end
+ end
+
+ def token_exchange(%Plug.Conn{} = conn, %{"grant_type" => "authorization_code"} = params) do
+ with {:ok, app} <- Token.Utils.fetch_app(conn),
+ fixed_token = Token.Utils.fix_padding(params["code"]),
+ {:ok, auth} <- Authorization.get_by_token(app, fixed_token),
+ %User{} = user <- User.get_cached_by_id(auth.user_id),
+ {:ok, token} <- Token.exchange_token(app, auth) do
+ response_attrs = %{created_at: Token.Utils.format_created_at(token)}
+
+ json(conn, Token.Response.build(user, token, response_attrs))
+ else
+ _error -> render_invalid_credentials_error(conn)