...

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

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

     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 stsexchange
     6  
     7  import (
     8  	"context"
     9  	"encoding/json"
    10  	"fmt"
    11  	"io"
    12  	"io/ioutil"
    13  	"net/http"
    14  	"net/url"
    15  	"strconv"
    16  	"strings"
    17  
    18  	"golang.org/x/oauth2"
    19  )
    20  
    21  func defaultHeader() http.Header {
    22  	header := make(http.Header)
    23  	header.Add("Content-Type", "application/x-www-form-urlencoded")
    24  	return header
    25  }
    26  
    27  // ExchangeToken performs an oauth2 token exchange with the provided endpoint.
    28  // The first 4 fields are all mandatory.  headers can be used to pass additional
    29  // headers beyond the bare minimum required by the token exchange.  options can
    30  // be used to pass additional JSON-structured options to the remote server.
    31  func ExchangeToken(ctx context.Context, endpoint string, request *TokenExchangeRequest, authentication ClientAuthentication, headers http.Header, options map[string]interface{}) (*Response, error) {
    32  	data := url.Values{}
    33  	data.Set("audience", request.Audience)
    34  	data.Set("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange")
    35  	data.Set("requested_token_type", "urn:ietf:params:oauth:token-type:access_token")
    36  	data.Set("subject_token_type", request.SubjectTokenType)
    37  	data.Set("subject_token", request.SubjectToken)
    38  	data.Set("scope", strings.Join(request.Scope, " "))
    39  	if options != nil {
    40  		opts, err := json.Marshal(options)
    41  		if err != nil {
    42  			return nil, fmt.Errorf("oauth2/google: failed to marshal additional options: %v", err)
    43  		}
    44  		data.Set("options", string(opts))
    45  	}
    46  
    47  	return makeRequest(ctx, endpoint, data, authentication, headers)
    48  }
    49  
    50  func RefreshAccessToken(ctx context.Context, endpoint string, refreshToken string, authentication ClientAuthentication, headers http.Header) (*Response, error) {
    51  	data := url.Values{}
    52  	data.Set("grant_type", "refresh_token")
    53  	data.Set("refresh_token", refreshToken)
    54  
    55  	return makeRequest(ctx, endpoint, data, authentication, headers)
    56  }
    57  
    58  func makeRequest(ctx context.Context, endpoint string, data url.Values, authentication ClientAuthentication, headers http.Header) (*Response, error) {
    59  	if headers == nil {
    60  		headers = defaultHeader()
    61  	}
    62  	client := oauth2.NewClient(ctx, nil)
    63  	authentication.InjectAuthentication(data, headers)
    64  	encodedData := data.Encode()
    65  
    66  	req, err := http.NewRequest("POST", endpoint, strings.NewReader(encodedData))
    67  	if err != nil {
    68  		return nil, fmt.Errorf("oauth2/google: failed to properly build http request: %v", err)
    69  	}
    70  	req = req.WithContext(ctx)
    71  	for key, list := range headers {
    72  		for _, val := range list {
    73  			req.Header.Add(key, val)
    74  		}
    75  	}
    76  	req.Header.Add("Content-Length", strconv.Itoa(len(encodedData)))
    77  
    78  	resp, err := client.Do(req)
    79  
    80  	if err != nil {
    81  		return nil, fmt.Errorf("oauth2/google: invalid response from Secure Token Server: %v", err)
    82  	}
    83  	defer resp.Body.Close()
    84  
    85  	body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20))
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  	if c := resp.StatusCode; c < 200 || c > 299 {
    90  		return nil, fmt.Errorf("oauth2/google: status code %d: %s", c, body)
    91  	}
    92  	var stsResp Response
    93  	err = json.Unmarshal(body, &stsResp)
    94  	if err != nil {
    95  		return nil, fmt.Errorf("oauth2/google: failed to unmarshal response body from Secure Token Server: %v", err)
    96  
    97  	}
    98  
    99  	return &stsResp, nil
   100  }
   101  
   102  // TokenExchangeRequest contains fields necessary to make an oauth2 token exchange.
   103  type TokenExchangeRequest struct {
   104  	ActingParty struct {
   105  		ActorToken     string
   106  		ActorTokenType string
   107  	}
   108  	GrantType          string
   109  	Resource           string
   110  	Audience           string
   111  	Scope              []string
   112  	RequestedTokenType string
   113  	SubjectToken       string
   114  	SubjectTokenType   string
   115  }
   116  
   117  // Response is used to decode the remote server response during an oauth2 token exchange.
   118  type Response struct {
   119  	AccessToken     string `json:"access_token"`
   120  	IssuedTokenType string `json:"issued_token_type"`
   121  	TokenType       string `json:"token_type"`
   122  	ExpiresIn       int    `json:"expires_in"`
   123  	Scope           string `json:"scope"`
   124  	RefreshToken    string `json:"refresh_token"`
   125  }
   126  

View as plain text