summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaelwenn (lanodan) Monnier <contact@hacktivis.me>2023-09-07 01:33:35 +0200
committerHaelwenn (lanodan) Monnier <contact@hacktivis.me>2023-09-10 09:55:12 +0200
commitbeddda85475bd475249b7eed2442f823175fedf8 (patch)
treeeb79b0c6ee2a6bd65b9f7ef6a6d1b5f03f9842f2
parent44f272b9fae270330a17ddf2c8cce410e6c35f21 (diff)
WebFinger: Serve /.well-known/host-meta.json requestscleanup-webfinger-xml
-rw-r--r--changelog.d/host-meta-json.add1
-rw-r--r--docs/configuration/how_to_serve_another_domain_for_webfinger.md5
-rw-r--r--lib/pleroma/web/router.ex1
-rw-r--r--lib/pleroma/web/web_finger.ex80
-rw-r--r--lib/pleroma/web/web_finger/web_finger_controller.ex8
-rw-r--r--test/pleroma/web/web_finger/web_finger_controller_test.exs22
6 files changed, 107 insertions, 10 deletions
diff --git a/changelog.d/host-meta-json.add b/changelog.d/host-meta-json.add
new file mode 100644
index 000000000..f145530db
--- /dev/null
+++ b/changelog.d/host-meta-json.add
@@ -0,0 +1 @@
+- Add support for serving `/.well-known/host-meta.json` requests. If you use a different hostname between WebFinger and Pleroma, like say `user@domain.tld` pointing to `social.domain.tld`, you'll need to update your HTTP server configuration.
diff --git a/docs/configuration/how_to_serve_another_domain_for_webfinger.md b/docs/configuration/how_to_serve_another_domain_for_webfinger.md
index 5ae3e7943..b41c84850 100644
--- a/docs/configuration/how_to_serve_another_domain_for_webfinger.md
+++ b/docs/configuration/how_to_serve_another_domain_for_webfinger.md
@@ -21,7 +21,7 @@ Both account identifiers are unique and required for Pleroma. An important risk
As said earlier, each Pleroma user has an `acct`: URI, which is used for discovery and authentication. When you add @user@example.org, a webfinger query is performed. This is done in two steps:
-1. Querying `https://example.org/.well-known/host-meta` (where the domain of the URL matches the domain part of the `acct`: URI) to get information on how to perform the query.
+1. Querying `https://example.org/.well-known/host-meta` or `https://example.org/.well-known/host-meta.json` (where the domain of the URL matches the domain part of the `acct`: URI) to get information on how to perform the query.
This file will indeed contain a URL template of the form `https://example.org/.well-known/webfinger?resource={uri}` that will be used in the second step.
2. Fill the returned template with the `acct`: URI to be queried and perform the query: `https://example.org/.well-known/webfinger?resource=acct:user@example.org`
@@ -57,6 +57,9 @@ With nginx, it would be as simple as adding:
location = /.well-known/host-meta {
return 301 https://pleroma.example.org$request_uri;
}
+location = /.well-known/host-meta.json {
+ return 301 https://pleroma.example.org$request_uri;
+}
```
in example.org's server block.
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 6b9e158a3..16c9b75bb 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -920,6 +920,7 @@ defmodule Pleroma.Web.Router do
pipe_through(:well_known)
get("/host-meta", WebFinger.WebFingerController, :host_meta)
+ get("/host-meta.json", WebFinger.WebFingerController, :host_meta_json)
get("/webfinger", WebFinger.WebFingerController, :webfinger)
get("/nodeinfo", Nodeinfo.NodeinfoController, :schemas)
end
diff --git a/lib/pleroma/web/web_finger.ex b/lib/pleroma/web/web_finger.ex
index f95dc2458..979cc09fd 100644
--- a/lib/pleroma/web/web_finger.ex
+++ b/lib/pleroma/web/web_finger.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.WebFinger do
@@ -9,9 +9,13 @@ defmodule Pleroma.Web.WebFinger do
alias Pleroma.Web.Federator.Publisher
alias Pleroma.Web.XML
alias Pleroma.XmlBuilder
+
require Jason
require Logger
+ @cachex Pleroma.Config.get([:cachex, :provider], Cachex)
+
+ # Technically not WebFinger but RFC6415 "Web Host Metadata"
def host_meta do
base_url = Endpoint.url()
@@ -30,6 +34,21 @@ defmodule Pleroma.Web.WebFinger do
|> XmlBuilder.to_doc()
end
+ def host_meta_json do
+ base_url = Endpoint.url()
+
+ %{
+ "links" => [
+ %{
+ "rel" => "lrdd",
+ "type" => "application/jrd+json",
+ "template" => "#{base_url}/.well-known/webfinger?resource={uri}"
+ }
+ ]
+ }
+ end
+
+ # WebFinger RFC7033
def webfinger(resource, fmt) when fmt in ["XML", "JSON"] do
host = Pleroma.Web.Endpoint.host()
@@ -68,7 +87,7 @@ defmodule Pleroma.Web.WebFinger do
[user.ap_id | user.also_known_as]
end
- def represent_user(user, "JSON") do
+ defp represent_user(user, "JSON") do
%{
"subject" => "acct:#{user.nickname}@#{domain()}",
"aliases" => gather_aliases(user),
@@ -76,7 +95,7 @@ defmodule Pleroma.Web.WebFinger do
}
end
- def represent_user(user, "XML") do
+ defp represent_user(user, "XML") do
aliases =
user
|> gather_aliases()
@@ -146,7 +165,7 @@ defmodule Pleroma.Web.WebFinger do
end
end
- def get_template_from_xml(body) do
+ defp get_template_from_xml(body) do
xpath = "//Link[@rel='lrdd']/@template"
with {:ok, doc} <- XML.parse_document(body),
@@ -155,16 +174,61 @@ defmodule Pleroma.Web.WebFinger do
end
end
- def find_lrdd_template(domain) do
+ defp cached_find_lrdd_template(domain) do
+ @cachex.fetch!(:webfinger_cache, "lrdd:#{domain}", fn _ ->
+ with {:ok, _} = template <- find_lrdd_template(domain) do
+ {:commit, template}
+ else
+ e -> {:ignore, e}
+ end
+ end)
+ end
+
+ defp find_lrdd_template(domain) do
+ with {:ok, _} = template <- find_lrdd_template_json(domain) do
+ template
+ else
+ :error ->
+ with {:ok, _} = template <- find_lrdd_template_xml(domain) do
+ template
+ end
+ end
+ end
+
+ defp find_lrdd_template_json(domain) do
+ # WebFinger is restricted to HTTPS - https://tools.ietf.org/html/rfc7033#section-9.1
+ meta_url = "https://#{domain}/.well-known/host-meta.json"
+
+ with
+ {_, {:ok, %{status: status, body: body} = resp}} when status in 200..299 <- {:http, HTTP.get(meta_json_url, [{"accept", "application/json"}])},
+ {_, content_type} when is_binary(content_type) <- {:content_type, Tesla.get_header(resp, "content-type")},
+ true <- content_type =~ ~r[^application/(jrd\+)?json(; .*)?],
+ %{"template" => template} <- Enum.find(body, fn link -> link["rel"] == "lrdd" and Map.has_key?(link, "template" end) do
+ {:ok, template}
+ else
+ {:http, e} ->
+ Logger.warn("WebFinger: #{meta_url} HTTP error: #{inspect(e)}")
+ :error
+ {:content_type, _} ->
+ Logger.warn("WebFinger: #{meta_url} had no Content-Type header")
+ :error
+ false ->
+ Logger.warn("WebFinger: #{meta_url} gave #{content_type} instead of application/json")
+ :error
+ nil ->
+ Logger.warn("WebFinger: #{meta_url} had no LRDD template")
+ :error
+ end
+ end
+
+ defp find_lrdd_template_xml(domain) do
# WebFinger is restricted to HTTPS - https://tools.ietf.org/html/rfc7033#section-9.1
meta_url = "https://#{domain}/.well-known/host-meta"
with {:ok, %{status: status, body: body}} when status in 200..299 <- HTTP.get(meta_url) do
get_template_from_xml(body)
else
- error ->
- Logger.warn("Can't find LRDD template in #{inspect(meta_url)}: #{inspect(error)}")
- {:error, :lrdd_not_found}
+ error -> {:error, error}
end
end
diff --git a/lib/pleroma/web/web_finger/web_finger_controller.ex b/lib/pleroma/web/web_finger/web_finger_controller.ex
index 093a8efd0..0733fe9db 100644
--- a/lib/pleroma/web/web_finger/web_finger_controller.ex
+++ b/lib/pleroma/web/web_finger/web_finger_controller.ex
@@ -18,6 +18,14 @@ defmodule Pleroma.Web.WebFinger.WebFingerController do
|> send_resp(200, xml)
end
+ def host_meta_json(conn, _params) do
+ jrd = WebFinger.host_meta_json()
+
+ conn
+ |> put_resp_content_type("application/jrd+json")
+ |> json(jrd)
+ end
+
def webfinger(%{assigns: %{format: format}} = conn, %{"resource" => resource})
when format in ["xml", "xrd+xml"] do
with {:ok, response} <- WebFinger.webfinger(resource, "XML") do
diff --git a/test/pleroma/web/web_finger/web_finger_controller_test.exs b/test/pleroma/web/web_finger/web_finger_controller_test.exs
index 3a3e06b17..2096b2627 100644
--- a/test/pleroma/web/web_finger/web_finger_controller_test.exs
+++ b/test/pleroma/web/web_finger/web_finger_controller_test.exs
@@ -22,9 +22,29 @@ defmodule Pleroma.Web.WebFinger.WebFingerControllerTest do
|> get("/.well-known/host-meta")
assert response.status == 200
+ assert get_resp_header(response, "content-type") == ["application/xrd+xml; charset=utf-8"]
assert response.resp_body ==
- ~s(<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><Link rel="lrdd" template="#{Pleroma.Web.Endpoint.url()}/.well-known/webfinger?resource={uri}" type="application/xrd+xml" /></XRD>)
+ ~s[<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><Link rel="lrdd" template="#{Pleroma.Web.Endpoint.url()}/.well-known/webfinger?resource={uri}" type="application/xrd+xml" /></XRD>]
+ end
+
+ test "GET host-meta.json" do
+ response =
+ build_conn()
+ |> get("/.well-known/host-meta.json")
+
+ # RFC6415 (Web Host Metadata) says it MUST be application/json
+ assert get_resp_header(resp, "content-type") == ["application/json; charset=utf-8"]
+
+ assert response == json_response(resp, 200)
+
+ assert response["links"] == [
+ %{
+ "rel" => "lrdd",
+ "type" => "application/jrd+json",
+ "template" => "#{Pleroma.Web.Endpoint.url()}/.well-known/webfinger?resource={uri}"
+ }
+ ]
end
test "Webfinger JRD" do