github.com/xhghs/rclone@v1.51.1-0.20200430155106-e186a28cced8/lib/jwtutil/jwtutil.go (about)

     1  package jwtutil
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/rand"
     6  	"crypto/rsa"
     7  	"encoding/hex"
     8  	"encoding/json"
     9  	"net/http"
    10  	"time"
    11  
    12  	"github.com/pkg/errors"
    13  	"github.com/rclone/rclone/fs/config/configmap"
    14  	"github.com/rclone/rclone/lib/oauthutil"
    15  
    16  	"golang.org/x/oauth2"
    17  	"golang.org/x/oauth2/jws"
    18  )
    19  
    20  // RandomHex creates a random string of the given length
    21  func RandomHex(n int) (string, error) {
    22  	bytes := make([]byte, n)
    23  	if _, err := rand.Read(bytes); err != nil {
    24  		return "", err
    25  	}
    26  	return hex.EncodeToString(bytes), nil
    27  }
    28  
    29  // Config configures rclone using JWT
    30  func Config(id, name string, claims *jws.ClaimSet, header *jws.Header, queryParams map[string]string, privateKey *rsa.PrivateKey, m configmap.Mapper, client *http.Client) (err error) {
    31  	payload, err := jws.Encode(header, claims, privateKey)
    32  	if err != nil {
    33  		return errors.Wrap(err, "jwtutil: failed to encode payload")
    34  	}
    35  	req, err := http.NewRequest("POST", claims.Aud, nil)
    36  	if err != nil {
    37  		return errors.Wrap(err, "jwtutil: failed to create new request")
    38  	}
    39  	q := req.URL.Query()
    40  	q.Add("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer")
    41  	q.Add("assertion", payload)
    42  	for key, value := range queryParams {
    43  		q.Add(key, value)
    44  	}
    45  	queryString := q.Encode()
    46  
    47  	req, err = http.NewRequest("POST", claims.Aud, bytes.NewBuffer([]byte(queryString)))
    48  	if err != nil {
    49  		return errors.Wrap(err, "jwtutil: failed to create new request")
    50  	}
    51  	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    52  
    53  	resp, err := client.Do(req)
    54  	if err != nil {
    55  		return errors.Wrap(err, "jwtutil: failed making auth request")
    56  	}
    57  	if resp.StatusCode != 200 {
    58  		return errors.Wrap(err, "jwtutil: failed making auth request")
    59  	}
    60  	defer func() {
    61  		deferedErr := resp.Body.Close()
    62  		if deferedErr != nil {
    63  			err = errors.Wrap(err, "jwtutil: failed to close resp.Body")
    64  		}
    65  	}()
    66  
    67  	result := &response{}
    68  	err = json.NewDecoder(resp.Body).Decode(result)
    69  	if err != nil || result.AccessToken == "" {
    70  		return errors.Wrap(err, "jwtutil: failed to get token")
    71  	}
    72  	token := &oauth2.Token{
    73  		AccessToken: result.AccessToken,
    74  		TokenType:   result.TokenType,
    75  	}
    76  	e := result.ExpiresIn
    77  	if e != 0 {
    78  		token.Expiry = time.Now().Add(time.Duration(e) * time.Second)
    79  	}
    80  	return oauthutil.PutToken(name, m, token, true)
    81  }
    82  
    83  type response struct {
    84  	AccessToken string `json:"access_token"`
    85  	TokenType   string `json:"token_type"`
    86  	ExpiresIn   int    `json:"expires_in"`
    87  }