FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Merge pull request #201 from IDFoundry/refactor/duplicate-string-lite… · IDFoundry/FAPIgo@c7bf79c · GitHub

Commit c7bf79c

Browse files
authored
Merge pull request #201 from IDFoundry/refactor/duplicate-string-literals
2 parents b43aae8 + e1c242c commit c7bf79c

12 files changed

Lines changed: 456 additions & 64 deletions

File tree

‎client/backchannel.go‎

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ const cibaGrantType = "urn:openid:params:grant-type:ciba"
3131
// it should assume a default of 5 seconds").
3232
const defaultBackchannelAuthenticationPollInterval = 5 * time.Second
3333

34+
// errBackchannelAuthenticationRequestFailed is the shared newError
35+
// description sendBackchannelAuthenticationRequest's own retry sequence
36+
// uses at every call site — the same transport-level failure
37+
// regardless of which attempt hit it.
38+
const errBackchannelAuthenticationRequestFailed = "backchannel authentication request failed"
39+
3440
// BeginBackchannelAuthenticationRequest is the input to
3541
// Client.BeginBackchannelAuthentication. Exactly one of LoginHint,
3642
// LoginHintToken or IDTokenHint must be set — validated locally before
@@ -263,7 +269,7 @@ func (c *Client) sendBackchannelAuthenticationRequest(ctx context.Context, dpopS
263269
if c.cfg.SenderConstrain == storage.SenderConstrainMTLS {
264270
body, status, _, err := c.postForm(ctx, endpointURL.String(), form, nil)
265271
if err != nil {
266-
return nil, newError(ErrorInternal, "backchannel authentication request failed", err)
272+
return nil, newError(ErrorInternal, errBackchannelAuthenticationRequestFailed, err)
267273
}
268274
if status != http.StatusOK {
269275
return nil, parErrorFromResponse(body)
@@ -272,9 +278,9 @@ func (c *Client) sendBackchannelAuthenticationRequest(ctx context.Context, dpopS
272278
}
273279
body, status, header, err := c.postBackchannelAuthenticationRequestWithDPoP(ctx, dpopSigner, endpointURL, form, c.cachedDPoPNonce(ctx, asNonceScope))
274280
if err != nil {
275-
return nil, newError(ErrorInternal, "backchannel authentication request failed", err)
281+
return nil, newError(ErrorInternal, errBackchannelAuthenticationRequestFailed, err)
276282
}
277-
nextNonce := header.Get("DPoP-Nonce")
283+
nextNonce := header.Get(dpopNonceHeader)
278284
c.cacheDPoPNonce(ctx, asNonceScope, nextNonce)
279285
if status == http.StatusOK {
280286
return body, nil
@@ -288,9 +294,9 @@ func (c *Client) sendBackchannelAuthenticationRequest(ctx context.Context, dpopS
288294
}
289295
body, status, header, err = c.postBackchannelAuthenticationRequestWithDPoP(ctx, dpopSigner, endpointURL, retryForm, nextNonce)
290296
if err != nil {
291-
return nil, newError(ErrorInternal, "backchannel authentication request failed", err)
297+
return nil, newError(ErrorInternal, errBackchannelAuthenticationRequestFailed, err)
292298
}
293-
c.cacheDPoPNonce(ctx, asNonceScope, header.Get("DPoP-Nonce"))
299+
c.cacheDPoPNonce(ctx, asNonceScope, header.Get(dpopNonceHeader))
294300
if status != http.StatusOK {
295301
return nil, parErrorFromResponse(body)
296302
}
@@ -470,15 +476,15 @@ func (c *Client) pollBackchannelAuthenticationOnce(ctx context.Context, dpopSign
470476
if c.cfg.SenderConstrain == storage.SenderConstrainMTLS {
471477
body, status, _, err := c.postForm(ctx, tokenURL.String(), form, nil)
472478
if err != nil {
473-
return nil, 0, newError(ErrorInternal, "token request failed", err)
479+
return nil, 0, newError(ErrorInternal, errTokenRequestFailed, err)
474480
}
475481
return body, status, nil
476482
}
477483
body, status, header, err := c.postTokenRequestWithDPoP(ctx, dpopSigner, tokenURL, form, c.cachedDPoPNonce(ctx, asNonceScope))
478484
if err != nil {
479-
return nil, 0, newError(ErrorInternal, "token request failed", err)
485+
return nil, 0, newError(ErrorInternal, errTokenRequestFailed, err)
480486
}
481-
nextNonce := header.Get("DPoP-Nonce")
487+
nextNonce := header.Get(dpopNonceHeader)
482488
c.cacheDPoPNonce(ctx, asNonceScope, nextNonce)
483489
if status == http.StatusOK || nextNonce == "" || !isDPoPNonceError(body) {
484490
return body, status, nil
@@ -489,9 +495,9 @@ func (c *Client) pollBackchannelAuthenticationOnce(ctx context.Context, dpopSign
489495
}
490496
body, status, header, err = c.postTokenRequestWithDPoP(ctx, dpopSigner, tokenURL, retryForm, nextNonce)
491497
if err != nil {
492-
return nil, 0, newError(ErrorInternal, "token request failed", err)
498+
return nil, 0, newError(ErrorInternal, errTokenRequestFailed, err)
493499
}
494-
c.cacheDPoPNonce(ctx, asNonceScope, header.Get("DPoP-Nonce"))
500+
c.cacheDPoPNonce(ctx, asNonceScope, header.Get(dpopNonceHeader))
495501
return body, status, nil
496502
}
497503

‎client/begin_authorization.go‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ const (
5757
responseModeJARM = "jarm"
5858
)
5959

60+
// errPushedAuthorizationRequestFailed is the shared newError
61+
// description sendPushedAuthorizationRequest's own retry sequence uses
62+
// at every call site — the same transport-level failure regardless of
63+
// which attempt hit it.
64+
const errPushedAuthorizationRequestFailed = "pushed authorization request failed"
65+
6066
// BeginAuthorization starts a new authorization attempt: it generates
6167
// state, nonce and a PKCE verifier, builds and signs a request object
6268
// when Config.Profile requires one, authenticates to and calls the
@@ -185,7 +191,7 @@ func (c *Client) pushAuthorizationRequestPlain(ctx context.Context, params map[s
185191
}
186192
body, status, _, err := c.postForm(ctx, c.cfg.Endpoints.PushedAuthorizationRequest.String(), par.EncodeForm(formParams), nil)
187193
if err != nil {
188-
return nil, newError(ErrorInternal, "pushed authorization request failed", err)
194+
return nil, newError(ErrorInternal, errPushedAuthorizationRequestFailed, err)
189195
}
190196
if status != http.StatusCreated && status != http.StatusOK {
191197
return nil, parErrorFromResponse(body)
@@ -212,7 +218,7 @@ func (c *Client) pushAuthorizationRequestWithJKT(ctx context.Context, params map
212218
}
213219
body, status, _, err := c.postForm(ctx, c.cfg.Endpoints.PushedAuthorizationRequest.String(), par.EncodeForm(formParams), nil)
214220
if err != nil {
215-
return nil, newError(ErrorInternal, "pushed authorization request failed", err)
221+
return nil, newError(ErrorInternal, errPushedAuthorizationRequestFailed, err)
216222
}
217223
if status != http.StatusCreated && status != http.StatusOK {
218224
return nil, parErrorFromResponse(body)
@@ -252,9 +258,9 @@ func (c *Client) pushAuthorizationRequestWithDPoPProof(ctx context.Context, para
252258

253259
body, status, header, err := c.postParRequestWithDPoP(ctx, dpopSigner, &parURL, form, c.cachedDPoPNonce(ctx, asNonceScope))
254260
if err != nil {
255-
return nil, newError(ErrorInternal, "pushed authorization request failed", err)
261+
return nil, newError(ErrorInternal, errPushedAuthorizationRequestFailed, err)
256262
}
257-
nextNonce := header.Get("DPoP-Nonce")
263+
nextNonce := header.Get(dpopNonceHeader)
258264
c.cacheDPoPNonce(ctx, asNonceScope, nextNonce)
259265
if status == http.StatusCreated || status == http.StatusOK {
260266
return body, nil
@@ -269,9 +275,9 @@ func (c *Client) pushAuthorizationRequestWithDPoPProof(ctx context.Context, para
269275
}
270276
body, status, header, err = c.postParRequestWithDPoP(ctx, dpopSigner, &parURL, retryForm, nextNonce)
271277
if err != nil {
272-
return nil, newError(ErrorInternal, "pushed authorization request failed", err)
278+
return nil, newError(ErrorInternal, errPushedAuthorizationRequestFailed, err)
273279
}
274-
c.cacheDPoPNonce(ctx, asNonceScope, header.Get("DPoP-Nonce"))
280+
c.cacheDPoPNonce(ctx, asNonceScope, header.Get(dpopNonceHeader))
275281
if status != http.StatusCreated && status != http.StatusOK {
276282
return nil, parErrorFromResponse(body)
277283
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package client_test
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"net/http/httptest"
7+
"testing"
8+
9+
fapi "github.com/idfoundry/fapigo"
10+
"github.com/idfoundry/fapigo/client"
11+
"github.com/idfoundry/fapigo/storage"
12+
)
13+
14+
// TestBeginAuthorizationPropagatesTransportFailureOnPARPlain covers
15+
// pushAuthorizationRequestPlain's (SenderConstrainMTLS) transport-failure
16+
// branch — a connection failure at PAR, as opposed to an HTTP-level
17+
// error response.
18+
func TestBeginAuthorizationPropagatesTransportFailureOnPARPlain(t *testing.T) {
19+
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
20+
w.WriteHeader(http.StatusOK)
21+
}))
22+
ts.Close() // closed before use: every request now fails to connect
23+
24+
cfg := validConfig(t)
25+
cfg.SenderConstrain = storage.SenderConstrainMTLS
26+
cfg.Algorithms.DPoP = 0
27+
parURL, err := fapi.ParseEndpointURL(ts.URL+"/par", fapi.AllowLoopbackHTTP())
28+
if err != nil {
29+
t.Fatalf("ParseEndpointURL(par): %v", err)
30+
}
31+
cfg.Endpoints.PushedAuthorizationRequest = parURL
32+
33+
deps := validDependencies(t)
34+
deps.HTTP = ts.Client()
35+
36+
c, err := client.New(cfg, deps)
37+
if err != nil {
38+
t.Fatalf("client.New: %v", err)
39+
}
40+
if _, err := c.BeginAuthorization(context.Background(), client.BeginAuthorizationRequest{Scope: []string{"openid"}}); err == nil {
41+
t.Fatalf("BeginAuthorization(MTLS PAR transport failure) = nil error, want error")
42+
}
43+
}
44+
45+
// TestBeginAuthorizationPropagatesTransportFailureOnPARWithJKT covers
46+
// pushAuthorizationRequestWithJKT's transport-failure branch.
47+
func TestBeginAuthorizationPropagatesTransportFailureOnPARWithJKT(t *testing.T) {
48+
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
49+
w.WriteHeader(http.StatusOK)
50+
}))
51+
ts.Close() // closed before use: every request now fails to connect
52+
53+
cfg := validConfig(t)
54+
cfg.PARDPoPBinding = client.PARDPoPBindingJKT
55+
parURL, err := fapi.ParseEndpointURL(ts.URL+"/par", fapi.AllowLoopbackHTTP())
56+
if err != nil {
57+
t.Fatalf("ParseEndpointURL(par): %v", err)
58+
}
59+
cfg.Endpoints.PushedAuthorizationRequest = parURL
60+
61+
deps := validDependencies(t)
62+
deps.HTTP = ts.Client()
63+
64+
c, err := client.New(cfg, deps)
65+
if err != nil {
66+
t.Fatalf("client.New: %v", err)
67+
}
68+
if _, err := c.BeginAuthorization(context.Background(), client.BeginAuthorizationRequest{Scope: []string{"openid"}}); err == nil {
69+
t.Fatalf("BeginAuthorization(dpop_jkt PAR transport failure) = nil error, want error")
70+
}
71+
}
72+
73+
// TestBeginAuthorizationPropagatesTransportFailureOnPARWithDPoPProof
74+
// covers pushAuthorizationRequestWithDPoPProof's initial-attempt
75+
// transport-failure branch (PARDPoPBindingProof, the default).
76+
func TestBeginAuthorizationPropagatesTransportFailureOnPARWithDPoPProof(t *testing.T) {
77+
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
78+
w.WriteHeader(http.StatusOK)
79+
}))
80+
ts.Close() // closed before use: every request now fails to connect
81+
82+
cfg := validConfig(t)
83+
parURL, err := fapi.ParseEndpointURL(ts.URL+"/par", fapi.AllowLoopbackHTTP())
84+
if err != nil {
85+
t.Fatalf("ParseEndpointURL(par): %v", err)
86+
}
87+
cfg.Endpoints.PushedAuthorizationRequest = parURL
88+
89+
deps := validDependencies(t)
90+
deps.HTTP = ts.Client()
91+
92+
c, err := client.New(cfg, deps)
93+
if err != nil {
94+
t.Fatalf("client.New: %v", err)
95+
}
96+
if _, err := c.BeginAuthorization(context.Background(), client.BeginAuthorizationRequest{Scope: []string{"openid"}}); err == nil {
97+
t.Fatalf("BeginAuthorization(PAR transport failure) = nil error, want error")
98+
}
99+
}
100+
101+
// TestBeginAuthorizationPropagatesTransportFailureOnPARRetry covers
102+
// pushAuthorizationRequestWithDPoPProof's retry-attempt transport-failure
103+
// branch: the initial PAR call succeeds far enough to receive a
104+
// use_dpop_nonce challenge, but the replay carrying the fresh nonce
105+
// fails at the transport level.
106+
func TestBeginAuthorizationPropagatesTransportFailureOnPARRetry(t *testing.T) {
107+
as := newFakeAS(t, testIssuer, false)
108+
ts := httptest.NewServer(as.handler())
109+
t.Cleanup(ts.Close)
110+
as.challengeParDPoPNonce = "server-issued-par-nonce"
111+
112+
cfg := validConfig(t)
113+
parURL, err := fapi.ParseEndpointURL(ts.URL+"/par", fapi.AllowLoopbackHTTP())
114+
if err != nil {
115+
t.Fatalf("ParseEndpointURL(par): %v", err)
116+
}
117+
cfg.Endpoints.PushedAuthorizationRequest = parURL
118+
119+
deps := validDependencies(t)
120+
deps.HTTP = &failNthRequestHTTPClient{real: ts.Client(), pathSuffix: "/par", failOn: 2}
121+
122+
c, err := client.New(cfg, deps)
123+
if err != nil {
124+
t.Fatalf("client.New: %v", err)
125+
}
126+
if _, err := c.BeginAuthorization(context.Background(), client.BeginAuthorizationRequest{Scope: []string{"openid"}}); err == nil {
127+
t.Fatalf("BeginAuthorization(PAR retry transport failure) = nil error, want error")
128+
}
129+
if as.parCallCount != 1 {
130+
t.Fatalf("parCallCount = %d, want 1 (the retry failed at the transport, never reaching the fake AS)", as.parCallCount)
131+
}
132+
}

‎client/exchange_code.go‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ func (c *Client) sendTokenRequest(ctx context.Context, dpopSigner crypto.Signer,
255255
if c.cfg.SenderConstrain == storage.SenderConstrainMTLS {
256256
body, status, _, err := c.postForm(ctx, tokenURL.String(), form, nil)
257257
if err != nil {
258-
return nil, newError(ErrorInternal, "token request failed", err)
258+
return nil, newError(ErrorInternal, errTokenRequestFailed, err)
259259
}
260260
if status != http.StatusOK {
261261
return nil, parErrorFromResponse(body)
@@ -264,9 +264,9 @@ func (c *Client) sendTokenRequest(ctx context.Context, dpopSigner crypto.Signer,
264264
}
265265
body, status, header, err := c.postTokenRequestWithDPoP(ctx, dpopSigner, tokenURL, form, c.cachedDPoPNonce(ctx, asNonceScope))
266266
if err != nil {
267-
return nil, newError(ErrorInternal, "token request failed", err)
267+
return nil, newError(ErrorInternal, errTokenRequestFailed, err)
268268
}
269-
nextNonce := header.Get("DPoP-Nonce")
269+
nextNonce := header.Get(dpopNonceHeader)
270270
c.cacheDPoPNonce(ctx, asNonceScope, nextNonce)
271271
if status == http.StatusOK {
272272
return body, nil
@@ -281,9 +281,9 @@ func (c *Client) sendTokenRequest(ctx context.Context, dpopSigner crypto.Signer,
281281
}
282282
body, status, header, err = c.postTokenRequestWithDPoP(ctx, dpopSigner, tokenURL, retryForm, nextNonce)
283283
if err != nil {
284-
return nil, newError(ErrorInternal, "token request failed", err)
284+
return nil, newError(ErrorInternal, errTokenRequestFailed, err)
285285
}
286-
c.cacheDPoPNonce(ctx, asNonceScope, header.Get("DPoP-Nonce"))
286+
c.cacheDPoPNonce(ctx, asNonceScope, header.Get(dpopNonceHeader))
287287
if status != http.StatusOK {
288288
return nil, parErrorFromResponse(body)
289289
}

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL