feat: add .well-known/webfinger
This commit is contained in:
		
							parent
							
								
									507b7349ba
								
							
						
					
					
						commit
						9bde1a1aa7
					
				
					 4 changed files with 133 additions and 0 deletions
				
			
		|  | @ -11,6 +11,7 @@ import ( | |||
| 	"git.sleepycat.moe/sam/mercury/web/app" | ||||
| 	"git.sleepycat.moe/sam/mercury/web/auth" | ||||
| 	"git.sleepycat.moe/sam/mercury/web/frontend" | ||||
| 	"git.sleepycat.moe/sam/mercury/web/wellknown" | ||||
| 	"github.com/go-chi/chi/v5" | ||||
| ) | ||||
| 
 | ||||
|  | @ -25,6 +26,13 @@ func Routes(app *app.App) { | |||
| 		r.Post("/sign_up", auth.PostSignup) | ||||
| 	}) | ||||
| 
 | ||||
| 	// .well-known handlers | ||||
| 	app.Router.Route("/.well-known", func(r chi.Router) { | ||||
| 		wellknown := wellknown.New(app) | ||||
| 
 | ||||
| 		r.Get("/webfinger", api.WrapHandlerT(wellknown.WebFinger)) | ||||
| 	}) | ||||
| 
 | ||||
| 	// APIv1 handlers | ||||
| 	app.Router.Route("/api/v1", func(r chi.Router) { | ||||
| 		unauthedAccess := !app.AppConfig.Security.RestrictAPI | ||||
|  |  | |||
							
								
								
									
										13
									
								
								web/wellknown/module.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								web/wellknown/module.go
									
										
									
									
									
										Normal 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, | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										84
									
								
								web/wellknown/webfinger.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								web/wellknown/webfinger.go
									
										
									
									
									
										Normal 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 | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue