feat: add .well-known/webfinger

This commit is contained in:
sam 2023-10-16 15:45:43 +02:00
parent 507b7349ba
commit 9bde1a1aa7
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
4 changed files with 133 additions and 0 deletions

13
web/wellknown/module.go Normal file
View file

@ -0,0 +1,13 @@
package wellknown
import "git.sleepycat.moe/sam/mercury/web/app"
type App struct {
*app.App
}
func New(app *app.App) *App {
return &App{
App: app,
}
}

View file

@ -0,0 +1,84 @@
package wellknown
import (
"fmt"
"net/http"
"net/url"
"strings"
"git.sleepycat.moe/sam/mercury/internal/database/sql"
"git.sleepycat.moe/sam/mercury/web/api"
"github.com/rs/zerolog/log"
)
func (app *App) WebFinger(w http.ResponseWriter, r *http.Request) (any, error) {
ctx := r.Context()
validDomains := app.AppConfig.Web.WebFingerDomains()
resource, err := url.QueryUnescape(r.FormValue("resource"))
if err != nil {
return nil, api.Error{Code: api.ErrBadRequest}
}
if !strings.HasPrefix(resource, "acct:") {
return nil, api.Error{Code: api.ErrBadRequest, Details: "WebFinger only supports `acct:` queries"}
}
resource = strings.TrimPrefix(resource, "acct:")
username, domain, ok := strings.Cut(resource, "@")
if !ok {
return nil, api.Error{Code: api.ErrBadRequest}
}
if !anyMatches(validDomains, domain) {
return nil, api.Error{Code: api.ErrBadRequest, Details: "Not a local user on this instance"}
}
blog, err := app.Blog().ByName(ctx, username, "")
if err != nil {
if err == sql.ErrNotFound {
return nil, api.Error{Code: api.ErrNotFound}
}
log.Err(err).Str("username", username).Msg("looking up user for webfinger request")
return nil, err
}
webFingerHref := fmt.Sprintf("%s/@%s", app.AppConfig.Web.Domain, blog.Name)
return WebFinger{
Subject: fmt.Sprintf("acct:%s@%s", blog.Name, validDomains[0]),
Aliases: []string{webFingerHref},
Links: []WebFingerLink{
{
Rel: "http://webfinger.net/rel/profile-page",
Type: "text/html",
Href: webFingerHref,
},
{
Rel: "self",
Type: "application/activity+json",
Href: webFingerHref,
},
},
}, nil
}
type WebFinger struct {
Subject string `json:"subject"`
Aliases []string `json:"aliases"`
Links []WebFingerLink `json:"links"`
}
type WebFingerLink struct {
Rel string `json:"rel"`
Type string `json:"type"`
Href string `json:"href"`
}
func anyMatches[T comparable](slice []T, t T) bool {
for _, entry := range slice {
if entry == t {
return true
}
}
return false
}