+4
-4
oauth/dpop/manager.go
+4
-4
oauth/dpop/manager.go
···
75
75
}
76
76
77
77
proof := extractProof(headers)
78
-
79
78
if proof == "" {
80
79
return nil, nil
81
80
}
···
197
196
198
197
nonce, _ := claims["nonce"].(string)
199
198
if nonce == "" {
200
-
// WARN: this _must_ be `use_dpop_nonce` for clients know they should make another request
199
+
// reference impl checks if self.nonce is not null before returning an error, but we always have a
200
+
// nonce so we do not bother checking
201
201
return nil, ErrUseDpopNonce
202
202
}
203
203
204
204
if nonce != "" && !dm.nonce.Check(nonce) {
205
-
// WARN: this _must_ be `use_dpop_nonce` so that clients will fetch a new nonce
205
+
// dpop nonce mismatch
206
206
return nil, ErrUseDpopNonce
207
207
}
208
208
···
237
237
}
238
238
239
239
func extractProof(headers http.Header) string {
240
-
dpopHeaders := headers["Dpop"]
240
+
dpopHeaders := headers.Values("dpop")
241
241
switch len(dpopHeaders) {
242
242
case 0:
243
243
return ""
+3
-3
oauth/provider/client_auth.go
+3
-3
oauth/provider/client_auth.go
···
19
19
}
20
20
21
21
type AuthenticateClientRequestBase struct {
22
-
ClientID string `form:"client_id" json:"client_id" validate:"required"`
23
-
ClientAssertionType *string `form:"client_assertion_type" json:"client_assertion_type,omitempty"`
24
-
ClientAssertion *string `form:"client_assertion" json:"client_assertion,omitempty"`
22
+
ClientID string `form:"client_id" json:"client_id" query:"client_id" validate:"required"`
23
+
ClientAssertionType *string `form:"client_assertion_type" json:"client_assertion_type,omitempty" query:"client_assertion_type"`
24
+
ClientAssertion *string `form:"client_assertion" json:"client_assertion,omitempty" query:"client_assertion"`
25
25
}
26
26
27
27
func (p *Provider) AuthenticateClient(ctx context.Context, req AuthenticateClientRequestBase, proof *dpop.Proof, opts *AuthenticateClientOptions) (*client.Client, *ClientAuth, error) {
+9
-8
oauth/provider/models.go
+9
-8
oauth/provider/models.go
···
32
32
33
33
type ParRequest struct {
34
34
AuthenticateClientRequestBase
35
-
ResponseType string `form:"response_type" json:"response_type" validate:"required"`
36
-
CodeChallenge *string `form:"code_challenge" json:"code_challenge" validate:"required"`
37
-
CodeChallengeMethod string `form:"code_challenge_method" json:"code_challenge_method" validate:"required"`
38
-
State string `form:"state" json:"state" validate:"required"`
39
-
RedirectURI string `form:"redirect_uri" json:"redirect_uri" validate:"required"`
40
-
Scope string `form:"scope" json:"scope" validate:"required"`
41
-
LoginHint *string `form:"login_hint" json:"login_hint,omitempty"`
42
-
DpopJkt *string `form:"dpop_jkt" json:"dpop_jkt,omitempty"`
35
+
ResponseType string `form:"response_type" json:"response_type" query:"response_type" validate:"required"`
36
+
CodeChallenge *string `form:"code_challenge" json:"code_challenge" query:"code_challenge" validate:"required"`
37
+
CodeChallengeMethod string `form:"code_challenge_method" json:"code_challenge_method" query:"code_challenge_method" validate:"required"`
38
+
State string `form:"state" json:"state" query:"state" validate:"required"`
39
+
RedirectURI string `form:"redirect_uri" json:"redirect_uri" query:"redirect_uri" validate:"required"`
40
+
Scope string `form:"scope" json:"scope" query:"scope" validate:"required"`
41
+
LoginHint *string `form:"login_hint" query:"login_hint" json:"login_hint,omitempty"`
42
+
DpopJkt *string `form:"dpop_jkt" query:"dpop_jkt" json:"dpop_jkt,omitempty"`
43
+
ResponseMode *string `form:"response_mode" json:"response_mode,omitempty" query:"response_mode"`
43
44
}
44
45
45
46
func (opr *ParRequest) Scan(value any) error {
+1
server/handle_oauth_par.go
+1
server/handle_oauth_par.go
···
42
42
e.Response().Header().Set("DPoP-Nonce", nonce)
43
43
e.Response().Header().Add("access-control-expose-headers", "DPoP-Nonce")
44
44
}
45
+
logger.Error("nonce error: use_dpop_nonce", "headers", e.Request().Header)
45
46
return e.JSON(400, map[string]string{
46
47
"error": "use_dpop_nonce",
47
48
})
+28
-8
server/handle_sync_subscribe_repos.go
+28
-8
server/handle_sync_subscribe_repos.go
···
12
12
)
13
13
14
14
func (s *Server) handleSyncSubscribeRepos(e echo.Context) error {
15
-
ctx := e.Request().Context()
15
+
ctx, cancel := context.WithCancel(e.Request().Context())
16
+
defer cancel()
17
+
16
18
logger := s.logger.With("component", "subscribe-repos-websocket")
17
19
18
20
conn, err := websocket.Upgrade(e.Response().Writer, e.Request(), e.Response().Header(), 1<<10, 1<<10)
···
30
32
metrics.RelaysConnected.WithLabelValues(ident).Dec()
31
33
}()
32
34
33
-
evts, cancel, err := s.evtman.Subscribe(ctx, ident, func(evt *events.XRPCStreamEvent) bool {
35
+
evts, evtManCancel, err := s.evtman.Subscribe(ctx, ident, func(evt *events.XRPCStreamEvent) bool {
34
36
return true
35
37
}, nil)
36
38
if err != nil {
37
39
return err
38
40
}
39
-
defer cancel()
41
+
defer evtManCancel()
42
+
43
+
// drop the connection whenever a subscriber disconnects from the socket, we should get errors
44
+
go func() {
45
+
for {
46
+
select {
47
+
case <-ctx.Done():
48
+
return
49
+
default:
50
+
if _, _, err := conn.ReadMessage(); err != nil {
51
+
logger.Warn("websocket error", "err", err)
52
+
cancel()
53
+
return
54
+
}
55
+
}
56
+
}
57
+
}()
40
58
41
59
header := events.EventHeader{Op: events.EvtKindMessage}
42
60
for evt := range evts {
···
97
115
98
116
// we should tell the relay to request a new crawl at this point if we got disconnected
99
117
// use a new context since the old one might be cancelled at this point
100
-
ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second)
101
-
defer cancel()
102
-
if err := s.requestCrawl(ctx); err != nil {
103
-
logger.Error("error requesting crawls", "err", err)
104
-
}
118
+
go func() {
119
+
retryCtx, retryCancel := context.WithTimeout(context.Background(), 10*time.Second)
120
+
defer retryCancel()
121
+
if err := s.requestCrawl(retryCtx); err != nil {
122
+
logger.Error("error requesting crawls", "err", err)
123
+
}
124
+
}()
105
125
106
126
return nil
107
127
}