From 7bec97b2f51ed3ac4f9b88bf100d301da3f5d1bd Mon Sep 17 00:00:00 2001 From: Cody Oss <6331106+codyoss@users.noreply.github.com> Date: Wed, 20 Mar 2024 15:54:18 -0500 Subject: [PATCH] fix(auth): port sts expires fix (#9618) Ported from: https://togithub.com/golang/oauth2/commit/3c9c1f6d00e8761389cd2c50bc4179459d6320b5 --- .../externalaccount/externalaccount.go | 6 +- .../externalaccount/externalaccount_test.go | 75 +++++++++++++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/auth/credentials/internal/externalaccount/externalaccount.go b/auth/credentials/internal/externalaccount/externalaccount.go index e346e037d5f1..3146a25bd39d 100644 --- a/auth/credentials/internal/externalaccount/externalaccount.go +++ b/auth/credentials/internal/externalaccount/externalaccount.go @@ -181,11 +181,11 @@ func (tp *tokenProvider) Token(ctx context.Context) (*auth.Token, error) { Value: stsResp.AccessToken, Type: stsResp.TokenType, } - if stsResp.ExpiresIn < 0 { + // The RFC8693 doesn't define the explicit 0 of "expires_in" field behavior. + if stsResp.ExpiresIn <= 0 { return nil, fmt.Errorf("detect: got invalid expiry from security token service") - } else if stsResp.ExpiresIn >= 0 { - tok.Expiry = now().Add(time.Duration(stsResp.ExpiresIn) * time.Second) } + tok.Expiry = now().Add(time.Duration(stsResp.ExpiresIn) * time.Second) return tok, nil } diff --git a/auth/credentials/internal/externalaccount/externalaccount_test.go b/auth/credentials/internal/externalaccount/externalaccount_test.go index 1a3dc067b64c..88b8a4bd9701 100644 --- a/auth/credentials/internal/externalaccount/externalaccount_test.go +++ b/auth/credentials/internal/externalaccount/externalaccount_test.go @@ -16,6 +16,7 @@ package externalaccount import ( "context" + "encoding/json" "fmt" "io" "net/http" @@ -24,6 +25,7 @@ import ( "time" "cloud.google.com/go/auth" + "cloud.google.com/go/auth/credentials/internal/stsexchange" "cloud.google.com/go/auth/internal" "cloud.google.com/go/auth/internal/internaldetect" ) @@ -58,6 +60,79 @@ var ( ) func TestToken(t *testing.T) { + tests := []struct { + name string + respBody *stsexchange.TokenResponse + wantError bool + }{ + { + name: "works", + respBody: &stsexchange.TokenResponse{ + AccessToken: correctAT, + IssuedTokenType: "urn:ietf:params:oauth:token-type:access_token", + TokenType: "Bearer", + ExpiresIn: 3600, + Scope: "https://www.googleapis.com/auth/cloud-platform", + }, + }, + { + name: "no exp time on tok", + respBody: &stsexchange.TokenResponse{ + AccessToken: correctAT, + IssuedTokenType: "urn:ietf:params:oauth:token-type:access_token", + TokenType: "Bearer", + Scope: "https://www.googleapis.com/auth/cloud-platform", + }, + wantError: true, + }, + { + name: "negative exp time", + respBody: &stsexchange.TokenResponse{ + AccessToken: correctAT, + IssuedTokenType: "urn:ietf:params:oauth:token-type:access_token", + TokenType: "Bearer", + ExpiresIn: -1, + Scope: "https://www.googleapis.com/auth/cloud-platform", + }, + wantError: true, + }, + } + for _, tt := range tests { + opts := &Options{ + Audience: "32555940559.apps.googleusercontent.com", + SubjectTokenType: idTokenType, + ClientSecret: "notsosecret", + ClientID: "rbrgnognrhongo3bi4gb9ghg9g", + CredentialSource: testBaseCredSource, + Scopes: []string{"https://www.googleapis.com/auth/devstorage.full_control"}, + } + + respBody, err := json.Marshal(tt.respBody) + if err != nil { + t.Fatal(err) + } + + server := &testExchangeTokenServer{ + url: "/", + authorization: "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ=", + contentType: "application/x-www-form-urlencoded", + body: baseCredsRequestBody, + response: string(respBody), + metricsHeader: expectedMetricsHeader("file", false, false), + } + + tok, err := run(t, opts, server) + if err != nil && !tt.wantError { + t.Fatal(err) + } + if tt.wantError { + if err == nil { + t.Fatal("want err, got nil") + } + continue + } + validateToken(t, tok) + } opts := &Options{ Audience: "32555940559.apps.googleusercontent.com", SubjectTokenType: idTokenType,