...

Source file src/cloud.google.com/go/auth/credentials/internal/externalaccountuser/externalaccountuser.go

Documentation: cloud.google.com/go/auth/credentials/internal/externalaccountuser

     1  // Copyright 2023 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package externalaccountuser
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"net/http"
    21  	"time"
    22  
    23  	"cloud.google.com/go/auth"
    24  	"cloud.google.com/go/auth/credentials/internal/stsexchange"
    25  	"cloud.google.com/go/auth/internal"
    26  )
    27  
    28  // Options stores the configuration for fetching tokens with external authorized
    29  // user credentials.
    30  type Options struct {
    31  	// Audience is the Secure Token Service (STS) audience which contains the
    32  	// resource name for the workforce pool and the provider identifier in that
    33  	// pool.
    34  	Audience string
    35  	// RefreshToken is the OAuth 2.0 refresh token.
    36  	RefreshToken string
    37  	// TokenURL is the STS token exchange endpoint for refresh.
    38  	TokenURL string
    39  	// TokenInfoURL is the STS endpoint URL for token introspection. Optional.
    40  	TokenInfoURL string
    41  	// ClientID is only required in conjunction with ClientSecret, as described
    42  	// below.
    43  	ClientID string
    44  	// ClientSecret is currently only required if token_info endpoint also needs
    45  	// to be called with the generated a cloud access token. When provided, STS
    46  	// will be called with additional basic authentication using client_id as
    47  	// username and client_secret as password.
    48  	ClientSecret string
    49  	// Scopes contains the desired scopes for the returned access token.
    50  	Scopes []string
    51  
    52  	// Client for token request.
    53  	Client *http.Client
    54  }
    55  
    56  func (c *Options) validate() bool {
    57  	return c.ClientID != "" && c.ClientSecret != "" && c.RefreshToken != "" && c.TokenURL != ""
    58  }
    59  
    60  // NewTokenProvider returns a [cloud.google.com/go/auth.TokenProvider]
    61  // configured with the provided options.
    62  func NewTokenProvider(opts *Options) (auth.TokenProvider, error) {
    63  	if !opts.validate() {
    64  		return nil, errors.New("credentials: invalid external_account_authorized_user configuration")
    65  	}
    66  
    67  	tp := &tokenProvider{
    68  		o: opts,
    69  	}
    70  	return auth.NewCachedTokenProvider(tp, nil), nil
    71  }
    72  
    73  type tokenProvider struct {
    74  	o *Options
    75  }
    76  
    77  func (tp *tokenProvider) Token(ctx context.Context) (*auth.Token, error) {
    78  	opts := tp.o
    79  
    80  	clientAuth := stsexchange.ClientAuthentication{
    81  		AuthStyle:    auth.StyleInHeader,
    82  		ClientID:     opts.ClientID,
    83  		ClientSecret: opts.ClientSecret,
    84  	}
    85  	headers := make(http.Header)
    86  	headers.Set("Content-Type", "application/x-www-form-urlencoded")
    87  	stsResponse, err := stsexchange.RefreshAccessToken(ctx, &stsexchange.Options{
    88  		Client:         opts.Client,
    89  		Endpoint:       opts.TokenURL,
    90  		RefreshToken:   opts.RefreshToken,
    91  		Authentication: clientAuth,
    92  		Headers:        headers,
    93  	})
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	if stsResponse.ExpiresIn < 0 {
    98  		return nil, errors.New("credentials: invalid expiry from security token service")
    99  	}
   100  
   101  	// guarded by the wrapping with CachedTokenProvider
   102  	if stsResponse.RefreshToken != "" {
   103  		opts.RefreshToken = stsResponse.RefreshToken
   104  	}
   105  	return &auth.Token{
   106  		Value:  stsResponse.AccessToken,
   107  		Expiry: time.Now().UTC().Add(time.Duration(stsResponse.ExpiresIn) * time.Second),
   108  		Type:   internal.TokenTypeBearer,
   109  	}, nil
   110  }
   111  

View as plain text