golang.org/x/oauth2@v0.18.0/google/internal/stsexchange/sts_exchange_test.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 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", //TODO: Make sure audience is correct in this test (might be mismatched) 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 /* Lean test specifically for options, as the other features are tested earlier. */ 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 // Send a proper reply so that no other errors crop up. 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 }