google: add Credentials.UniverseDomain to support TPC

Read and expose universe_domain from service account JSON files in
CredentialsFromJSONWithParams to support TPC in 1p clients.

Change-Id: I3518a0ec8be5ff7235b946cffd88b26ac8d303cf
Reviewed-on: https://go-review.googlesource.com/c/oauth2/+/531715
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/default.go b/google/default.go
index 2cf71f0..1d69bf7 100644
--- a/google/default.go
+++ b/google/default.go
@@ -19,7 +19,10 @@
 	"golang.org/x/oauth2/authhandler"
 )
 
-const adcSetupURL = "https://cloud.google.com/docs/authentication/external/set-up-adc"
+const (
+	adcSetupURL           = "https://cloud.google.com/docs/authentication/external/set-up-adc"
+	universeDomainDefault = "googleapis.com"
+)
 
 // Credentials holds Google credentials, including "Application Default Credentials".
 // For more details, see:
@@ -37,6 +40,18 @@
 	// environment and not with a credentials file, e.g. when code is
 	// running on Google Cloud Platform.
 	JSON []byte
+
+	// universeDomain is the default service domain for a given Cloud universe.
+	universeDomain string
+}
+
+// UniverseDomain returns the default service domain for a given Cloud universe.
+// The default value is "googleapis.com".
+func (c *Credentials) UniverseDomain() string {
+	if c.universeDomain == "" {
+		return universeDomainDefault
+	}
+	return c.universeDomain
 }
 
 // DefaultCredentials is the old name of Credentials.
@@ -200,15 +215,17 @@
 	if err := json.Unmarshal(jsonData, &f); err != nil {
 		return nil, err
 	}
+
 	ts, err := f.tokenSource(ctx, params)
 	if err != nil {
 		return nil, err
 	}
 	ts = newErrWrappingTokenSource(ts)
 	return &Credentials{
-		ProjectID:   f.ProjectID,
-		TokenSource: ts,
-		JSON:        jsonData,
+		ProjectID:      f.ProjectID,
+		TokenSource:    ts,
+		JSON:           jsonData,
+		universeDomain: f.UniverseDomain,
 	}, nil
 }
 
diff --git a/google/default_test.go b/google/default_test.go
new file mode 100644
index 0000000..5425e35
--- /dev/null
+++ b/google/default_test.go
@@ -0,0 +1,43 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package google
+
+import (
+	"context"
+	"testing"
+)
+
+var jwtJSONKeyUniverseDomain = []byte(`{
+  "type": "service_account",
+  "project_id": "fake_project",
+  "universe_domain": "example.com",
+  "private_key_id": "268f54e43a1af97cfc71731688434f45aca15c8b",
+  "private_key": "super secret key",
+  "client_email": "gopher@developer.gserviceaccount.com",
+  "client_id": "gopher.apps.googleusercontent.com",
+  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
+  "token_uri": "https://oauth2.googleapis.com/token",
+  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
+  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/gopher%40fake_project.iam.gserviceaccount.com"
+}`)
+
+func TestCredentialsFromJSONWithParams_UniverseDomain(t *testing.T) {
+	ctx := context.Background()
+	scope := "https://www.googleapis.com/auth/cloud-platform"
+	params := CredentialsParams{
+		Scopes: []string{scope},
+	}
+	creds, err := CredentialsFromJSONWithParams(ctx, jwtJSONKeyUniverseDomain, params)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if want := "fake_project"; creds.ProjectID != want {
+		t.Fatalf("got %q, want %q", creds.ProjectID, want)
+	}
+	if want := "example.com"; creds.UniverseDomain() != want {
+		t.Fatalf("got %q, want %q", creds.UniverseDomain(), want)
+	}
+}
diff --git a/google/google.go b/google/google.go
index 8bc6018..c66c535 100644
--- a/google/google.go
+++ b/google/google.go
@@ -22,10 +22,10 @@
 
 // Endpoint is Google's OAuth 2.0 default endpoint.
 var Endpoint = oauth2.Endpoint{
-	AuthURL:   "https://accounts.google.com/o/oauth2/auth",
-	TokenURL:  "https://oauth2.googleapis.com/token",
+	AuthURL:       "https://accounts.google.com/o/oauth2/auth",
+	TokenURL:      "https://oauth2.googleapis.com/token",
 	DeviceAuthURL: "https://oauth2.googleapis.com/device/code",
-	AuthStyle: oauth2.AuthStyleInParams,
+	AuthStyle:     oauth2.AuthStyleInParams,
 }
 
 // MTLSTokenURL is Google's OAuth 2.0 default mTLS endpoint.
@@ -109,12 +109,13 @@
 	Type string `json:"type"`
 
 	// Service Account fields
-	ClientEmail  string `json:"client_email"`
-	PrivateKeyID string `json:"private_key_id"`
-	PrivateKey   string `json:"private_key"`
-	AuthURL      string `json:"auth_uri"`
-	TokenURL     string `json:"token_uri"`
-	ProjectID    string `json:"project_id"`
+	ClientEmail    string `json:"client_email"`
+	PrivateKeyID   string `json:"private_key_id"`
+	PrivateKey     string `json:"private_key"`
+	AuthURL        string `json:"auth_uri"`
+	TokenURL       string `json:"token_uri"`
+	ProjectID      string `json:"project_id"`
+	UniverseDomain string `json:"universe_domain"`
 
 	// User Credential fields
 	// (These typically come from gcloud auth.)