google: Support scopes for ComputeTokenSource

Scopes have been added as a query parameter to the metadata server.

Change-Id: Ife68db01beeca386e558edd424fa11da508b7287
GitHub-Last-Rev: 1cb4a6ec12f2b17f6a2290a524b18d60246d56ae
GitHub-Pull-Request: golang/oauth2#376
Reviewed-on: https://go-review.googlesource.com/c/oauth2/+/170106
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/google/default.go b/google/default.go
index 5087d84..ad2c092 100644
--- a/google/default.go
+++ b/google/default.go
@@ -73,7 +73,6 @@
 //   4. On Google Compute Engine, Google App Engine standard second generation runtimes
 //      (>= Go 1.11), and Google App Engine flexible environment, it fetches
 //      credentials from the metadata server.
-//      (In this final case any provided scopes are ignored.)
 func FindDefaultCredentials(ctx context.Context, scopes ...string) (*Credentials, error) {
 	// First, try the environment variable.
 	const envVar = "GOOGLE_APPLICATION_CREDENTIALS"
@@ -109,7 +108,7 @@
 		id, _ := metadata.ProjectID()
 		return &DefaultCredentials{
 			ProjectID:   id,
-			TokenSource: ComputeTokenSource(""),
+			TokenSource: ComputeTokenSource("", scopes...),
 		}, nil
 	}
 
diff --git a/google/example_test.go b/google/example_test.go
index 4338ca3..3fc9cad 100644
--- a/google/example_test.go
+++ b/google/example_test.go
@@ -126,7 +126,9 @@
 			// Fetch from Google Compute Engine's metadata server to retrieve
 			// an access token for the provided account.
 			// If no account is specified, "default" is used.
-			Source: google.ComputeTokenSource(""),
+			// If no scopes are specified, a set of default scopes
+			// are automatically granted.
+			Source: google.ComputeTokenSource("", "https://www.googleapis.com/auth/bigquery"),
 		},
 	}
 	client.Get("...")
diff --git a/google/google.go b/google/google.go
index df8e87d..6eb2aa9 100644
--- a/google/google.go
+++ b/google/google.go
@@ -9,6 +9,7 @@
 	"encoding/json"
 	"errors"
 	"fmt"
+	"net/url"
 	"strings"
 	"time"
 
@@ -151,14 +152,16 @@
 // from Google Compute Engine (GCE)'s metadata server. It's only valid to use
 // this token source if your program is running on a GCE instance.
 // If no account is specified, "default" is used.
+// If no scopes are specified, a set of default scopes are automatically granted.
 // Further information about retrieving access tokens from the GCE metadata
 // server can be found at https://cloud.google.com/compute/docs/authentication.
-func ComputeTokenSource(account string) oauth2.TokenSource {
-	return oauth2.ReuseTokenSource(nil, computeSource{account: account})
+func ComputeTokenSource(account string, scope ...string) oauth2.TokenSource {
+	return oauth2.ReuseTokenSource(nil, computeSource{account: account, scopes: scope})
 }
 
 type computeSource struct {
 	account string
+	scopes  []string
 }
 
 func (cs computeSource) Token() (*oauth2.Token, error) {
@@ -169,7 +172,13 @@
 	if acct == "" {
 		acct = "default"
 	}
-	tokenJSON, err := metadata.Get("instance/service-accounts/" + acct + "/token")
+	tokenURI := "instance/service-accounts/" + acct + "/token"
+	if len(cs.scopes) > 0 {
+		v := url.Values{}
+		v.Set("scopes", strings.Join(cs.scopes, ","))
+		tokenURI = tokenURI + "?" + v.Encode()
+	}
+	tokenJSON, err := metadata.Get(tokenURI)
 	if err != nil {
 		return nil, err
 	}