+44
-28
oauth/client/manager.go
+44
-28
oauth/client/manager.go
···
22
cli *http.Client
23
logger *slog.Logger
24
jwksCache cache.Cache[string, jwk.Key]
25
-
metadataCache cache.Cache[string, Metadata]
26
}
27
28
type ManagerArgs struct {
···
40
}
41
42
jwksCache := cache.NewCache[string, jwk.Key]().WithLRU().WithMaxKeys(500).WithTTL(5 * time.Minute)
43
-
metadataCache := cache.NewCache[string, Metadata]().WithLRU().WithMaxKeys(500).WithTTL(5 * time.Minute)
44
45
return &Manager{
46
cli: args.Cli,
···
57
}
58
59
var jwks jwk.Key
60
-
if metadata.JWKS != nil && len(metadata.JWKS.Keys) > 0 {
61
-
// TODO: this is kinda bad but whatever for now. there could obviously be more than one jwk, and we need to
62
-
// make sure we use the right one
63
-
b, err := json.Marshal(metadata.JWKS.Keys[0])
64
-
if err != nil {
65
-
return nil, err
66
-
}
67
68
-
k, err := helpers.ParseJWKFromBytes(b)
69
-
if err != nil {
70
-
return nil, err
71
-
}
72
73
-
jwks = k
74
-
} else if metadata.JWKSURI != nil {
75
-
maybeJwks, err := cm.getClientJwks(ctx, clientId, *metadata.JWKSURI)
76
-
if err != nil {
77
-
return nil, err
78
-
}
79
80
-
jwks = maybeJwks
81
-
} else {
82
-
return nil, fmt.Errorf("no valid jwks found in oauth client metadata")
83
}
84
85
return &Client{
···
89
}
90
91
func (cm *Manager) getClientMetadata(ctx context.Context, clientId string) (*Metadata, error) {
92
-
metadataCached, ok := cm.metadataCache.Get(clientId)
93
if !ok {
94
req, err := http.NewRequestWithContext(ctx, "GET", clientId, nil)
95
if err != nil {
···
117
return nil, err
118
}
119
120
return validated, nil
121
} else {
122
-
return &metadataCached, nil
123
}
124
}
125
···
204
return nil, fmt.Errorf("error unmarshaling metadata: %w", err)
205
}
206
207
u, err := url.Parse(metadata.ClientURI)
208
if err != nil {
209
return nil, fmt.Errorf("unable to parse client uri: %w", err)
210
}
211
212
if isLocalHostname(u.Hostname()) {
213
-
return nil, errors.New("`client_uri` hostname is invalid")
214
}
215
216
if metadata.Scope == "" {
···
349
if u.Scheme != "http" {
350
return nil, fmt.Errorf("loopback redirect uri %s must use http", ruri)
351
}
352
-
353
-
break
354
case u.Scheme == "http":
355
return nil, errors.New("only loopbvack redirect uris are allowed to use the `http` scheme")
356
case u.Scheme == "https":
357
if isLocalHostname(u.Hostname()) {
358
return nil, fmt.Errorf("redirect uri %s's domain must not be a local hostname", ruri)
359
}
360
-
break
361
case strings.Contains(u.Scheme, "."):
362
if metadata.ApplicationType != "native" {
363
return nil, errors.New("private-use uri scheme redirect uris are only allowed for native apps")
···
22
cli *http.Client
23
logger *slog.Logger
24
jwksCache cache.Cache[string, jwk.Key]
25
+
metadataCache cache.Cache[string, *Metadata]
26
}
27
28
type ManagerArgs struct {
···
40
}
41
42
jwksCache := cache.NewCache[string, jwk.Key]().WithLRU().WithMaxKeys(500).WithTTL(5 * time.Minute)
43
+
metadataCache := cache.NewCache[string, *Metadata]().WithLRU().WithMaxKeys(500).WithTTL(5 * time.Minute)
44
45
return &Manager{
46
cli: args.Cli,
···
57
}
58
59
var jwks jwk.Key
60
+
if metadata.TokenEndpointAuthMethod == "private_key_jwt" {
61
+
if metadata.JWKS != nil && len(metadata.JWKS.Keys) > 0 {
62
+
// TODO: this is kinda bad but whatever for now. there could obviously be more than one jwk, and we need to
63
+
// make sure we use the right one
64
+
b, err := json.Marshal(metadata.JWKS.Keys[0])
65
+
if err != nil {
66
+
return nil, err
67
+
}
68
69
+
k, err := helpers.ParseJWKFromBytes(b)
70
+
if err != nil {
71
+
return nil, err
72
+
}
73
74
+
jwks = k
75
+
} else if metadata.JWKS != nil {
76
+
} else if metadata.JWKSURI != nil {
77
+
maybeJwks, err := cm.getClientJwks(ctx, clientId, *metadata.JWKSURI)
78
+
if err != nil {
79
+
return nil, err
80
+
}
81
82
+
jwks = maybeJwks
83
+
} else {
84
+
return nil, fmt.Errorf("no valid jwks found in oauth client metadata")
85
+
}
86
}
87
88
return &Client{
···
92
}
93
94
func (cm *Manager) getClientMetadata(ctx context.Context, clientId string) (*Metadata, error) {
95
+
cached, ok := cm.metadataCache.Get(clientId)
96
if !ok {
97
req, err := http.NewRequestWithContext(ctx, "GET", clientId, nil)
98
if err != nil {
···
120
return nil, err
121
}
122
123
+
cm.metadataCache.Set(clientId, validated, 10*time.Minute)
124
+
125
return validated, nil
126
} else {
127
+
return cached, nil
128
}
129
}
130
···
209
return nil, fmt.Errorf("error unmarshaling metadata: %w", err)
210
}
211
212
+
if metadata.ClientURI == "" {
213
+
u, err := url.Parse(metadata.ClientID)
214
+
if err != nil {
215
+
return nil, fmt.Errorf("unable to parse client id: %w", err)
216
+
}
217
+
u.RawPath = ""
218
+
u.RawQuery = ""
219
+
metadata.ClientURI = u.String()
220
+
}
221
+
222
u, err := url.Parse(metadata.ClientURI)
223
if err != nil {
224
return nil, fmt.Errorf("unable to parse client uri: %w", err)
225
}
226
227
+
if metadata.ClientName == "" {
228
+
metadata.ClientName = metadata.ClientURI
229
+
}
230
+
231
if isLocalHostname(u.Hostname()) {
232
+
return nil, fmt.Errorf("`client_uri` hostname is invalid: %s", u.Hostname())
233
}
234
235
if metadata.Scope == "" {
···
368
if u.Scheme != "http" {
369
return nil, fmt.Errorf("loopback redirect uri %s must use http", ruri)
370
}
371
case u.Scheme == "http":
372
return nil, errors.New("only loopbvack redirect uris are allowed to use the `http` scheme")
373
case u.Scheme == "https":
374
if isLocalHostname(u.Hostname()) {
375
return nil, fmt.Errorf("redirect uri %s's domain must not be a local hostname", ruri)
376
}
377
case strings.Contains(u.Scheme, "."):
378
if metadata.ApplicationType != "native" {
379
return nil, errors.New("private-use uri scheme redirect uris are only allowed for native apps")