golang.org/x/oauth2@v0.18.0/google/internal/externalaccountauthorizeduser/externalaccountauthorizeduser.go (about) 1 // Copyright 2023 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 externalaccountauthorizeduser 6 7 import ( 8 "context" 9 "errors" 10 "time" 11 12 "golang.org/x/oauth2" 13 "golang.org/x/oauth2/google/internal/stsexchange" 14 ) 15 16 // now aliases time.Now for testing. 17 var now = func() time.Time { 18 return time.Now().UTC() 19 } 20 21 var tokenValid = func(token oauth2.Token) bool { 22 return token.Valid() 23 } 24 25 type Config struct { 26 // Audience is the Secure Token Service (STS) audience which contains the resource name for the workforce pool and 27 // the provider identifier in that pool. 28 Audience string 29 // RefreshToken is the optional OAuth 2.0 refresh token. If specified, credentials can be refreshed. 30 RefreshToken string 31 // TokenURL is the optional STS token exchange endpoint for refresh. Must be specified for refresh, can be left as 32 // None if the token can not be refreshed. 33 TokenURL string 34 // TokenInfoURL is the optional STS endpoint URL for token introspection. 35 TokenInfoURL string 36 // ClientID is only required in conjunction with ClientSecret, as described above. 37 ClientID string 38 // ClientSecret is currently only required if token_info endpoint also needs to be called with the generated GCP 39 // access token. When provided, STS will be called with additional basic authentication using client_id as username 40 // and client_secret as password. 41 ClientSecret string 42 // Token is the OAuth2.0 access token. Can be nil if refresh information is provided. 43 Token string 44 // Expiry is the optional expiration datetime of the OAuth 2.0 access token. 45 Expiry time.Time 46 // RevokeURL is the optional STS endpoint URL for revoking tokens. 47 RevokeURL string 48 // QuotaProjectID is the optional project ID used for quota and billing. This project may be different from the 49 // project used to create the credentials. 50 QuotaProjectID string 51 Scopes []string 52 } 53 54 func (c *Config) canRefresh() bool { 55 return c.ClientID != "" && c.ClientSecret != "" && c.RefreshToken != "" && c.TokenURL != "" 56 } 57 58 func (c *Config) TokenSource(ctx context.Context) (oauth2.TokenSource, error) { 59 var token oauth2.Token 60 if c.Token != "" && !c.Expiry.IsZero() { 61 token = oauth2.Token{ 62 AccessToken: c.Token, 63 Expiry: c.Expiry, 64 TokenType: "Bearer", 65 } 66 } 67 if !tokenValid(token) && !c.canRefresh() { 68 return nil, errors.New("oauth2/google: Token should be created with fields to make it valid (`token` and `expiry`), or fields to allow it to refresh (`refresh_token`, `token_url`, `client_id`, `client_secret`).") 69 } 70 71 ts := tokenSource{ 72 ctx: ctx, 73 conf: c, 74 } 75 76 return oauth2.ReuseTokenSource(&token, ts), nil 77 } 78 79 type tokenSource struct { 80 ctx context.Context 81 conf *Config 82 } 83 84 func (ts tokenSource) Token() (*oauth2.Token, error) { 85 conf := ts.conf 86 if !conf.canRefresh() { 87 return nil, errors.New("oauth2/google: The credentials do not contain the necessary fields need to refresh the access token. You must specify refresh_token, token_url, client_id, and client_secret.") 88 } 89 90 clientAuth := stsexchange.ClientAuthentication{ 91 AuthStyle: oauth2.AuthStyleInHeader, 92 ClientID: conf.ClientID, 93 ClientSecret: conf.ClientSecret, 94 } 95 96 stsResponse, err := stsexchange.RefreshAccessToken(ts.ctx, conf.TokenURL, conf.RefreshToken, clientAuth, nil) 97 if err != nil { 98 return nil, err 99 } 100 if stsResponse.ExpiresIn < 0 { 101 return nil, errors.New("oauth2/google: got invalid expiry from security token service") 102 } 103 104 if stsResponse.RefreshToken != "" { 105 conf.RefreshToken = stsResponse.RefreshToken 106 } 107 108 token := &oauth2.Token{ 109 AccessToken: stsResponse.AccessToken, 110 Expiry: now().Add(time.Duration(stsResponse.ExpiresIn) * time.Second), 111 TokenType: "Bearer", 112 } 113 return token, nil 114 }