From e2f749b5b020bafb0362ce98f37396ab6045fc05 Mon Sep 17 00:00:00 2001 From: Aria Date: Sun, 17 Dec 2023 18:56:15 +0000 Subject: [PATCH 1/4] don't select ueberauth 0.10.6, as it is broken see https://github.com/ueberauth/ueberauth/issues/194 --- mix.exs | 2 +- mix.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 2789a46e7..23f1761b5 100644 --- a/mix.exs +++ b/mix.exs @@ -156,7 +156,7 @@ defmodule Pleroma.Mixfile do {:ex_syslogger, "~> 2.0.0"}, {:floki, "~> 0.34"}, {:timex, "~> 3.7"}, - {:ueberauth, "~> 0.10"}, + {:ueberauth, "== 0.10.5"}, {:linkify, git: "https://akkoma.dev/AkkomaGang/linkify.git"}, {:http_signatures, git: "https://akkoma.dev/AkkomaGang/http_signatures.git", diff --git a/mix.lock b/mix.lock index 501ed8b0a..810356345 100644 --- a/mix.lock +++ b/mix.lock @@ -124,7 +124,7 @@ "timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"}, "trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"}, "tzdata": {:hex, :tzdata, "1.1.1", "20c8043476dfda8504952d00adac41c6eda23912278add38edc140ae0c5bcc46", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a69cec8352eafcd2e198dea28a34113b60fdc6cb57eb5ad65c10292a6ba89787"}, - "ueberauth": {:hex, :ueberauth, "0.10.6", "8dbefd5aec30c5830af2b6ce6e03f62cc28ae0757f34e2986454f54b8dca3f65", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "b0ad1c7508f3cfd5c2c1c668d1a32bafd77de4c56af82c7bfd7e54ed078a7928"}, + "ueberauth": {:hex, :ueberauth, "0.10.5", "806adb703df87e55b5615cf365e809f84c20c68aa8c08ff8a416a5a6644c4b02", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3efd1f31d490a125c7ed453b926f7c31d78b97b8a854c755f5c40064bf3ac9e1"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, "unsafe": {:hex, :unsafe, "1.0.2", "23c6be12f6c1605364801f4b47007c0c159497d0446ad378b5cf05f1855c0581", [:mix], [], "hexpm", "b485231683c3ab01a9cd44cb4a79f152c6f3bb87358439c6f68791b85c2df675"}, "vex": {:hex, :vex, "0.9.1", "cb65348ebd1c4002861b65bef36e524c29d9a879c90119b2d0e674e323124277", [:mix], [], "hexpm", "a0f9f3959d127ad6a6a617c3f607ecfb1bc6f3c59f9c3614a901a46d1765bafe"}, From eb0dbf6b79f2b6055adad2f188c18f0633a50c55 Mon Sep 17 00:00:00 2001 From: Aria Date: Sun, 17 Dec 2023 19:27:36 +0000 Subject: [PATCH 2/4] fix oauth consumer mode the previous code passed a state parameter to ueberauth with info about where to go after the user logged in, etc. since ueberauth 0.7, this parameter is ignored and oauth state is used for actual CSRF reasons. we now set a cookie with the state we need to keep track of, and read it once the callback happens. --- lib/pleroma/web/o_auth/o_auth_controller.ex | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/pleroma/web/o_auth/o_auth_controller.ex b/lib/pleroma/web/o_auth/o_auth_controller.ex index ba33dc9e7..29cbd6aa6 100644 --- a/lib/pleroma/web/o_auth/o_auth_controller.ex +++ b/lib/pleroma/web/o_auth/o_auth_controller.ex @@ -443,13 +443,10 @@ defmodule Pleroma.Web.OAuth.OAuthController do |> Map.put("scope", scope) |> Jason.encode!() - params = - auth_attrs - |> Map.drop(~w(scope scopes client_id redirect_uri)) - |> Map.put("state", state) - # Handing the request to Ueberauth - redirect(conn, to: ~p"/oauth/#{provider}?#{params}") + conn + |> put_resp_cookie("akkoma_oauth_state", state) + |> redirect(to: ~p"/oauth/#{provider}") end def request(%Plug.Conn{} = conn, params) do @@ -468,7 +465,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do end def callback(%Plug.Conn{assigns: %{ueberauth_failure: failure}} = conn, params) do - params = callback_params(params) + params = callback_params(conn, params) messages = for e <- Map.get(failure, :errors, []), do: e.message message = Enum.join(messages, "; ") @@ -481,7 +478,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do end def callback(%Plug.Conn{} = conn, params) do - params = callback_params(params) + params = callback_params(conn, params) with {:ok, registration} <- Authenticator.get_registration(conn) do auth_attrs = Map.take(params, ~w(client_id redirect_uri scope scopes state)) @@ -511,8 +508,9 @@ defmodule Pleroma.Web.OAuth.OAuthController do end end - defp callback_params(%{"state" => state} = params) do - Map.merge(params, Jason.decode!(state)) + defp callback_params(%Plug.Conn{} = conn, params) do + fetch_cookies(conn) + Map.merge(params, Jason.decode!(Map.get(conn.req_cookies, "akkoma_oauth_state", "{}"))) end def registration_details(%Plug.Conn{} = conn, %{"authorization" => auth_attrs}) do From a074be24ca8dedebd6e2e4df3d59a66104667b70 Mon Sep 17 00:00:00 2001 From: Aria Date: Sun, 17 Dec 2023 19:36:27 +0000 Subject: [PATCH 3/4] add bit about frontend configuration to oauth consumer docs --- docs/docs/configuration/cheatsheet.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/docs/configuration/cheatsheet.md b/docs/docs/configuration/cheatsheet.md index 73fdf9eea..2f53f0c78 100644 --- a/docs/docs/configuration/cheatsheet.md +++ b/docs/docs/configuration/cheatsheet.md @@ -958,6 +958,15 @@ config :ueberauth, Ueberauth, ] ``` +You may also need to set up your frontend to use oauth logins. For example, for `akkoma-fe`: + +```elixir +config :pleroma, :frontend_configurations, + pleroma_fe: %{ + loginMethod: "token" + } +``` + ## Link parsing ### :uri_schemes From 77000b8ffd92f18d7c901a545b48df7d5a5d41a4 Mon Sep 17 00:00:00 2001 From: Aria Date: Sun, 17 Dec 2023 21:47:47 +0000 Subject: [PATCH 4/4] update tests for oauth consumer --- lib/pleroma/web/o_auth/o_auth_controller.ex | 23 +++++--- .../web/o_auth/o_auth_controller_test.exs | 55 +++++++++++++------ 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/lib/pleroma/web/o_auth/o_auth_controller.ex b/lib/pleroma/web/o_auth/o_auth_controller.ex index 29cbd6aa6..29aa8c10e 100644 --- a/lib/pleroma/web/o_auth/o_auth_controller.ex +++ b/lib/pleroma/web/o_auth/o_auth_controller.ex @@ -39,6 +39,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do action_fallback(Pleroma.Web.OAuth.FallbackController) @oob_token_redirect_uri "urn:ietf:wg:oauth:2.0:oob" + @state_cookie_name "akkoma_oauth_state" # Note: this definition is only called from error-handling methods with `conn.params` as 2nd arg def authorize(%Plug.Conn{} = conn, %{"authorization" => _} = params) do @@ -445,7 +446,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do # Handing the request to Ueberauth conn - |> put_resp_cookie("akkoma_oauth_state", state) + |> put_resp_cookie(@state_cookie_name, state) |> redirect(to: ~p"/oauth/#{provider}") end @@ -469,12 +470,18 @@ defmodule Pleroma.Web.OAuth.OAuthController do messages = for e <- Map.get(failure, :errors, []), do: e.message message = Enum.join(messages, "; ") - conn - |> put_flash( - :error, - dgettext("errors", "Failed to authenticate: %{message}.", message: message) - ) - |> redirect(external: redirect_uri(conn, params["redirect_uri"])) + error_message = dgettext("errors", "Failed to authenticate: %{message}.", message: message) + + if params["redirect_uri"] do + conn + |> put_flash( + :error, + error_message + ) + |> redirect(external: redirect_uri(conn, params["redirect_uri"])) + else + send_resp(conn, :bad_request, error_message) + end end def callback(%Plug.Conn{} = conn, params) do @@ -510,7 +517,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do defp callback_params(%Plug.Conn{} = conn, params) do fetch_cookies(conn) - Map.merge(params, Jason.decode!(Map.get(conn.req_cookies, "akkoma_oauth_state", "{}"))) + Map.merge(params, Jason.decode!(Map.get(conn.req_cookies, @state_cookie_name, "{}"))) end def registration_details(%Plug.Conn{} = conn, %{"authorization" => auth_attrs}) do diff --git a/test/pleroma/web/o_auth/o_auth_controller_test.exs b/test/pleroma/web/o_auth/o_auth_controller_test.exs index e0ba339db..d0703f44c 100644 --- a/test/pleroma/web/o_auth/o_auth_controller_test.exs +++ b/test/pleroma/web/o_auth/o_auth_controller_test.exs @@ -81,9 +81,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do assert html_response(conn, 302) - redirect_query = URI.parse(redirected_to(conn)).query - assert %{"state" => state_param} = URI.decode_query(redirect_query) - assert {:ok, state_components} = Jason.decode(state_param) + assert {:ok, state_components} = Jason.decode(conn.resp_cookies["akkoma_oauth_state"].value) expected_client_id = app.client_id expected_redirect_uri = app.redirect_uris @@ -97,7 +95,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do end test "with user-bound registration, GET /oauth//callback redirects to `redirect_uri` with `code`", - %{app: app, conn: conn} do + %{app: app, conn: _} do registration = insert(:registration) redirect_uri = OAuthController.default_redirect_uri(app) @@ -109,15 +107,17 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do } conn = - conn + build_conn() + |> put_req_cookie("akkoma_oauth_state", Jason.encode!(state_params)) + |> Plug.Session.call(Plug.Session.init(@session_opts)) + |> fetch_session() |> assign(:ueberauth_auth, %{provider: registration.provider, uid: registration.uid}) |> get( "/oauth/twitter/callback", %{ "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM", "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs", - "provider" => "twitter", - "state" => Jason.encode!(state_params) + "provider" => "twitter" } ) @@ -162,15 +162,42 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do test "on authentication error, GET /oauth//callback redirects to `redirect_uri`", %{ app: app, - conn: conn + conn: _ } do state_params = %{ "scope" => Enum.join(app.scopes, " "), "client_id" => app.client_id, - "redirect_uri" => OAuthController.default_redirect_uri(app), - "state" => "" + "redirect_uri" => OAuthController.default_redirect_uri(app) } + conn = + build_conn() + |> put_req_cookie("akkoma_oauth_state", Jason.encode!(state_params)) + |> Plug.Session.call(Plug.Session.init(@session_opts)) + |> fetch_session() + |> assign(:ueberauth_failure, %{errors: [%{message: "(error description)"}]}) + |> get( + "/oauth/twitter/callback", + %{ + "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM", + "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs", + "provider" => "twitter", + "state" => "" + } + ) + + assert html_response(conn, 302) + assert redirected_to(conn) == app.redirect_uris + + assert Phoenix.Flash.get(conn.assigns.flash, :error) == + "Failed to authenticate: (error description)." + end + + test "on authentication error with no prior state, GET /oauth//callback returns 400", + %{ + app: _, + conn: conn + } do conn = conn |> assign(:ueberauth_failure, %{errors: [%{message: "(error description)"}]}) @@ -180,15 +207,11 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM", "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs", "provider" => "twitter", - "state" => Jason.encode!(state_params) + "state" => "" } ) - assert html_response(conn, 302) - assert redirected_to(conn) == app.redirect_uris - - assert Phoenix.Flash.get(conn.assigns.flash, :error) == - "Failed to authenticate: (error description)." + assert response(conn, 400) end test "GET /oauth/registration_details renders registration details form", %{