alpha
Login
or
Join now
willdot.net
/
distributed-pds
forked from
willdot.net/cocoon
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
A fork of the Cocoon PDS but being made more distributed.
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Overview
Issues
Pulls
Pipelines
allow localhost oauth clients (#90)
author
meri
committer
GitHub
date
3 weeks ago
(Jun 3, 2026, 9:38 AM -0700)
commit
b3c809a5
b3c809a544cd4bbf8b06f4fe325a629df060b7ea
parent
4bd740d8
4bd740d8a039fa15429f255f33963d571a8ba4a2
+99
-5
2 changed files
Expand all
Collapse all
Unified
Split
oauth
client
client.go
manager.go
+33
-3
oauth/client/client.go
Reviewed
···
1
1
package client
2
2
3
3
-
import "github.com/lestrrat-go/jwx/v2/jwk"
3
3
+
import (
4
4
+
"net/url"
5
5
+
6
6
+
"github.com/lestrrat-go/jwx/v2/jwk"
7
7
+
)
4
8
5
9
type Client struct {
6
6
-
Metadata *Metadata
7
7
-
JWKS jwk.Key
10
10
+
Metadata *Metadata
11
11
+
JWKS jwk.Key
12
12
+
IsLocalhostClient bool
13
13
+
}
14
14
+
15
15
+
func (c *Client) IsRedirectURIAllowed(requestedURI string) bool {
16
16
+
if c.IsLocalhostClient {
17
17
+
ru, err := url.Parse(requestedURI)
18
18
+
if err != nil || ru.Scheme != "http" || !isLoopbackHost(ru.Hostname()) {
19
19
+
return false
20
20
+
}
21
21
+
for _, registered := range c.Metadata.RedirectURIs {
22
22
+
reg, err := url.Parse(registered)
23
23
+
if err != nil {
24
24
+
continue
25
25
+
}
26
26
+
if reg.Hostname() == ru.Hostname() && reg.Path == ru.Path {
27
27
+
return true
28
28
+
}
29
29
+
}
30
30
+
return false
31
31
+
}
32
32
+
for _, uri := range c.Metadata.RedirectURIs {
33
33
+
if uri == requestedURI {
34
34
+
return true
35
35
+
}
36
36
+
}
37
37
+
return false
8
38
}
+66
-2
oauth/client/manager.go
Reviewed
···
85
85
}
86
86
87
87
return &Client{
88
88
-
Metadata: metadata,
89
89
-
JWKS: jwks,
88
88
+
Metadata: metadata,
89
89
+
JWKS: jwks,
90
90
+
IsLocalhostClient: isLocalhostClientID(clientId),
90
91
}, nil
91
92
}
92
93
93
94
func (cm *Manager) getClientMetadata(ctx context.Context, clientId string) (*Metadata, error) {
95
95
+
if isLocalhostClientID(clientId) {
96
96
+
return buildLocalhostVirtualMetadata(clientId)
97
97
+
}
98
98
+
94
99
cached, ok := cm.metadataCache.Get(clientId)
95
100
if !ok {
96
101
req, err := http.NewRequestWithContext(ctx, "GET", clientId, nil)
···
393
398
}
394
399
395
400
return &metadata, nil
401
401
+
}
402
402
+
403
403
+
func isLocalhostClientID(clientId string) bool {
404
404
+
u, err := url.Parse(clientId)
405
405
+
if err != nil {
406
406
+
return false
407
407
+
}
408
408
+
return u.Scheme == "http" &&
409
409
+
u.Hostname() == "localhost" &&
410
410
+
u.Port() == "" &&
411
411
+
(u.Path == "" || u.Path == "/")
412
412
+
}
413
413
+
414
414
+
func buildLocalhostVirtualMetadata(clientId string) (*Metadata, error) {
415
415
+
u, err := url.Parse(clientId)
416
416
+
if err != nil {
417
417
+
return nil, fmt.Errorf("error parsing localhost client_id: %w", err)
418
418
+
}
419
419
+
420
420
+
q := u.Query()
421
421
+
422
422
+
redirectURIs := q["redirect_uri"]
423
423
+
if len(redirectURIs) == 0 {
424
424
+
redirectURIs = []string{"http://127.0.0.1/", "http://[::1]/"}
425
425
+
}
426
426
+
427
427
+
for _, ruri := range redirectURIs {
428
428
+
ru, err := url.Parse(ruri)
429
429
+
if err != nil {
430
430
+
return nil, fmt.Errorf("invalid redirect_uri %q: %w", ruri, err)
431
431
+
}
432
432
+
if ru.Scheme != "http" || !isLoopbackHost(ru.Hostname()) {
433
433
+
return nil, fmt.Errorf("localhost client redirect_uri must use a loopback address, got %q", ruri)
434
434
+
}
435
435
+
}
436
436
+
437
437
+
scope := q.Get("scope")
438
438
+
if scope == "" {
439
439
+
scope = "atproto"
440
440
+
} else if !slices.Contains(strings.Split(scope, " "), "atproto") {
441
441
+
scope = "atproto " + scope
442
442
+
}
443
443
+
444
444
+
return &Metadata{
445
445
+
ClientID: clientId,
446
446
+
ClientName: "Development client",
447
447
+
ClientURI: "http://localhost",
448
448
+
RedirectURIs: redirectURIs,
449
449
+
GrantTypes: []string{"authorization_code", "refresh_token"},
450
450
+
ResponseTypes: []string{"code"},
451
451
+
ApplicationType: "native",
452
452
+
DpopBoundAccessTokens: true,
453
453
+
Scope: scope,
454
454
+
TokenEndpointAuthMethod: "none",
455
455
+
}, nil
456
456
+
}
457
457
+
458
458
+
func isLoopbackHost(hostname string) bool {
459
459
+
return hostname == "localhost" || hostname == "127.0.0.1" || hostname == "::1"
396
460
}
397
461
398
462
func isLocalHostname(hostname string) bool {