mirror of
https://git.pleroma.social/pleroma/pleroma.git
synced 2025-04-24 22:07:52 -04:00
WebFinger: Serve /.well-known/host-meta.json requests
This commit is contained in:
parent
44f272b9fa
commit
beddda8547
6 changed files with 107 additions and 10 deletions
1
changelog.d/host-meta-json.add
Normal file
1
changelog.d/host-meta-json.add
Normal file
|
@ -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.
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue