...

Source file src/golang.org/x/oauth2/google/internal/externalaccountauthorizeduser/externalaccountauthorizeduser.go

Documentation: golang.org/x/oauth2/google/internal/externalaccountauthorizeduser

     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  }
   115  

View as plain text