Skip to content

Commit 3c9c1f6

Browse files
BigTailWolfgopherbot
authored andcommitted
oauth2/google: fix the logic of sts 0 value of expires_in
The sts response contains an optional field of `expires_in` and the value can be any integer. https://github.com/golang/oauth2/blob/master/google/internal/externalaccount/basecredentials.go#L246-L248 In the case of less than `0`, we are going to throw an error. But in the case of equals to `0` practically it means "never expire" instead of "instantly expire" which doesn't make sense. So we need to not set the expiration value for Token object. The current else if greater or equal is wrong. It's never triggered only because we are sending positive `3600` in sts response. Change-Id: Id227ca71130855235572b65ab178681e80d0da3a GitHub-Last-Rev: a95c923 GitHub-Pull-Request: #687 Reviewed-on: https://go-review.googlesource.com/c/oauth2/+/545895 Reviewed-by: Shin Fan <shinfan@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Cody Oss <codyoss@google.com> Reviewed-by: Cody Oss <codyoss@google.com>
1 parent 5a05c65 commit 3c9c1f6

File tree

2 files changed

+103
-27
lines changed

2 files changed

+103
-27
lines changed

google/externalaccount/basecredentials.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -471,11 +471,12 @@ func (ts tokenSource) Token() (*oauth2.Token, error) {
471471
AccessToken: stsResp.AccessToken,
472472
TokenType: stsResp.TokenType,
473473
}
474-
if stsResp.ExpiresIn < 0 {
474+
475+
// The RFC8693 doesn't define the explicit 0 of "expires_in" field behavior.
476+
if stsResp.ExpiresIn <= 0 {
475477
return nil, fmt.Errorf("oauth2/google/externalaccount: got invalid expiry from security token service")
476-
} else if stsResp.ExpiresIn >= 0 {
477-
accessToken.Expiry = now().Add(time.Duration(stsResp.ExpiresIn) * time.Second)
478478
}
479+
accessToken.Expiry = now().Add(time.Duration(stsResp.ExpiresIn) * time.Second)
479480

480481
if stsResp.RefreshToken != "" {
481482
accessToken.RefreshToken = stsResp.RefreshToken

google/externalaccount/basecredentials_test.go

+99-24
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package externalaccount
66

77
import (
88
"context"
9+
"encoding/json"
910
"fmt"
1011
"io/ioutil"
1112
"net/http"
@@ -101,15 +102,18 @@ func run(t *testing.T, config *Config, tets *testExchangeTokenServer) (*oauth2.T
101102
return ts.Token()
102103
}
103104

104-
func validateToken(t *testing.T, tok *oauth2.Token) {
105-
if got, want := tok.AccessToken, correctAT; got != want {
105+
func validateToken(t *testing.T, tok *oauth2.Token, expectToken *oauth2.Token) {
106+
if expectToken == nil {
107+
return
108+
}
109+
if got, want := tok.AccessToken, expectToken.AccessToken; got != want {
106110
t.Errorf("Unexpected access token: got %v, but wanted %v", got, want)
107111
}
108-
if got, want := tok.TokenType, "Bearer"; got != want {
112+
if got, want := tok.TokenType, expectToken.TokenType; got != want {
109113
t.Errorf("Unexpected TokenType: got %v, but wanted %v", got, want)
110114
}
111115

112-
if got, want := tok.Expiry, testNow().Add(time.Duration(3600)*time.Second); got != want {
116+
if got, want := tok.Expiry, expectToken.Expiry; got != want {
113117
t.Errorf("Unexpected Expiry: got %v, but wanted %v", got, want)
114118
}
115119
}
@@ -173,30 +177,91 @@ func getExpectedMetricsHeader(source string, saImpersonation bool, configLifetim
173177
}
174178

175179
func TestToken(t *testing.T) {
176-
config := Config{
177-
Audience: "32555940559.apps.googleusercontent.com",
178-
SubjectTokenType: "urn:ietf:params:oauth:token-type:id_token",
179-
ClientSecret: "notsosecret",
180-
ClientID: "rbrgnognrhongo3bi4gb9ghg9g",
181-
CredentialSource: &testBaseCredSource,
182-
Scopes: []string{"https://www.googleapis.com/auth/devstorage.full_control"},
180+
type MockSTSResponse struct {
181+
AccessToken string `json:"access_token"`
182+
IssuedTokenType string `json:"issued_token_type"`
183+
TokenType string `json:"token_type"`
184+
ExpiresIn int32 `json:"expires_in,omitempty"`
185+
Scope string `json:"scopre,omitenpty"`
183186
}
184187

185-
server := testExchangeTokenServer{
186-
url: "/",
187-
authorization: "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ=",
188-
contentType: "application/x-www-form-urlencoded",
189-
metricsHeader: getExpectedMetricsHeader("file", false, false),
190-
body: baseCredsRequestBody,
191-
response: baseCredsResponseBody,
188+
testCases := []struct {
189+
name string
190+
responseBody MockSTSResponse
191+
expectToken *oauth2.Token
192+
expectErrorMsg string
193+
}{
194+
{
195+
name: "happy case",
196+
responseBody: MockSTSResponse{
197+
AccessToken: correctAT,
198+
IssuedTokenType: "urn:ietf:params:oauth:token-type:access_token",
199+
TokenType: "Bearer",
200+
ExpiresIn: 3600,
201+
Scope: "https://www.googleapis.com/auth/cloud-platform",
202+
},
203+
expectToken: &oauth2.Token{
204+
AccessToken: correctAT,
205+
TokenType: "Bearer",
206+
Expiry: testNow().Add(time.Duration(3600) * time.Second),
207+
},
208+
},
209+
{
210+
name: "no expiry time on token",
211+
responseBody: MockSTSResponse{
212+
AccessToken: correctAT,
213+
IssuedTokenType: "urn:ietf:params:oauth:token-type:access_token",
214+
TokenType: "Bearer",
215+
Scope: "https://www.googleapis.com/auth/cloud-platform",
216+
},
217+
expectToken: nil,
218+
expectErrorMsg: "oauth2/google/externalaccount: got invalid expiry from security token service",
219+
},
220+
{
221+
name: "negative expiry time",
222+
responseBody: MockSTSResponse{
223+
AccessToken: correctAT,
224+
IssuedTokenType: "urn:ietf:params:oauth:token-type:access_token",
225+
TokenType: "Bearer",
226+
ExpiresIn: -1,
227+
Scope: "https://www.googleapis.com/auth/cloud-platform",
228+
},
229+
expectToken: nil,
230+
expectErrorMsg: "oauth2/google/externalaccount: got invalid expiry from security token service",
231+
},
192232
}
193233

194-
tok, err := run(t, &config, &server)
234+
for _, testCase := range testCases {
235+
config := Config{
236+
Audience: "32555940559.apps.googleusercontent.com",
237+
SubjectTokenType: "urn:ietf:params:oauth:token-type:id_token",
238+
ClientSecret: "notsosecret",
239+
ClientID: "rbrgnognrhongo3bi4gb9ghg9g",
240+
CredentialSource: &testBaseCredSource,
241+
Scopes: []string{"https://www.googleapis.com/auth/devstorage.full_control"},
242+
}
195243

196-
if err != nil {
197-
t.Fatalf("Unexpected error: %e", err)
244+
responseBody, err := json.Marshal(testCase.responseBody)
245+
if err != nil {
246+
t.Errorf("Invalid response received.")
247+
}
248+
249+
server := testExchangeTokenServer{
250+
url: "/",
251+
authorization: "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ=",
252+
contentType: "application/x-www-form-urlencoded",
253+
metricsHeader: getExpectedMetricsHeader("file", false, false),
254+
body: baseCredsRequestBody,
255+
response: string(responseBody),
256+
}
257+
258+
tok, err := run(t, &config, &server)
259+
260+
if err != nil && err.Error() != testCase.expectErrorMsg {
261+
t.Errorf("Error not as expected: got = %v, and want = %v", err, testCase.expectErrorMsg)
262+
}
263+
validateToken(t, tok, testCase.expectToken)
198264
}
199-
validateToken(t, tok)
200265
}
201266

202267
func TestWorkforcePoolTokenWithClientID(t *testing.T) {
@@ -224,7 +289,12 @@ func TestWorkforcePoolTokenWithClientID(t *testing.T) {
224289
if err != nil {
225290
t.Fatalf("Unexpected error: %e", err)
226291
}
227-
validateToken(t, tok)
292+
expectToken := oauth2.Token{
293+
AccessToken: correctAT,
294+
TokenType: "Bearer",
295+
Expiry: testNow().Add(time.Duration(3600) * time.Second),
296+
}
297+
validateToken(t, tok, &expectToken)
228298
}
229299

230300
func TestWorkforcePoolTokenWithoutClientID(t *testing.T) {
@@ -251,7 +321,12 @@ func TestWorkforcePoolTokenWithoutClientID(t *testing.T) {
251321
if err != nil {
252322
t.Fatalf("Unexpected error: %e", err)
253323
}
254-
validateToken(t, tok)
324+
expectToken := oauth2.Token{
325+
AccessToken: correctAT,
326+
TokenType: "Bearer",
327+
Expiry: testNow().Add(time.Duration(3600) * time.Second),
328+
}
329+
validateToken(t, tok, &expectToken)
255330
}
256331

257332
func TestNonworkforceWithWorkforcePoolUserProject(t *testing.T) {

0 commit comments

Comments
 (0)