From c86b2e192a53b0e01402999dbbb0813a8d72e7ab Mon Sep 17 00:00:00 2001 From: Laura Hausmann Date: Wed, 14 Aug 2024 01:05:38 +0200 Subject: [PATCH] [backend/federation] Improve WebFinger host-meta handling --- .../Federation/Schemas/HostMetaXmlResponse.cs | 2 +- .../Federation/WellKnownController.cs | 2 +- .../Federation/WebFinger/WebFingerService.cs | 35 ++++++++++--------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/Iceshrimp.Backend/Controllers/Federation/Schemas/HostMetaXmlResponse.cs b/Iceshrimp.Backend/Controllers/Federation/Schemas/HostMetaXmlResponse.cs index 652e4b29..70b2d997 100644 --- a/Iceshrimp.Backend/Controllers/Federation/Schemas/HostMetaXmlResponse.cs +++ b/Iceshrimp.Backend/Controllers/Federation/Schemas/HostMetaXmlResponse.cs @@ -16,7 +16,7 @@ public class HostMetaXmlResponseLink() { [XmlAttribute("rel")] public string Rel = "lrdd"; [XmlAttribute("template")] public required string Template; - [XmlAttribute("type")] public string Type = "application/xrd+xml"; + [XmlAttribute("type")] public string Type = "application/jrd+json"; [SetsRequiredMembers] public HostMetaXmlResponseLink(string webDomain) : this() => diff --git a/Iceshrimp.Backend/Controllers/Federation/WellKnownController.cs b/Iceshrimp.Backend/Controllers/Federation/WellKnownController.cs index 9ab7e730..7495aa11 100644 --- a/Iceshrimp.Backend/Controllers/Federation/WellKnownController.cs +++ b/Iceshrimp.Backend/Controllers/Federation/WellKnownController.cs @@ -24,7 +24,7 @@ namespace Iceshrimp.Backend.Controllers.Federation; public class WellKnownController(IOptions config, DatabaseContext db) : ControllerBase { [HttpGet("webfinger")] - [Produces(MediaTypeNames.Application.Json)] + [Produces(MediaTypeNames.Application.Json, "application/jrd+json")] [ProducesResults(HttpStatusCode.OK)] [ProducesErrors(HttpStatusCode.NotFound)] public async Task WebFinger([FromQuery] string resource) diff --git a/Iceshrimp.Backend/Core/Federation/WebFinger/WebFingerService.cs b/Iceshrimp.Backend/Core/Federation/WebFinger/WebFingerService.cs index 98f9aa39..f675c8e6 100644 --- a/Iceshrimp.Backend/Core/Federation/WebFinger/WebFingerService.cs +++ b/Iceshrimp.Backend/Core/Federation/WebFinger/WebFingerService.cs @@ -1,6 +1,7 @@ +using System.Collections.Immutable; using System.Net; using System.Text.Encodings.Web; -using System.Xml; +using System.Xml.Linq; using Iceshrimp.Backend.Controllers.Federation.Schemas; using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Middleware; @@ -28,6 +29,11 @@ public class WebFingerService( IOptions config ) { + private static readonly ImmutableArray Accept = + [ + "application/jrd+json", "application/json", "application/xrd+xml", "application/xml" + ]; + public async Task ResolveAsync(string query) { (query, var proto, var domain) = ParseQuery(query); @@ -39,7 +45,7 @@ public class WebFingerService( using var cts = CancellationTokenSource.CreateLinkedTokenSource(appLifetime.ApplicationStopping); cts.CancelAfter(TimeSpan.FromSeconds(10)); - var req = httpRqSvc.Get(webFingerUrl, ["application/jrd+json", "application/json"]); + var req = httpRqSvc.Get(webFingerUrl, Accept); var res = await client.SendAsync(req, cts.Token); if (res.StatusCode == HttpStatusCode.Gone) @@ -96,6 +102,7 @@ public class WebFingerService( return template.Replace("{uri}", encoded); } + // Technically, we should be checking for rel=lrdd *and* type=application/jrd+json, but nearly all implementations break this, so we can't. private async Task GetWebFingerTemplateFromHostMetaXmlAsync(string proto, string domain) { try @@ -105,19 +112,12 @@ public class WebFingerService( HttpCompletionOption.ResponseHeadersRead); await using var stream = await res.Content.ReadAsStreamAsync(); - var xml = new XmlDocument(); - xml.Load(stream); - - var section = xml["XRD"]?.GetElementsByTagName("Link"); - if (section == null) return null; - - //TODO: implement https://stackoverflow.com/a/37322614/18402176 instead - - for (var i = 0; i < section.Count; i++) - if (section[i]?.Attributes?["rel"]?.InnerText == "lrdd") - return section[i]?.Attributes?["template"]?.InnerText; - - return null; + return XElement.Load(stream) + .Descendants(XName.Get("Link", "http://docs.oasis-open.org/ns/xri/xrd-1.0")) + //.Where(p => Accept.Contains(p.Attribute("type"))?.Value ?? "")) + .FirstOrDefault(p => p.Attribute("rel")?.Value == "lrdd") + ?.Attribute("template") + ?.Value; } catch { @@ -125,6 +125,7 @@ public class WebFingerService( } } + // See above comment as for why jrd+json is commented out. private async Task GetWebFingerTemplateFromHostMetaJsonAsync(string proto, string domain) { try @@ -137,7 +138,7 @@ public class WebFingerService( var result = deserialized?.Links?.FirstOrDefault(p => p is { Rel: "lrdd", - Type: "application/jrd+json", + //Type: "application/jrd+json", Template: not null }); @@ -159,7 +160,7 @@ public class WebFingerService( var result = deserialized?.Links?.FirstOrDefault(p => p is { Rel: "lrdd", - Type: "application/jrd+json", + //Type: "application/jrd+json", Template: not null });