fix: missing expiration_time field isn't a problem for executables

Change-Id: Ib19e3d9dcd8a4c41afebf2a1fb97429617eef86b
GitHub-Last-Rev: 96eb2344dee49b7c08aca5c728c0f4d9ec4634b8
GitHub-Pull-Request: golang/oauth2#576
Reviewed-on: https://go-review.googlesource.com/c/oauth2/+/418434
Reviewed-by: Leo Siracusa <leosiracusa@google.com>
Run-TryBot: Cody Oss <codyoss@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Cody Oss <codyoss@google.com>
diff --git a/google/internal/externalaccount/executablecredsource.go b/google/internal/externalaccount/executablecredsource.go
index 6ecbe6e..7e8f85b 100644
--- a/google/internal/externalaccount/executablecredsource.go
+++ b/google/internal/externalaccount/executablecredsource.go
@@ -178,7 +178,7 @@
 	Message        string `json:"message,omitempty"`
 }
 
-func parseSubjectTokenFromSource(response []byte, source string, now int64) (string, error) {
+func (cs executableCredentialSource) parseSubjectTokenFromSource(response []byte, source string, now int64) (string, error) {
 	var result executableResponse
 	if err := json.Unmarshal(response, &result); err != nil {
 		return "", jsonParsingError(source, string(response))
@@ -203,7 +203,7 @@
 		return "", unsupportedVersionError(source, result.Version)
 	}
 
-	if result.ExpirationTime == 0 {
+	if result.ExpirationTime == 0 && cs.OutputFile != "" {
 		return "", missingFieldError(source, "expiration_time")
 	}
 
@@ -211,7 +211,7 @@
 		return "", missingFieldError(source, "token_type")
 	}
 
-	if result.ExpirationTime < now {
+	if result.ExpirationTime != 0 && result.ExpirationTime < now {
 		return "", tokenExpiredError()
 	}
 
@@ -259,7 +259,7 @@
 		return "", nil
 	}
 
-	token, err = parseSubjectTokenFromSource(data, outputFileSource, cs.env.now().Unix())
+	token, err = cs.parseSubjectTokenFromSource(data, outputFileSource, cs.env.now().Unix())
 	if err != nil {
 		if _, ok := err.(nonCacheableError); ok {
 			// If the cached token is expired we need a new token,
@@ -304,5 +304,5 @@
 	if err != nil {
 		return "", err
 	}
-	return parseSubjectTokenFromSource(output, executableSource, cs.env.now().Unix())
+	return cs.parseSubjectTokenFromSource(output, executableSource, cs.env.now().Unix())
 }
diff --git a/google/internal/externalaccount/executablecredsource_test.go b/google/internal/externalaccount/executablecredsource_test.go
index f115b29..074dfc4 100644
--- a/google/internal/externalaccount/executablecredsource_test.go
+++ b/google/internal/externalaccount/executablecredsource_test.go
@@ -389,19 +389,6 @@
 	},
 
 	{
-		name: "Missing Expiration",
-		testEnvironment: testEnvironment{
-			envVars: executablesAllowed,
-			jsonResponse: &executableResponse{
-				Success:   Bool(true),
-				Version:   1,
-				TokenType: "urn:ietf:params:oauth:token-type:jwt",
-			},
-		},
-		expectedErr: missingFieldError(executableSource, "expiration_time"),
-	},
-
-	{
 		name: "Token Expired",
 		testEnvironment: testEnvironment{
 			envVars: executablesAllowed,
@@ -564,6 +551,19 @@
 			},
 		},
 	},
+
+	{
+		name: "Missing Expiration",
+		testEnvironment: testEnvironment{
+			envVars: executablesAllowed,
+			jsonResponse: &executableResponse{
+				Success:   Bool(true),
+				Version:   1,
+				TokenType: "urn:ietf:params:oauth:token-type:jwt",
+				IdToken:   "tokentokentoken",
+			},
+		},
+	},
 }
 
 func TestRetrieveExecutableSubjectTokenSuccesses(t *testing.T) {