From 3e4768efca88124b3ae418d41da923c428598275 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sun, 4 Aug 2024 13:59:13 -0400 Subject: [PATCH 1/2] Revert "Remove invalid test" This reverts commit d0f4b2b02fc3aee3f08239d9c188ca5a2e8ad482. --- test/pleroma/integration/mastodon_websocket_test.exs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/pleroma/integration/mastodon_websocket_test.exs b/test/pleroma/integration/mastodon_websocket_test.exs index f499f54ade..88f32762d5 100644 --- a/test/pleroma/integration/mastodon_websocket_test.exs +++ b/test/pleroma/integration/mastodon_websocket_test.exs @@ -268,6 +268,17 @@ test "accepts the 'user:notification' stream", %{token: token} = _state do end) end + test "accepts valid token on Sec-WebSocket-Protocol header", %{token: token} do + assert {:ok, _} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", token.token}]) + + capture_log(fn -> + assert {:error, %WebSockex.RequestError{code: 401}} = + start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}]) + + Process.sleep(30) + end) + end + test "accepts valid token on client-sent event", %{token: token} do assert {:ok, pid} = start_socket() From 8c91fd8785c25e694d9341b17b5182041c575166 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sun, 4 Aug 2024 14:58:16 -0400 Subject: [PATCH 2/2] Fix Mastodon WebSocket authentication Mastodon uses the Sec-Websocket-Protocol header to send the auth token. It is not clear if this is a violation of the RFC, but Mastodon is not the first application in the wild to use this header for authentication purposes. Phoenix does not allow accessing this header, so we work around it temporarily with a minor patch to Phoenix 1.7.14. We will reach out to Phoenix to discuss how to make this use case possible. --- changelog.d/mastodon-websocket.fix | 1 + lib/pleroma/web/endpoint.ex | 1 + lib/pleroma/web/mastodon_api/websocket_handler.ex | 11 ++++++++++- mix.exs | 3 ++- mix.lock | 4 ++-- 5 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 changelog.d/mastodon-websocket.fix diff --git a/changelog.d/mastodon-websocket.fix b/changelog.d/mastodon-websocket.fix new file mode 100644 index 0000000000..2c4fe86ef6 --- /dev/null +++ b/changelog.d/mastodon-websocket.fix @@ -0,0 +1 @@ +Fix Mastodon WebSocket authentication diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index fef907ace6..bab3c9fd00 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -14,6 +14,7 @@ defmodule Pleroma.Web.Endpoint do websocket: [ path: "/", compress: false, + connect_info: [:sec_websocket_protocol], error_handler: {Pleroma.Web.MastodonAPI.WebsocketHandler, :handle_error, []}, fullsweep_after: 20 ] diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex index 730295a4ce..3ed1cdd6cd 100644 --- a/lib/pleroma/web/mastodon_api/websocket_handler.ex +++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex @@ -22,7 +22,7 @@ def child_spec(_opts), do: :ignore # This only prepares the connection and is not in the process yet @impl Phoenix.Socket.Transport def connect(%{params: params} = transport_info) do - with access_token <- Map.get(params, "access_token"), + with access_token <- find_access_token(transport_info), {:ok, user, oauth_token} <- authenticate_request(access_token), {:ok, topic} <- Streamer.get_topic(params["stream"], user, oauth_token, params) do @@ -244,4 +244,13 @@ def handle_error(conn, :unauthorized) do def handle_error(conn, _reason) do Plug.Conn.send_resp(conn, 404, "Not Found") end + + defp find_access_token(%{ + connect_info: %{sec_websocket_protocol: [token]} + }), + do: token + + defp find_access_token(%{params: %{"access_token" => token}}), do: token + + defp find_access_token(_), do: nil end diff --git a/mix.exs b/mix.exs index 69e52e5264..88b558a758 100644 --- a/mix.exs +++ b/mix.exs @@ -132,7 +132,8 @@ defp logger_deps do # Type `mix help deps` for examples and options. defp deps do [ - {:phoenix, "~> 1.7.3"}, + {:phoenix, + git: "https://github.com/feld/phoenix", branch: "v1.7.14-websocket-headers", override: true}, {:phoenix_ecto, "~> 4.4"}, {:ecto_sql, "~> 3.10"}, {:ecto_enum, "~> 1.4"}, diff --git a/mix.lock b/mix.lock index 61ede9e5eb..a26ac0e84c 100644 --- a/mix.lock +++ b/mix.lock @@ -65,7 +65,7 @@ "httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "inet_cidr": {:hex, :inet_cidr, "1.0.8", "d26bb7bdbdf21ae401ead2092bf2bb4bf57fe44a62f5eaa5025280720ace8a40", [:mix], [], "hexpm", "d5b26da66603bb56c933c65214c72152f0de9a6ea53618b56d63302a68f6a90e"}, - "jason": {:hex, :jason, "1.4.3", "d3f984eeb96fe53b85d20e0b049f03e57d075b5acda3ac8d465c969a2536c17b", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "9a90e868927f7c777689baa16d86f4d0e086d968db5c05d917ccff6d443e58a3"}, + "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "joken": {:hex, :joken, "2.6.0", "b9dd9b6d52e3e6fcb6c65e151ad38bf4bc286382b5b6f97079c47ade6b1bcc6a", [:mix], [{:jose, "~> 1.11.5", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5a95b05a71cd0b54abd35378aeb1d487a23a52c324fa7efdffc512b655b5aaa7"}, "jose": {:hex, :jose, "1.11.6", "613fda82552128aa6fb804682e3a616f4bc15565a048dabd05b1ebd5827ed965", [:mix, :rebar3], [], "hexpm", "6275cb75504f9c1e60eeacb771adfeee4905a9e182103aa59b53fed651ff9738"}, "jumper": {:hex, :jumper, "1.0.2", "68cdcd84472a00ac596b4e6459a41b3062d4427cbd4f1e8c8793c5b54f1406a7", [:mix], [], "hexpm", "9b7782409021e01ab3c08270e26f36eb62976a38c1aa64b2eaf6348422f165e1"}, @@ -94,7 +94,7 @@ "open_api_spex": {:hex, :open_api_spex, "3.18.2", "8c855e83bfe8bf81603d919d6e892541eafece3720f34d1700b58024dadde247", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0 or ~> 4.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "aa3e6dcfc0ad6a02596b2172662da21c9dd848dac145ea9e603f54e3d81b8d2b"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"}, - "phoenix": {:hex, :phoenix, "1.7.14", "a7d0b3f1bc95987044ddada111e77bd7f75646a08518942c72a8440278ae7825", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "c7859bc56cc5dfef19ecfc240775dae358cbaa530231118a9e014df392ace61a"}, + "phoenix": {:git, "https://github.com/feld/phoenix", "fb6dc76c657422e49600896c64aab4253fceaef6", [branch: "v1.7.14-websocket-headers"]}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.3", "86e9878f833829c3f66da03d75254c155d91d72a201eb56ae83482328dc7ca93", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "d36c401206f3011fefd63d04e8ef626ec8791975d9d107f9a0817d426f61ac07"}, "phoenix_html": {:hex, :phoenix_html, "3.3.4", "42a09fc443bbc1da37e372a5c8e6755d046f22b9b11343bf885067357da21cb3", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0249d3abec3714aff3415e7ee3d9786cb325be3151e6c4b3021502c585bf53fb"}, "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.3", "7ff51c9b6609470f681fbea20578dede0e548302b0c8bdf338b5a753a4f045bf", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "f9470a0a8bae4f56430a23d42f977b5a6205fdba6559d76f932b876bfaec652d"},