Skip to content

Commit c825d81

Browse files
authored
feat: add support for webfinger (#941)
1 parent f404c2e commit c825d81

1 file changed

Lines changed: 75 additions & 0 deletions

File tree

internal/controller/well_known_controller.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,27 @@ package controller
33
import (
44
"fmt"
55
"net/http"
6+
"net/url"
7+
"slices"
8+
"strings"
69

710
"github.com/gin-gonic/gin"
811
"github.com/tinyauthapp/tinyauth/internal/service"
912
"go.uber.org/dig"
1013
)
1114

15+
const OpenIDConnectRel = "http://openid.net/specs/connect/1.0/issuer"
16+
17+
type WebfingerResponseLink struct {
18+
Rel string `json:"rel,omitempty"`
19+
Href string `json:"href"`
20+
}
21+
22+
type WebfingerResponse struct {
23+
Subject string `json:"subject"`
24+
Links []WebfingerResponseLink `json:"links"`
25+
}
26+
1227
type OpenIDConnectConfiguration struct {
1328
Issuer string `json:"issuer"`
1429
AuthorizationEndpoint string `json:"authorization_endpoint"`
@@ -45,6 +60,7 @@ func NewWellKnownController(i WellKnownControllerInput) *WellKnownController {
4560

4661
i.RouterGroup.GET("/.well-known/openid-configuration", controller.OpenIDConnectConfiguration)
4762
i.RouterGroup.GET("/.well-known/jwks.json", controller.JWKS)
63+
i.RouterGroup.GET("/.well-known/webfinger", controller.WebFinger)
4864

4965
return controller
5066
}
@@ -105,3 +121,62 @@ func (controller *WellKnownController) JWKS(c *gin.Context) {
105121

106122
c.Status(http.StatusOK)
107123
}
124+
125+
func (controller *WellKnownController) WebFinger(c *gin.Context) {
126+
c.Header("Content-Type", "application/jrd+json")
127+
c.Header("Access-Control-Allow-Origin", "*")
128+
129+
resource := c.Query("resource")
130+
131+
if !controller.validateWebFingerResource(resource) {
132+
c.JSON(400, gin.H{
133+
"status": 400,
134+
"message": "invalid resource",
135+
})
136+
return
137+
}
138+
139+
res := WebfingerResponse{
140+
Subject: resource,
141+
Links: []WebfingerResponseLink{},
142+
}
143+
144+
rel := c.Request.URL.Query()["rel"]
145+
146+
if controller.oidc != nil && (len(rel) == 0 || slices.Contains(rel, OpenIDConnectRel)) {
147+
res.Links = append(res.Links, WebfingerResponseLink{Rel: OpenIDConnectRel, Href: controller.oidc.GetIssuer()})
148+
}
149+
150+
c.JSON(200, res)
151+
}
152+
153+
func (controller *WellKnownController) validateWebFingerResource(resource string) bool {
154+
prefix, suffix, found := strings.Cut(resource, ":")
155+
156+
if !found {
157+
return false
158+
}
159+
160+
switch prefix {
161+
case "acct":
162+
if strings.Count(suffix, "@") != 1 {
163+
return false
164+
}
165+
username, domain, found := strings.Cut(suffix, "@")
166+
if !found || username == "" || domain == "" {
167+
return false
168+
}
169+
case "https", "http":
170+
u, err := url.Parse(resource)
171+
if err != nil {
172+
return false
173+
}
174+
if u.Host == "" {
175+
return false
176+
}
177+
default:
178+
return false
179+
}
180+
181+
return true
182+
}

0 commit comments

Comments
 (0)