...
1
2
3
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
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
33
34 type ImpersonateTokenSource struct {
35
36
37 Ctx context.Context
38
39
40 Ts oauth2.TokenSource
41
42
43
44 URL string
45
46 Scopes []string
47
48
49
50 Delegates []string
51
52
53 TokenLifetimeSeconds int
54 }
55
56
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