...

Source file src/golang.org/x/oauth2/google/externalaccount/basecredentials_test.go

Documentation: golang.org/x/oauth2/google/externalaccount

     1  // Copyright 2020 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package externalaccount
     6  
     7  import (
     8  	"context"
     9  	"encoding/json"
    10  	"fmt"
    11  	"io/ioutil"
    12  	"net/http"
    13  	"net/http/httptest"
    14  	"testing"
    15  	"time"
    16  
    17  	"golang.org/x/oauth2"
    18  )
    19  
    20  const (
    21  	textBaseCredPath             = "testdata/3pi_cred.txt"
    22  	jsonBaseCredPath             = "testdata/3pi_cred.json"
    23  	baseImpersonateCredsReqBody  = "audience=32555940559.apps.googleusercontent.com&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform&subject_token=street123&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Ajwt"
    24  	baseImpersonateCredsRespBody = `{"accessToken":"Second.Access.Token","expireTime":"2020-12-28T15:01:23Z"}`
    25  )
    26  
    27  var testBaseCredSource = CredentialSource{
    28  	File:   textBaseCredPath,
    29  	Format: Format{Type: fileTypeText},
    30  }
    31  
    32  var testConfig = Config{
    33  	Audience:         "32555940559.apps.googleusercontent.com",
    34  	SubjectTokenType: "urn:ietf:params:oauth:token-type:jwt",
    35  	TokenInfoURL:     "http://localhost:8080/v1/tokeninfo",
    36  	ClientSecret:     "notsosecret",
    37  	ClientID:         "rbrgnognrhongo3bi4gb9ghg9g",
    38  	CredentialSource: &testBaseCredSource,
    39  	Scopes:           []string{"https://www.googleapis.com/auth/devstorage.full_control"},
    40  }
    41  
    42  var (
    43  	baseCredsRequestBody                          = "audience=32555940559.apps.googleusercontent.com&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control&subject_token=street123&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aid_token"
    44  	baseCredsResponseBody                         = `{"access_token":"Sample.Access.Token","issued_token_type":"urn:ietf:params:oauth:token-type:access_token","token_type":"Bearer","expires_in":3600,"scope":"https://www.googleapis.com/auth/cloud-platform"}`
    45  	workforcePoolRequestBodyWithClientId          = "audience=%2F%2Fiam.googleapis.com%2Flocations%2Feu%2FworkforcePools%2Fpool-id%2Fproviders%2Fprovider-id&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control&subject_token=street123&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aid_token"
    46  	workforcePoolRequestBodyWithoutClientId       = "audience=%2F%2Fiam.googleapis.com%2Flocations%2Feu%2FworkforcePools%2Fpool-id%2Fproviders%2Fprovider-id&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&options=%7B%22userProject%22%3A%22myProject%22%7D&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control&subject_token=street123&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aid_token"
    47  	correctAT                                     = "Sample.Access.Token"
    48  	expiry                                  int64 = 234852
    49  )
    50  var (
    51  	testNow = func() time.Time { return time.Unix(expiry, 0) }
    52  )
    53  
    54  type testExchangeTokenServer struct {
    55  	url           string
    56  	authorization string
    57  	contentType   string
    58  	metricsHeader string
    59  	body          string
    60  	response      string
    61  }
    62  
    63  func run(t *testing.T, config *Config, tets *testExchangeTokenServer) (*oauth2.Token, error) {
    64  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    65  		if got, want := r.URL.String(), tets.url; got != want {
    66  			t.Errorf("URL.String(): got %v but want %v", got, want)
    67  		}
    68  		headerAuth := r.Header.Get("Authorization")
    69  		if got, want := headerAuth, tets.authorization; got != want {
    70  			t.Errorf("got %v but want %v", got, want)
    71  		}
    72  		headerContentType := r.Header.Get("Content-Type")
    73  		if got, want := headerContentType, tets.contentType; got != want {
    74  			t.Errorf("got %v but want %v", got, want)
    75  		}
    76  		headerMetrics := r.Header.Get("x-goog-api-client")
    77  		if got, want := headerMetrics, tets.metricsHeader; got != want {
    78  			t.Errorf("got %v but want %v", got, want)
    79  		}
    80  		body, err := ioutil.ReadAll(r.Body)
    81  		if err != nil {
    82  			t.Fatalf("Failed reading request body: %s.", err)
    83  		}
    84  		if got, want := string(body), tets.body; got != want {
    85  			t.Errorf("Unexpected exchange payload: got %v but want %v", got, want)
    86  		}
    87  		w.Header().Set("Content-Type", "application/json")
    88  		w.Write([]byte(tets.response))
    89  	}))
    90  	defer server.Close()
    91  	config.TokenURL = server.URL
    92  
    93  	oldNow := now
    94  	defer func() { now = oldNow }()
    95  	now = testNow
    96  
    97  	ts := tokenSource{
    98  		ctx:  context.Background(),
    99  		conf: config,
   100  	}
   101  
   102  	return ts.Token()
   103  }
   104  
   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 {
   110  		t.Errorf("Unexpected access token: got %v, but wanted %v", got, want)
   111  	}
   112  	if got, want := tok.TokenType, expectToken.TokenType; got != want {
   113  		t.Errorf("Unexpected TokenType: got %v, but wanted %v", got, want)
   114  	}
   115  
   116  	if got, want := tok.Expiry, expectToken.Expiry; got != want {
   117  		t.Errorf("Unexpected Expiry: got %v, but wanted %v", got, want)
   118  	}
   119  }
   120  
   121  func createImpersonationServer(urlWanted, authWanted, bodyWanted, response string, t *testing.T) *httptest.Server {
   122  	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   123  		if got, want := r.URL.String(), urlWanted; got != want {
   124  			t.Errorf("URL.String(): got %v but want %v", got, want)
   125  		}
   126  		headerAuth := r.Header.Get("Authorization")
   127  		if got, want := headerAuth, authWanted; got != want {
   128  			t.Errorf("got %v but want %v", got, want)
   129  		}
   130  		headerContentType := r.Header.Get("Content-Type")
   131  		if got, want := headerContentType, "application/json"; got != want {
   132  			t.Errorf("got %v but want %v", got, want)
   133  		}
   134  		body, err := ioutil.ReadAll(r.Body)
   135  		if err != nil {
   136  			t.Fatalf("Failed reading request body: %v.", err)
   137  		}
   138  		if got, want := string(body), bodyWanted; got != want {
   139  			t.Errorf("Unexpected impersonation payload: got %v but want %v", got, want)
   140  		}
   141  		w.Header().Set("Content-Type", "application/json")
   142  		w.Write([]byte(response))
   143  	}))
   144  }
   145  
   146  func createTargetServer(metricsHeaderWanted string, t *testing.T) *httptest.Server {
   147  	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   148  		if got, want := r.URL.String(), "/"; got != want {
   149  			t.Errorf("URL.String(): got %v but want %v", got, want)
   150  		}
   151  		headerAuth := r.Header.Get("Authorization")
   152  		if got, want := headerAuth, "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ="; got != want {
   153  			t.Errorf("got %v but want %v", got, want)
   154  		}
   155  		headerContentType := r.Header.Get("Content-Type")
   156  		if got, want := headerContentType, "application/x-www-form-urlencoded"; got != want {
   157  			t.Errorf("got %v but want %v", got, want)
   158  		}
   159  		headerMetrics := r.Header.Get("x-goog-api-client")
   160  		if got, want := headerMetrics, metricsHeaderWanted; got != want {
   161  			t.Errorf("got %v but want %v", got, want)
   162  		}
   163  		body, err := ioutil.ReadAll(r.Body)
   164  		if err != nil {
   165  			t.Fatalf("Failed reading request body: %v.", err)
   166  		}
   167  		if got, want := string(body), baseImpersonateCredsReqBody; got != want {
   168  			t.Errorf("Unexpected exchange payload: got %v but want %v", got, want)
   169  		}
   170  		w.Header().Set("Content-Type", "application/json")
   171  		w.Write([]byte(baseCredsResponseBody))
   172  	}))
   173  }
   174  
   175  func getExpectedMetricsHeader(source string, saImpersonation bool, configLifetime bool) string {
   176  	return fmt.Sprintf("gl-go/%s auth/unknown google-byoid-sdk source/%s sa-impersonation/%t config-lifetime/%t", goVersion(), source, saImpersonation, configLifetime)
   177  }
   178  
   179  func TestToken(t *testing.T) {
   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"`
   186  	}
   187  
   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  		},
   232  	}
   233  
   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  		}
   243  
   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)
   264  	}
   265  }
   266  
   267  func TestWorkforcePoolTokenWithClientID(t *testing.T) {
   268  	config := Config{
   269  		Audience:                 "//iam.googleapis.com/locations/eu/workforcePools/pool-id/providers/provider-id",
   270  		SubjectTokenType:         "urn:ietf:params:oauth:token-type:id_token",
   271  		ClientSecret:             "notsosecret",
   272  		ClientID:                 "rbrgnognrhongo3bi4gb9ghg9g",
   273  		CredentialSource:         &testBaseCredSource,
   274  		Scopes:                   []string{"https://www.googleapis.com/auth/devstorage.full_control"},
   275  		WorkforcePoolUserProject: "myProject",
   276  	}
   277  
   278  	server := testExchangeTokenServer{
   279  		url:           "/",
   280  		authorization: "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ=",
   281  		contentType:   "application/x-www-form-urlencoded",
   282  		metricsHeader: getExpectedMetricsHeader("file", false, false),
   283  		body:          workforcePoolRequestBodyWithClientId,
   284  		response:      baseCredsResponseBody,
   285  	}
   286  
   287  	tok, err := run(t, &config, &server)
   288  
   289  	if err != nil {
   290  		t.Fatalf("Unexpected error: %e", err)
   291  	}
   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)
   298  }
   299  
   300  func TestWorkforcePoolTokenWithoutClientID(t *testing.T) {
   301  	config := Config{
   302  		Audience:                 "//iam.googleapis.com/locations/eu/workforcePools/pool-id/providers/provider-id",
   303  		SubjectTokenType:         "urn:ietf:params:oauth:token-type:id_token",
   304  		ClientSecret:             "notsosecret",
   305  		CredentialSource:         &testBaseCredSource,
   306  		Scopes:                   []string{"https://www.googleapis.com/auth/devstorage.full_control"},
   307  		WorkforcePoolUserProject: "myProject",
   308  	}
   309  
   310  	server := testExchangeTokenServer{
   311  		url:           "/",
   312  		authorization: "",
   313  		contentType:   "application/x-www-form-urlencoded",
   314  		metricsHeader: getExpectedMetricsHeader("file", false, false),
   315  		body:          workforcePoolRequestBodyWithoutClientId,
   316  		response:      baseCredsResponseBody,
   317  	}
   318  
   319  	tok, err := run(t, &config, &server)
   320  
   321  	if err != nil {
   322  		t.Fatalf("Unexpected error: %e", err)
   323  	}
   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)
   330  }
   331  
   332  func TestNonworkforceWithWorkforcePoolUserProject(t *testing.T) {
   333  	config := Config{
   334  		Audience:                 "32555940559.apps.googleusercontent.com",
   335  		SubjectTokenType:         "urn:ietf:params:oauth:token-type:id_token",
   336  		TokenURL:                 "https://sts.googleapis.com",
   337  		ClientSecret:             "notsosecret",
   338  		ClientID:                 "rbrgnognrhongo3bi4gb9ghg9g",
   339  		CredentialSource:         &testBaseCredSource,
   340  		Scopes:                   []string{"https://www.googleapis.com/auth/devstorage.full_control"},
   341  		WorkforcePoolUserProject: "myProject",
   342  	}
   343  
   344  	_, err := NewTokenSource(context.Background(), config)
   345  
   346  	if err == nil {
   347  		t.Fatalf("Expected error but found none")
   348  	}
   349  	if got, want := err.Error(), "oauth2/google/externalaccount: Workforce pool user project should not be set for non-workforce pool credentials"; got != want {
   350  		t.Errorf("Incorrect error received.\nExpected: %s\nRecieved: %s", want, got)
   351  	}
   352  }
   353  
   354  func TestWorkforcePoolCreation(t *testing.T) {
   355  	var audienceValidatyTests = []struct {
   356  		audience      string
   357  		expectSuccess bool
   358  	}{
   359  		{"//iam.googleapis.com/locations/global/workforcePools/pool-id/providers/provider-id", true},
   360  		{"//iam.googleapis.com/locations/eu/workforcePools/pool-id/providers/provider-id", true},
   361  		{"//iam.googleapis.com/locations/eu/workforcePools/workloadIdentityPools/providers/provider-id", true},
   362  		{"identitynamespace:1f12345:my_provider", false},
   363  		{"//iam.googleapis.com/projects/123456/locations/global/workloadIdentityPools/pool-id/providers/provider-id", false},
   364  		{"//iam.googleapis.com/projects/123456/locations/eu/workloadIdentityPools/pool-id/providers/provider-id", false},
   365  		{"//iam.googleapis.com/projects/123456/locations/global/workloadIdentityPools/workforcePools/providers/provider-id", false},
   366  		{"//iamgoogleapis.com/locations/eu/workforcePools/pool-id/providers/provider-id", false},
   367  		{"//iam.googleapiscom/locations/eu/workforcePools/pool-id/providers/provider-id", false},
   368  		{"//iam.googleapis.com/locations/workforcePools/pool-id/providers/provider-id", false},
   369  		{"//iam.googleapis.com/locations/eu/workforcePool/pool-id/providers/provider-id", false},
   370  		{"//iam.googleapis.com/locations//workforcePool/pool-id/providers/provider-id", false},
   371  	}
   372  
   373  	ctx := context.Background()
   374  	for _, tt := range audienceValidatyTests {
   375  		t.Run(" "+tt.audience, func(t *testing.T) { // We prepend a space ahead of the test input when outputting for sake of readability.
   376  			config := testConfig
   377  			config.TokenURL = "https://sts.googleapis.com" // Setting the most basic acceptable tokenURL
   378  			config.ServiceAccountImpersonationURL = "https://iamcredentials.googleapis.com"
   379  			config.Audience = tt.audience
   380  			config.WorkforcePoolUserProject = "myProject"
   381  			_, err := NewTokenSource(ctx, config)
   382  
   383  			if tt.expectSuccess && err != nil {
   384  				t.Errorf("got %v but want nil", err)
   385  			} else if !tt.expectSuccess && err == nil {
   386  				t.Errorf("got nil but expected an error")
   387  			}
   388  		})
   389  	}
   390  }
   391  
   392  var impersonationTests = []struct {
   393  	name                      string
   394  	config                    Config
   395  	expectedImpersonationBody string
   396  	expectedMetricsHeader     string
   397  }{
   398  	{
   399  		name: "Base Impersonation",
   400  		config: Config{
   401  			Audience:         "32555940559.apps.googleusercontent.com",
   402  			SubjectTokenType: "urn:ietf:params:oauth:token-type:jwt",
   403  			TokenInfoURL:     "http://localhost:8080/v1/tokeninfo",
   404  			ClientSecret:     "notsosecret",
   405  			ClientID:         "rbrgnognrhongo3bi4gb9ghg9g",
   406  			CredentialSource: &testBaseCredSource,
   407  			Scopes:           []string{"https://www.googleapis.com/auth/devstorage.full_control"},
   408  		},
   409  		expectedImpersonationBody: "{\"lifetime\":\"3600s\",\"scope\":[\"https://www.googleapis.com/auth/devstorage.full_control\"]}",
   410  		expectedMetricsHeader:     getExpectedMetricsHeader("file", true, false),
   411  	},
   412  	{
   413  		name: "With TokenLifetime Set",
   414  		config: Config{
   415  			Audience:         "32555940559.apps.googleusercontent.com",
   416  			SubjectTokenType: "urn:ietf:params:oauth:token-type:jwt",
   417  			TokenInfoURL:     "http://localhost:8080/v1/tokeninfo",
   418  			ClientSecret:     "notsosecret",
   419  			ClientID:         "rbrgnognrhongo3bi4gb9ghg9g",
   420  			CredentialSource: &testBaseCredSource,
   421  			Scopes:           []string{"https://www.googleapis.com/auth/devstorage.full_control"},
   422  			ServiceAccountImpersonationLifetimeSeconds: 10000,
   423  		},
   424  		expectedImpersonationBody: "{\"lifetime\":\"10000s\",\"scope\":[\"https://www.googleapis.com/auth/devstorage.full_control\"]}",
   425  		expectedMetricsHeader:     getExpectedMetricsHeader("file", true, true),
   426  	},
   427  }
   428  
   429  func TestImpersonation(t *testing.T) {
   430  	for _, tt := range impersonationTests {
   431  		t.Run(tt.name, func(t *testing.T) {
   432  			testImpersonateConfig := tt.config
   433  			impersonateServer := createImpersonationServer("/", "Bearer Sample.Access.Token", tt.expectedImpersonationBody, baseImpersonateCredsRespBody, t)
   434  			defer impersonateServer.Close()
   435  			testImpersonateConfig.ServiceAccountImpersonationURL = impersonateServer.URL
   436  
   437  			targetServer := createTargetServer(tt.expectedMetricsHeader, t)
   438  			defer targetServer.Close()
   439  			testImpersonateConfig.TokenURL = targetServer.URL
   440  
   441  			ourTS, err := testImpersonateConfig.tokenSource(context.Background(), "http")
   442  			if err != nil {
   443  				t.Fatalf("Failed to create TokenSource: %v", err)
   444  			}
   445  
   446  			oldNow := now
   447  			defer func() { now = oldNow }()
   448  			now = testNow
   449  
   450  			tok, err := ourTS.Token()
   451  			if err != nil {
   452  				t.Fatalf("Unexpected error: %e", err)
   453  			}
   454  			if got, want := tok.AccessToken, "Second.Access.Token"; got != want {
   455  				t.Errorf("Unexpected access token: got %v, but wanted %v", got, want)
   456  			}
   457  			if got, want := tok.TokenType, "Bearer"; got != want {
   458  				t.Errorf("Unexpected TokenType: got %v, but wanted %v", got, want)
   459  			}
   460  		})
   461  	}
   462  }
   463  
   464  var newTokenTests = []struct {
   465  	name   string
   466  	config Config
   467  }{
   468  	{
   469  		name: "Missing Audience",
   470  		config: Config{
   471  			SubjectTokenType: "urn:ietf:params:oauth:token-type:jwt",
   472  			TokenInfoURL:     "http://localhost:8080/v1/tokeninfo",
   473  			ClientSecret:     "notsosecret",
   474  			ClientID:         "rbrgnognrhongo3bi4gb9ghg9g",
   475  			CredentialSource: &testBaseCredSource,
   476  			Scopes:           []string{"https://www.googleapis.com/auth/devstorage.full_control"},
   477  			ServiceAccountImpersonationLifetimeSeconds: 10000,
   478  		},
   479  	},
   480  	{
   481  		name: "Missing Subject Token Type",
   482  		config: Config{
   483  			Audience:         "32555940559.apps.googleusercontent.com",
   484  			TokenInfoURL:     "http://localhost:8080/v1/tokeninfo",
   485  			ClientSecret:     "notsosecret",
   486  			ClientID:         "rbrgnognrhongo3bi4gb9ghg9g",
   487  			CredentialSource: &testBaseCredSource,
   488  			Scopes:           []string{"https://www.googleapis.com/auth/devstorage.full_control"},
   489  			ServiceAccountImpersonationLifetimeSeconds: 10000,
   490  		},
   491  	},
   492  	{
   493  		name: "No Cred Source",
   494  		config: Config{
   495  			Audience:         "32555940559.apps.googleusercontent.com",
   496  			SubjectTokenType: "urn:ietf:params:oauth:token-type:jwt",
   497  			TokenInfoURL:     "http://localhost:8080/v1/tokeninfo",
   498  			ClientSecret:     "notsosecret",
   499  			ClientID:         "rbrgnognrhongo3bi4gb9ghg9g",
   500  			Scopes:           []string{"https://www.googleapis.com/auth/devstorage.full_control"},
   501  			ServiceAccountImpersonationLifetimeSeconds: 10000,
   502  		},
   503  	},
   504  	{
   505  		name: "Cred Source and Supplier",
   506  		config: Config{
   507  			Audience:                       "32555940559.apps.googleusercontent.com",
   508  			SubjectTokenType:               "urn:ietf:params:oauth:token-type:jwt",
   509  			TokenInfoURL:                   "http://localhost:8080/v1/tokeninfo",
   510  			CredentialSource:               &testBaseCredSource,
   511  			AwsSecurityCredentialsSupplier: testAwsSupplier{},
   512  			ClientSecret:                   "notsosecret",
   513  			ClientID:                       "rbrgnognrhongo3bi4gb9ghg9g",
   514  			Scopes:                         []string{"https://www.googleapis.com/auth/devstorage.full_control"},
   515  			ServiceAccountImpersonationLifetimeSeconds: 10000,
   516  		},
   517  	},
   518  }
   519  
   520  func TestNewToken(t *testing.T) {
   521  	for _, tt := range newTokenTests {
   522  		t.Run(tt.name, func(t *testing.T) {
   523  			testConfig := tt.config
   524  
   525  			_, err := NewTokenSource(context.Background(), testConfig)
   526  			if err == nil {
   527  				t.Fatalf("expected error when calling NewToken()")
   528  			}
   529  		})
   530  	}
   531  }
   532  
   533  func TestConfig_TokenURL(t *testing.T) {
   534  	tests := []struct {
   535  		tokenURL       string
   536  		universeDomain string
   537  		want           string
   538  	}{
   539  		{
   540  			tokenURL:       "https://sts.googleapis.com/v1/token",
   541  			universeDomain: "",
   542  			want:           "https://sts.googleapis.com/v1/token",
   543  		},
   544  		{
   545  			tokenURL:       "",
   546  			universeDomain: "",
   547  			want:           "https://sts.googleapis.com/v1/token",
   548  		},
   549  		{
   550  			tokenURL:       "",
   551  			universeDomain: "googleapis.com",
   552  			want:           "https://sts.googleapis.com/v1/token",
   553  		},
   554  		{
   555  			tokenURL:       "",
   556  			universeDomain: "example.com",
   557  			want:           "https://sts.example.com/v1/token",
   558  		},
   559  	}
   560  	for _, tt := range tests {
   561  		config := &Config{
   562  			Audience:         "//iam.googleapis.com/locations/eu/workforcePools/pool-id/providers/provider-id",
   563  			SubjectTokenType: "urn:ietf:params:oauth:token-type:id_token",
   564  			CredentialSource: &testBaseCredSource,
   565  			Scopes:           []string{"https://www.googleapis.com/auth/devstorage.full_control"},
   566  		}
   567  		config.TokenURL = tt.tokenURL
   568  		config.UniverseDomain = tt.universeDomain
   569  		config.parse(context.Background())
   570  		if got := config.TokenURL; got != tt.want {
   571  			t.Errorf("got %q, want %q", got, tt.want)
   572  		}
   573  	}
   574  }
   575  

View as plain text