@@ -3,12 +3,27 @@ package controller
33import (
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+
1227type 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