github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/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 "io" 10 "io/ioutil" 11 "net/http" 12 "strings" 13 "time" 14 15 "github.com/pkg/errors" 16 "github.com/rclone/rclone/fs" 17 "github.com/rclone/rclone/fs/config/configmap" 18 "github.com/rclone/rclone/lib/oauthutil" 19 20 "golang.org/x/oauth2" 21 "golang.org/x/oauth2/jws" 22 ) 23 24 // RandomHex creates a random string of the given length 25 func RandomHex(n int) (string, error) { 26 bytes := make([]byte, n) 27 if _, err := rand.Read(bytes); err != nil { 28 return "", err 29 } 30 return hex.EncodeToString(bytes), nil 31 } 32 33 // Config configures rclone using JWT 34 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) { 35 payload, err := jws.Encode(header, claims, privateKey) 36 if err != nil { 37 return errors.Wrap(err, "jwtutil: failed to encode payload") 38 } 39 req, err := http.NewRequest("POST", claims.Aud, nil) 40 if err != nil { 41 return errors.Wrap(err, "jwtutil: failed to create new request") 42 } 43 q := req.URL.Query() 44 q.Add("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer") 45 q.Add("assertion", payload) 46 for key, value := range queryParams { 47 q.Add(key, value) 48 } 49 queryString := q.Encode() 50 51 req, err = http.NewRequest("POST", claims.Aud, bytes.NewBuffer([]byte(queryString))) 52 if err != nil { 53 return errors.Wrap(err, "jwtutil: failed to create new request") 54 } 55 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 56 57 resp, err := client.Do(req) 58 if err != nil { 59 return errors.Wrap(err, "jwtutil: failed making auth request") 60 } 61 62 s, err := bodyToString(resp.Body) 63 if err != nil { 64 fs.Debugf(nil, "jwtutil: failed to get response body") 65 } 66 if resp.StatusCode != 200 { 67 err = errors.New(resp.Status) 68 return errors.Wrap(err, "jwtutil: failed making auth request") 69 } 70 defer func() { 71 deferedErr := resp.Body.Close() 72 if deferedErr != nil { 73 err = errors.Wrap(err, "jwtutil: failed to close resp.Body") 74 } 75 }() 76 77 result := &response{} 78 err = json.NewDecoder(strings.NewReader(s)).Decode(result) 79 if result.AccessToken == "" && err == nil { 80 err = errors.New("No AccessToken in Response") 81 } 82 if err != nil { 83 return errors.Wrap(err, "jwtutil: failed to get token") 84 } 85 token := &oauth2.Token{ 86 AccessToken: result.AccessToken, 87 TokenType: result.TokenType, 88 } 89 e := result.ExpiresIn 90 if e != 0 { 91 token.Expiry = time.Now().Add(time.Duration(e) * time.Second) 92 } 93 return oauthutil.PutToken(name, m, token, true) 94 } 95 96 func bodyToString(responseBody io.Reader) (bodyString string, err error) { 97 bodyBytes, err := ioutil.ReadAll(responseBody) 98 if err != nil { 99 return "", err 100 } 101 bodyString = string(bodyBytes) 102 fs.Debugf(nil, "jwtutil: Response Body: "+bodyString) 103 return bodyString, nil 104 } 105 106 type response struct { 107 AccessToken string `json:"access_token"` 108 TokenType string `json:"token_type"` 109 ExpiresIn int `json:"expires_in"` 110 }