...

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

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

     1  // Copyright 2021 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 impersonate
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"encoding/json"
    11  	"fmt"
    12  	"io"
    13  	"io/ioutil"
    14  	"net/http"
    15  	"time"
    16  
    17  	"golang.org/x/oauth2"
    18  )
    19  
    20  // generateAccesstokenReq is used for service account impersonation
    21  type generateAccessTokenReq struct {
    22  	Delegates []string `json:"delegates,omitempty"`
    23  	Lifetime  string   `json:"lifetime,omitempty"`
    24  	Scope     []string `json:"scope,omitempty"`
    25  }
    26  
    27  type impersonateTokenResponse struct {
    28  	AccessToken string `json:"accessToken"`
    29  	ExpireTime  string `json:"expireTime"`
    30  }
    31  
    32  // ImpersonateTokenSource uses a source credential, stored in Ts, to request an access token to the provided URL.
    33  // Scopes can be defined when the access token is requested.
    34  type ImpersonateTokenSource struct {
    35  	// Ctx is the execution context of the impersonation process
    36  	// used to perform http call to the URL. Required
    37  	Ctx context.Context
    38  	// Ts is the source credential used to generate a token on the
    39  	// impersonated service account. Required.
    40  	Ts oauth2.TokenSource
    41  
    42  	// URL is the endpoint to call to generate a token
    43  	// on behalf the service account. Required.
    44  	URL string
    45  	// Scopes that the impersonated credential should have. Required.
    46  	Scopes []string
    47  	// Delegates are the service account email addresses in a delegation chain.
    48  	// Each service account must be granted roles/iam.serviceAccountTokenCreator
    49  	// on the next service account in the chain. Optional.
    50  	Delegates []string
    51  	// TokenLifetimeSeconds is the number of seconds the impersonation token will
    52  	// be valid for.
    53  	TokenLifetimeSeconds int
    54  }
    55  
    56  // Token performs the exchange to get a temporary service account token to allow access to GCP.
    57  func (its ImpersonateTokenSource) Token() (*oauth2.Token, error) {
    58  	lifetimeString := "3600s"
    59  	if its.TokenLifetimeSeconds != 0 {
    60  		lifetimeString = fmt.Sprintf("%ds", its.TokenLifetimeSeconds)
    61  	}
    62  	reqBody := generateAccessTokenReq{
    63  		Lifetime:  lifetimeString,
    64  		Scope:     its.Scopes,
    65  		Delegates: its.Delegates,
    66  	}
    67  	b, err := json.Marshal(reqBody)
    68  	if err != nil {
    69  		return nil, fmt.Errorf("oauth2/google: unable to marshal request: %v", err)
    70  	}
    71  	client := oauth2.NewClient(its.Ctx, its.Ts)
    72  	req, err := http.NewRequest("POST", its.URL, bytes.NewReader(b))
    73  	if err != nil {
    74  		return nil, fmt.Errorf("oauth2/google: unable to create impersonation request: %v", err)
    75  	}
    76  	req = req.WithContext(its.Ctx)
    77  	req.Header.Set("Content-Type", "application/json")
    78  
    79  	resp, err := client.Do(req)
    80  	if err != nil {
    81  		return nil, fmt.Errorf("oauth2/google: unable to generate access token: %v", err)
    82  	}
    83  	defer resp.Body.Close()
    84  	body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20))
    85  	if err != nil {
    86  		return nil, fmt.Errorf("oauth2/google: unable to read body: %v", err)
    87  	}
    88  	if c := resp.StatusCode; c < 200 || c > 299 {
    89  		return nil, fmt.Errorf("oauth2/google: status code %d: %s", c, body)
    90  	}
    91  
    92  	var accessTokenResp impersonateTokenResponse
    93  	if err := json.Unmarshal(body, &accessTokenResp); err != nil {
    94  		return nil, fmt.Errorf("oauth2/google: unable to parse response: %v", err)
    95  	}
    96  	expiry, err := time.Parse(time.RFC3339, accessTokenResp.ExpireTime)
    97  	if err != nil {
    98  		return nil, fmt.Errorf("oauth2/google: unable to parse expiry: %v", err)
    99  	}
   100  	return &oauth2.Token{
   101  		AccessToken: accessTokenResp.AccessToken,
   102  		Expiry:      expiry,
   103  		TokenType:   "Bearer",
   104  	}, nil
   105  }
   106  

View as plain text