1
2
3
4
5 package stsexchange
6
7 import (
8 "context"
9 "encoding/json"
10 "io/ioutil"
11 "net/http"
12 "net/http/httptest"
13 "net/url"
14 "testing"
15
16 "golang.org/x/oauth2"
17 )
18
19 var auth = ClientAuthentication{
20 AuthStyle: oauth2.AuthStyleInHeader,
21 ClientID: clientID,
22 ClientSecret: clientSecret,
23 }
24
25 var exchangeTokenRequest = TokenExchangeRequest{
26 ActingParty: struct {
27 ActorToken string
28 ActorTokenType string
29 }{},
30 GrantType: "urn:ietf:params:oauth:grant-type:token-exchange",
31 Resource: "",
32 Audience: "32555940559.apps.googleusercontent.com",
33 Scope: []string{"https://www.googleapis.com/auth/devstorage.full_control"},
34 RequestedTokenType: "urn:ietf:params:oauth:token-type:access_token",
35 SubjectToken: "Sample.Subject.Token",
36 SubjectTokenType: "urn:ietf:params:oauth:token-type:jwt",
37 }
38
39 var exchangeRequestBody = "audience=32555940559.apps.googleusercontent.com&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control&subject_token=Sample.Subject.Token&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Ajwt"
40 var exchangeResponseBody = `{"access_token":"Sample.Access.Token","issued_token_type":"urn:ietf:params:oauth:token-type:access_token","token_type":"Bearer","expires_in":3600,"scope":"https://www.googleapis.com/auth/cloud-platform"}`
41 var expectedExchangeToken = Response{
42 AccessToken: "Sample.Access.Token",
43 IssuedTokenType: "urn:ietf:params:oauth:token-type:access_token",
44 TokenType: "Bearer",
45 ExpiresIn: 3600,
46 Scope: "https://www.googleapis.com/auth/cloud-platform",
47 RefreshToken: "",
48 }
49
50 var refreshToken = "ReFrEsHtOkEn"
51 var refreshRequestBody = "grant_type=refresh_token&refresh_token=" + refreshToken
52 var refreshResponseBody = `{"access_token":"Sample.Access.Token","issued_token_type":"urn:ietf:params:oauth:token-type:access_token","token_type":"Bearer","expires_in":3600,"scope":"https://www.googleapis.com/auth/cloud-platform","refresh_token":"REFRESHED_REFRESH"}`
53 var expectedRefreshResponse = Response{
54 AccessToken: "Sample.Access.Token",
55 IssuedTokenType: "urn:ietf:params:oauth:token-type:access_token",
56 TokenType: "Bearer",
57 ExpiresIn: 3600,
58 Scope: "https://www.googleapis.com/auth/cloud-platform",
59 RefreshToken: "REFRESHED_REFRESH",
60 }
61
62 func TestExchangeToken(t *testing.T) {
63 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
64 if r.Method != "POST" {
65 t.Errorf("Unexpected request method, %v is found", r.Method)
66 }
67 if r.URL.String() != "/" {
68 t.Errorf("Unexpected request URL, %v is found", r.URL)
69 }
70 if got, want := r.Header.Get("Authorization"), "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ="; got != want {
71 t.Errorf("Unexpected authorization header, got %v, want %v", got, want)
72 }
73 if got, want := r.Header.Get("Content-Type"), "application/x-www-form-urlencoded"; got != want {
74 t.Errorf("Unexpected Content-Type header, got %v, want %v", got, want)
75 }
76 body, err := ioutil.ReadAll(r.Body)
77 if err != nil {
78 t.Errorf("Failed reading request body: %v.", err)
79 }
80 if got, want := string(body), exchangeRequestBody; got != want {
81 t.Errorf("Unexpected exchange payload, got %v but want %v", got, want)
82 }
83 w.Header().Set("Content-Type", "application/json")
84 w.Write([]byte(exchangeResponseBody))
85 }))
86 defer ts.Close()
87
88 headers := http.Header{}
89 headers.Add("Content-Type", "application/x-www-form-urlencoded")
90
91 resp, err := ExchangeToken(context.Background(), ts.URL, &exchangeTokenRequest, auth, headers, nil)
92 if err != nil {
93 t.Fatalf("exchangeToken failed with error: %v", err)
94 }
95
96 if expectedExchangeToken != *resp {
97 t.Errorf("mismatched messages received by mock server. \nWant: \n%v\n\nGot:\n%v", expectedExchangeToken, *resp)
98 }
99
100 resp, err = ExchangeToken(context.Background(), ts.URL, &exchangeTokenRequest, auth, nil, nil)
101 if err != nil {
102 t.Fatalf("exchangeToken failed with error: %v", err)
103 }
104
105 if expectedExchangeToken != *resp {
106 t.Errorf("mismatched messages received by mock server. \nWant: \n%v\n\nGot:\n%v", expectedExchangeToken, *resp)
107 }
108 }
109
110 func TestExchangeToken_Err(t *testing.T) {
111 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
112 w.Header().Set("Content-Type", "application/json")
113 w.Write([]byte("what's wrong with this response?"))
114 }))
115 defer ts.Close()
116
117 headers := http.Header{}
118 headers.Add("Content-Type", "application/x-www-form-urlencoded")
119 _, err := ExchangeToken(context.Background(), ts.URL, &exchangeTokenRequest, auth, headers, nil)
120 if err == nil {
121 t.Errorf("Expected handled error; instead got nil.")
122 }
123 }
124
125
126 type testOpts struct {
127 First string `json:"first"`
128 Second string `json:"second"`
129 }
130
131 var optsValues = [][]string{{"foo", "bar"}, {"cat", "pan"}}
132
133 func TestExchangeToken_Opts(t *testing.T) {
134 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
135 body, err := ioutil.ReadAll(r.Body)
136 if err != nil {
137 t.Fatalf("Failed reading request body: %v.", err)
138 }
139 data, err := url.ParseQuery(string(body))
140 if err != nil {
141 t.Fatalf("Failed to parse request body: %v", err)
142 }
143 strOpts, ok := data["options"]
144 if !ok {
145 t.Errorf("Server didn't recieve an \"options\" field.")
146 } else if len(strOpts) < 1 {
147 t.Errorf("\"options\" field has length 0.")
148 }
149 var opts map[string]interface{}
150 err = json.Unmarshal([]byte(strOpts[0]), &opts)
151 if err != nil {
152 t.Fatalf("Couldn't parse received \"options\" field.")
153 }
154 if len(opts) < 2 {
155 t.Errorf("Too few options received.")
156 }
157
158 val, ok := opts["one"]
159 if !ok {
160 t.Errorf("Couldn't find first option parameter.")
161 } else {
162 tOpts1, ok := val.(map[string]interface{})
163 if !ok {
164 t.Errorf("Failed to assert the first option parameter as type testOpts.")
165 } else {
166 if got, want := tOpts1["first"].(string), optsValues[0][0]; got != want {
167 t.Errorf("First value in first options field is incorrect; got %v but want %v", got, want)
168 }
169 if got, want := tOpts1["second"].(string), optsValues[0][1]; got != want {
170 t.Errorf("Second value in first options field is incorrect; got %v but want %v", got, want)
171 }
172 }
173 }
174
175 val2, ok := opts["two"]
176 if !ok {
177 t.Errorf("Couldn't find second option parameter.")
178 } else {
179 tOpts2, ok := val2.(map[string]interface{})
180 if !ok {
181 t.Errorf("Failed to assert the second option parameter as type testOpts.")
182 } else {
183 if got, want := tOpts2["first"].(string), optsValues[1][0]; got != want {
184 t.Errorf("First value in second options field is incorrect; got %v but want %v", got, want)
185 }
186 if got, want := tOpts2["second"].(string), optsValues[1][1]; got != want {
187 t.Errorf("Second value in second options field is incorrect; got %v but want %v", got, want)
188 }
189 }
190 }
191
192
193 w.Header().Set("Content-Type", "application/json")
194 w.Write([]byte(exchangeResponseBody))
195
196 }))
197 defer ts.Close()
198 headers := http.Header{}
199 headers.Add("Content-Type", "application/x-www-form-urlencoded")
200
201 firstOption := testOpts{optsValues[0][0], optsValues[0][1]}
202 secondOption := testOpts{optsValues[1][0], optsValues[1][1]}
203 inputOpts := make(map[string]interface{})
204 inputOpts["one"] = firstOption
205 inputOpts["two"] = secondOption
206 ExchangeToken(context.Background(), ts.URL, &exchangeTokenRequest, auth, headers, inputOpts)
207 }
208
209 func TestRefreshToken(t *testing.T) {
210 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
211 if r.Method != "POST" {
212 t.Errorf("Unexpected request method, %v is found", r.Method)
213 }
214 if r.URL.String() != "/" {
215 t.Errorf("Unexpected request URL, %v is found", r.URL)
216 }
217 if got, want := r.Header.Get("Authorization"), "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ="; got != want {
218 t.Errorf("Unexpected authorization header, got %v, want %v", got, want)
219 }
220 if got, want := r.Header.Get("Content-Type"), "application/x-www-form-urlencoded"; got != want {
221 t.Errorf("Unexpected Content-Type header, got %v, want %v", got, want)
222 }
223 body, err := ioutil.ReadAll(r.Body)
224 if err != nil {
225 t.Errorf("Failed reading request body: %v.", err)
226 }
227 if got, want := string(body), refreshRequestBody; got != want {
228 t.Errorf("Unexpected exchange payload, got %v but want %v", got, want)
229 }
230 w.Header().Set("Content-Type", "application/json")
231 w.Write([]byte(refreshResponseBody))
232 }))
233 defer ts.Close()
234
235 headers := http.Header{}
236 headers.Add("Content-Type", "application/x-www-form-urlencoded")
237
238 resp, err := RefreshAccessToken(context.Background(), ts.URL, refreshToken, auth, headers)
239 if err != nil {
240 t.Fatalf("exchangeToken failed with error: %v", err)
241 }
242
243 if expectedRefreshResponse != *resp {
244 t.Errorf("mismatched messages received by mock server. \nWant: \n%v\n\nGot:\n%v", expectedRefreshResponse, *resp)
245 }
246
247 resp, err = RefreshAccessToken(context.Background(), ts.URL, refreshToken, auth, nil)
248 if err != nil {
249 t.Fatalf("exchangeToken failed with error: %v", err)
250 }
251
252 if expectedRefreshResponse != *resp {
253 t.Errorf("mismatched messages received by mock server. \nWant: \n%v\n\nGot:\n%v", expectedRefreshResponse, *resp)
254 }
255 }
256
257 func TestRefreshToken_Err(t *testing.T) {
258 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
259 w.Header().Set("Content-Type", "application/json")
260 w.Write([]byte("what's wrong with this response?"))
261 }))
262 defer ts.Close()
263
264 headers := http.Header{}
265 headers.Add("Content-Type", "application/x-www-form-urlencoded")
266
267 _, err := RefreshAccessToken(context.Background(), ts.URL, refreshToken, auth, headers)
268 if err == nil {
269 t.Errorf("Expected handled error; instead got nil.")
270 }
271 }
272
View as plain text