1
2
3
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
28
29
30
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
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
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