Skip to content

Commit 6ca7ffb

Browse files
authored
BearerTokenPolicy ensures authN errors are NonRetriable (#25022)
1 parent 1827c76 commit 6ca7ffb

File tree

3 files changed

+44
-1
lines changed

3 files changed

+44
-1
lines changed

sdk/azcore/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
### Bugs Fixed
1010

11+
* Fixed a case in which `BearerTokenPolicy` didn't ensure an authentication error is non-retriable
12+
1113
### Other Changes
1214

1315
## 1.18.1 (2025-07-10)

sdk/azcore/runtime/policy_bearer_token.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,9 @@ func (b *BearerTokenPolicy) authenticateAndAuthorize(req *policy.Request) func(p
9797
as := acquiringResourceState{p: b, req: req, tro: tro}
9898
tk, err := b.mainResource.Get(as)
9999
if err != nil {
100-
return err
100+
// consider this error non-retriable because if it could be resolved by
101+
// retrying authentication, the credential would have done so already
102+
return errorinfo.NonRetriableError(err)
101103
}
102104
req.Raw().Header.Set(shared.HeaderAuthorization, shared.BearerTokenPrefix+tk.Token)
103105
return nil

sdk/azcore/runtime/policy_bearer_token_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,45 @@ func TestBearerTokenPolicy_CAEChallengeHandling(t *testing.T) {
518518
require.Equal(t, 2, tkReqs, "policy shouldn't handle a second CAE challenge for the same request")
519519
require.Equal(t, 2, srv.Requests(), "policy shouldn't handle a second CAE challenge for the same request")
520520
})
521+
522+
t.Run("errors non-retriable", func(t *testing.T) {
523+
srv, close := mock.NewTLSServer()
524+
defer close()
525+
srv.AppendResponse(mock.WithStatusCode(http.StatusOK))
526+
srv.AppendResponse(
527+
mock.WithHeader(shared.HeaderWWWAuthenticate, `Bearer error="insufficient_claims", claims="ey=="`),
528+
mock.WithStatusCode(http.StatusUnauthorized),
529+
)
530+
531+
called := false
532+
expectedErr := errors.New("something went wrong")
533+
cred := mockCredential{
534+
getTokenImpl: func(context.Context, policy.TokenRequestOptions) (exported.AccessToken, error) {
535+
if called {
536+
return exported.AccessToken{}, expectedErr
537+
}
538+
called = true
539+
return exported.AccessToken{Token: tokenValue, ExpiresOn: time.Now().Add(time.Hour).UTC()}, nil
540+
},
541+
}
542+
counter := &countingPolicy{}
543+
btp := NewBearerTokenPolicy(cred, []string{scope}, nil)
544+
pl := newTestPipeline(&policy.ClientOptions{PerRetryPolicies: []policy.Policy{counter, btp}, Transport: srv})
545+
546+
req, err := NewRequest(context.Background(), http.MethodGet, srv.URL())
547+
require.NoError(t, err)
548+
_, err = pl.Do(req)
549+
require.NoError(t, err)
550+
551+
req, err = NewRequest(context.Background(), http.MethodGet, srv.URL())
552+
require.NoError(t, err)
553+
_, err = pl.Do(req)
554+
require.EqualError(t, err, expectedErr.Error())
555+
require.ErrorAs(t, err, new(errorinfo.NonRetriable))
556+
// this is the crucial assertion; the retry policy would have retried the request
557+
// if BearerTokenPolicy didn't make the credential's error NonRetriable
558+
require.Equal(t, 2, counter.count, "BearerTokenPolicy should make the authentication error NonRetriable")
559+
})
521560
}
522561

523562
func TestBearerTokenPolicy_RequiresHTTPS(t *testing.T) {

0 commit comments

Comments
 (0)