auth
auth/authcode_test.go
Test our getClientAuth helper, switch to table-based tests. Our getClientAuth helper was being tested implicitly when we tested our verifyClient helper, but let's test them separately. While we're at it, let's use table based tests instead of copy and paste. I noticed a lot of copy/paste errors while I was updating this, and the less test code we have and the easier we make it to test new edge cases, the better of we are.
| paddy@29 | 1 package auth |
| paddy@29 | 2 |
| paddy@29 | 3 import ( |
| paddy@111 | 4 "bytes" |
| paddy@111 | 5 "io/ioutil" |
| paddy@111 | 6 "net/http" |
| paddy@111 | 7 "net/http/httptest" |
| paddy@111 | 8 "net/url" |
| paddy@111 | 9 "strings" |
| paddy@29 | 10 "testing" |
| paddy@29 | 11 "time" |
| paddy@29 | 12 |
| paddy@107 | 13 "code.secondbit.org/uuid.hg" |
| paddy@29 | 14 ) |
| paddy@29 | 15 |
| paddy@87 | 16 var authCodeStores = []authorizationCodeStore{NewMemstore()} |
| paddy@29 | 17 |
| paddy@87 | 18 func compareAuthorizationCodes(authCode1, authCode2 AuthorizationCode) (success bool, field string, authCode1val, authCode2val interface{}) { |
| paddy@87 | 19 if authCode1.Code != authCode2.Code { |
| paddy@87 | 20 return false, "code", authCode1.Code, authCode2.Code |
| paddy@34 | 21 } |
| paddy@87 | 22 if !authCode1.Created.Equal(authCode2.Created) { |
| paddy@87 | 23 return false, "created", authCode1.Created, authCode2.Created |
| paddy@34 | 24 } |
| paddy@87 | 25 if authCode1.ExpiresIn != authCode2.ExpiresIn { |
| paddy@87 | 26 return false, "expires in", authCode1.ExpiresIn, authCode2.ExpiresIn |
| paddy@34 | 27 } |
| paddy@87 | 28 if !authCode1.ClientID.Equal(authCode2.ClientID) { |
| paddy@87 | 29 return false, "client ID", authCode1.ClientID, authCode2.ClientID |
| paddy@34 | 30 } |
| paddy@87 | 31 if authCode1.Scope != authCode2.Scope { |
| paddy@87 | 32 return false, "scope", authCode1.Scope, authCode2.Scope |
| paddy@34 | 33 } |
| paddy@87 | 34 if authCode1.RedirectURI != authCode2.RedirectURI { |
| paddy@87 | 35 return false, "redirect URI", authCode1.RedirectURI, authCode2.RedirectURI |
| paddy@34 | 36 } |
| paddy@87 | 37 if authCode1.State != authCode2.State { |
| paddy@87 | 38 return false, "state", authCode1.State, authCode2.State |
| paddy@34 | 39 } |
| paddy@111 | 40 if !authCode1.ProfileID.Equal(authCode2.ProfileID) { |
| paddy@111 | 41 return false, "profile ID", authCode1.ProfileID, authCode2.ProfileID |
| paddy@111 | 42 } |
| paddy@111 | 43 if authCode1.Used != authCode2.Used { |
| paddy@111 | 44 return false, "used", authCode1.Used, authCode2.Used |
| paddy@111 | 45 } |
| paddy@34 | 46 return true, "", nil, nil |
| paddy@34 | 47 } |
| paddy@34 | 48 |
| paddy@111 | 49 func TestAuthorizationCodeStore(t *testing.T) { |
| paddy@36 | 50 t.Parallel() |
| paddy@87 | 51 authCode := AuthorizationCode{ |
| paddy@29 | 52 Code: "code", |
| paddy@29 | 53 Created: time.Now(), |
| paddy@29 | 54 ExpiresIn: 180, |
| paddy@29 | 55 ClientID: uuid.NewID(), |
| paddy@29 | 56 Scope: "scope", |
| paddy@29 | 57 RedirectURI: "redirectURI", |
| paddy@29 | 58 State: "state", |
| paddy@29 | 59 } |
| paddy@87 | 60 for _, store := range authCodeStores { |
| paddy@116 | 61 context := Context{authCodes: store} |
| paddy@116 | 62 err := context.SaveAuthorizationCode(authCode) |
| paddy@29 | 63 if err != nil { |
| paddy@87 | 64 t.Errorf("Error saving auth code to %T: %s", store, err) |
| paddy@34 | 65 } |
| paddy@116 | 66 err = context.SaveAuthorizationCode(authCode) |
| paddy@87 | 67 if err != ErrAuthorizationCodeAlreadyExists { |
| paddy@87 | 68 t.Errorf("Expected ErrAuthorizationCodeAlreadyExists from %T, got %+v", store, err) |
| paddy@29 | 69 } |
| paddy@116 | 70 retrieved, err := context.GetAuthorizationCode(authCode.Code) |
| paddy@29 | 71 if err != nil { |
| paddy@87 | 72 t.Errorf("Error retrieving auth code from %T: %s", store, err) |
| paddy@29 | 73 } |
| paddy@87 | 74 match, field, expectation, result := compareAuthorizationCodes(authCode, retrieved) |
| paddy@34 | 75 if !match { |
| paddy@87 | 76 t.Errorf("Expected `%v` in the `%s` field of auth code retrieved from %T, got `%v`", expectation, field, store, result) |
| paddy@34 | 77 } |
| paddy@116 | 78 err = context.UseAuthorizationCode(authCode.Code) |
| paddy@111 | 79 if err != nil { |
| paddy@111 | 80 t.Errorf("Error retrieving auth code from %T: %s", store, err) |
| paddy@111 | 81 } |
| paddy@116 | 82 retrieved, err = context.GetAuthorizationCode(authCode.Code) |
| paddy@111 | 83 if err != nil { |
| paddy@111 | 84 t.Errorf("Error retrieving auth code from %T: %s", store, err) |
| paddy@111 | 85 } |
| paddy@111 | 86 authCode.Used = true |
| paddy@111 | 87 match, field, expectation, result = compareAuthorizationCodes(authCode, retrieved) |
| paddy@111 | 88 if !match { |
| paddy@111 | 89 t.Errorf("Expected `%v` in the `%s` field of auth code retrieved from %T, got `%v`", expectation, field, store, result) |
| paddy@111 | 90 } |
| paddy@116 | 91 err = context.DeleteAuthorizationCode(authCode.Code) |
| paddy@29 | 92 if err != nil { |
| paddy@87 | 93 t.Errorf("Error removing auth code from %T: %s", store, err) |
| paddy@29 | 94 } |
| paddy@116 | 95 retrieved, err = context.GetAuthorizationCode(authCode.Code) |
| paddy@87 | 96 if err != ErrAuthorizationCodeNotFound { |
| paddy@87 | 97 t.Errorf("Expected ErrAuthorizationCodeNotFound from %T, got %+v and %+v", store, retrieved, err) |
| paddy@34 | 98 } |
| paddy@116 | 99 err = context.DeleteAuthorizationCode(authCode.Code) |
| paddy@87 | 100 if err != ErrAuthorizationCodeNotFound { |
| paddy@87 | 101 t.Errorf("Expected ErrAuthorizationCodeNotFound from %T, got %+v", store, err) |
| paddy@29 | 102 } |
| paddy@116 | 103 err = context.UseAuthorizationCode(authCode.Code) |
| paddy@111 | 104 if err != ErrAuthorizationCodeNotFound { |
| paddy@111 | 105 t.Errorf("Expected ErrAuthorizationCodeNotFound from %T, got %+v", store, err) |
| paddy@111 | 106 } |
| paddy@29 | 107 } |
| paddy@29 | 108 } |
| paddy@111 | 109 |
| paddy@111 | 110 func TestAuthCodeGrantValidate(t *testing.T) { |
| paddy@111 | 111 t.Parallel() |
| paddy@111 | 112 store := NewMemstore() |
| paddy@111 | 113 testContext := Context{ |
| paddy@111 | 114 clients: store, |
| paddy@111 | 115 authCodes: store, |
| paddy@111 | 116 profiles: store, |
| paddy@111 | 117 tokens: store, |
| paddy@111 | 118 sessions: store, |
| paddy@111 | 119 } |
| paddy@111 | 120 client := Client{ |
| paddy@111 | 121 ID: uuid.NewID(), |
| paddy@111 | 122 Secret: "super secret!", |
| paddy@111 | 123 OwnerID: uuid.NewID(), |
| paddy@111 | 124 Name: "My test client", |
| paddy@111 | 125 Logo: "https://secondbit.org/logo.png", |
| paddy@111 | 126 Website: "https://secondbit.org/", |
| paddy@111 | 127 Type: "public", |
| paddy@111 | 128 } |
| paddy@111 | 129 endpoint := Endpoint{ |
| paddy@111 | 130 ID: uuid.NewID(), |
| paddy@111 | 131 ClientID: client.ID, |
| paddy@116 | 132 URI: "https://test.secondbit.org/redirect", |
| paddy@111 | 133 Added: time.Now(), |
| paddy@111 | 134 } |
| paddy@116 | 135 err := testContext.SaveClient(client) |
| paddy@111 | 136 if err != nil { |
| paddy@111 | 137 t.Fatal("Can't store client:", err) |
| paddy@111 | 138 } |
| paddy@115 | 139 err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint}) |
| paddy@111 | 140 if err != nil { |
| paddy@111 | 141 t.Fatal("Can't store endpoint:", err) |
| paddy@111 | 142 } |
| paddy@111 | 143 code := AuthorizationCode{ |
| paddy@111 | 144 Code: "myauthcode", |
| paddy@111 | 145 Created: time.Now(), |
| paddy@111 | 146 ExpiresIn: 180, |
| paddy@111 | 147 ClientID: uuid.NewID(), |
| paddy@111 | 148 Scope: "scope", |
| paddy@111 | 149 RedirectURI: "redirectURI", |
| paddy@111 | 150 State: "state", |
| paddy@111 | 151 } |
| paddy@111 | 152 err = testContext.SaveAuthorizationCode(code) |
| paddy@111 | 153 if err != nil { |
| paddy@111 | 154 t.Fatal("Can't add auth code:", err) |
| paddy@111 | 155 } |
| paddy@112 | 156 code2 := code |
| paddy@112 | 157 code2.Code = "otherauthcode" |
| paddy@112 | 158 code2.ClientID = client.ID |
| paddy@112 | 159 err = testContext.SaveAuthorizationCode(code2) |
| paddy@112 | 160 if err != nil { |
| paddy@112 | 161 t.Fatal("Can't add second auth code:", err) |
| paddy@112 | 162 } |
| paddy@111 | 163 req, err := http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@111 | 164 if err != nil { |
| paddy@111 | 165 t.Fatal("Can't build request:", err) |
| paddy@111 | 166 } |
| paddy@112 | 167 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@111 | 168 w := httptest.NewRecorder() |
| paddy@111 | 169 params := url.Values{} |
| paddy@111 | 170 body := bytes.NewBufferString(params.Encode()) |
| paddy@111 | 171 req.Body = ioutil.NopCloser(body) |
| paddy@111 | 172 scope, profileID, valid := authCodeGrantValidate(w, req, testContext) |
| paddy@111 | 173 if valid { |
| paddy@112 | 174 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID) |
| paddy@111 | 175 } |
| paddy@111 | 176 if w.Code != http.StatusBadRequest { |
| paddy@111 | 177 t.Errorf("Expected status %d, got %d", http.StatusBadRequest, w.Code) |
| paddy@111 | 178 } |
| paddy@112 | 179 expectedBody := `{"error":"invalid_request"}` |
| paddy@112 | 180 if strings.TrimSpace(w.Body.String()) != expectedBody { |
| paddy@112 | 181 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String())) |
| paddy@112 | 182 } |
| paddy@112 | 183 |
| paddy@112 | 184 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 185 if err != nil { |
| paddy@112 | 186 t.Fatal("Can't build request:", err) |
| paddy@112 | 187 } |
| paddy@112 | 188 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 189 w = httptest.NewRecorder() |
| paddy@112 | 190 params = url.Values{} |
| paddy@112 | 191 params.Set("code", "notmycode") |
| paddy@112 | 192 body = bytes.NewBufferString(params.Encode()) |
| paddy@112 | 193 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 194 err = req.ParseForm() |
| paddy@112 | 195 if err != nil { |
| paddy@112 | 196 t.Log(err) |
| paddy@112 | 197 } |
| paddy@112 | 198 scope, profileID, valid = authCodeGrantValidate(w, req, testContext) |
| paddy@112 | 199 if valid { |
| paddy@112 | 200 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID) |
| paddy@112 | 201 } |
| paddy@112 | 202 if w.Code != http.StatusUnauthorized { |
| paddy@112 | 203 t.Errorf("Expected status %d, got %d", http.StatusUnauthorized, w.Code) |
| paddy@112 | 204 } |
| paddy@112 | 205 expectedBody = `{"error":"invalid_client"}` |
| paddy@112 | 206 if expectedBody != strings.TrimSpace(w.Body.String()) { |
| paddy@112 | 207 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String())) |
| paddy@112 | 208 } |
| paddy@112 | 209 |
| paddy@112 | 210 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 211 if err != nil { |
| paddy@112 | 212 t.Fatal("Can't build request:", err) |
| paddy@112 | 213 } |
| paddy@112 | 214 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 215 req.SetBasicAuth(client.ID.String(), client.Secret) |
| paddy@112 | 216 w = httptest.NewRecorder() |
| paddy@112 | 217 params = url.Values{} |
| paddy@112 | 218 params.Set("code", "notmycode") |
| paddy@112 | 219 body = bytes.NewBufferString(params.Encode()) |
| paddy@112 | 220 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 221 err = req.ParseForm() |
| paddy@112 | 222 if err != nil { |
| paddy@112 | 223 t.Log(err) |
| paddy@112 | 224 } |
| paddy@112 | 225 scope, profileID, valid = authCodeGrantValidate(w, req, testContext) |
| paddy@112 | 226 if valid { |
| paddy@112 | 227 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID) |
| paddy@112 | 228 } |
| paddy@112 | 229 if w.Code != http.StatusBadRequest { |
| paddy@112 | 230 t.Errorf("Expected status %d, got %d", http.StatusUnauthorized, w.Code) |
| paddy@112 | 231 } |
| paddy@112 | 232 expectedBody = `{"error":"invalid_grant"}` |
| paddy@112 | 233 if expectedBody != strings.TrimSpace(w.Body.String()) { |
| paddy@112 | 234 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String())) |
| paddy@112 | 235 } |
| paddy@112 | 236 |
| paddy@112 | 237 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 238 if err != nil { |
| paddy@112 | 239 t.Fatal("Can't build request:", err) |
| paddy@112 | 240 } |
| paddy@112 | 241 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 242 req.SetBasicAuth(client.ID.String(), client.Secret) |
| paddy@112 | 243 w = httptest.NewRecorder() |
| paddy@112 | 244 params = url.Values{} |
| paddy@112 | 245 params.Set("code", code.Code) |
| paddy@112 | 246 params.Set("redirect_uri", "not my redirectURI") |
| paddy@112 | 247 body = bytes.NewBufferString(params.Encode()) |
| paddy@112 | 248 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 249 err = req.ParseForm() |
| paddy@112 | 250 if err != nil { |
| paddy@112 | 251 t.Log(err) |
| paddy@112 | 252 } |
| paddy@112 | 253 scope, profileID, valid = authCodeGrantValidate(w, req, testContext) |
| paddy@112 | 254 if valid { |
| paddy@112 | 255 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID) |
| paddy@112 | 256 } |
| paddy@112 | 257 if w.Code != http.StatusBadRequest { |
| paddy@112 | 258 t.Errorf("Expected status %d, got %d", http.StatusUnauthorized, w.Code) |
| paddy@112 | 259 } |
| paddy@112 | 260 expectedBody = `{"error":"invalid_grant"}` |
| paddy@112 | 261 if expectedBody != strings.TrimSpace(w.Body.String()) { |
| paddy@112 | 262 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String())) |
| paddy@112 | 263 } |
| paddy@112 | 264 |
| paddy@112 | 265 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 266 if err != nil { |
| paddy@112 | 267 t.Fatal("Can't build request:", err) |
| paddy@112 | 268 } |
| paddy@112 | 269 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 270 req.SetBasicAuth(client.ID.String(), client.Secret) |
| paddy@112 | 271 w = httptest.NewRecorder() |
| paddy@112 | 272 params = url.Values{} |
| paddy@112 | 273 params.Set("code", code.Code) |
| paddy@112 | 274 params.Set("redirect_uri", code.RedirectURI) |
| paddy@112 | 275 body = bytes.NewBufferString(params.Encode()) |
| paddy@112 | 276 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 277 err = req.ParseForm() |
| paddy@112 | 278 if err != nil { |
| paddy@112 | 279 t.Log(err) |
| paddy@112 | 280 } |
| paddy@112 | 281 scope, profileID, valid = authCodeGrantValidate(w, req, testContext) |
| paddy@112 | 282 if valid { |
| paddy@112 | 283 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID) |
| paddy@112 | 284 } |
| paddy@112 | 285 if w.Code != http.StatusBadRequest { |
| paddy@112 | 286 t.Errorf("Expected status %d, got %d", http.StatusUnauthorized, w.Code) |
| paddy@112 | 287 } |
| paddy@112 | 288 expectedBody = `{"error":"invalid_grant"}` |
| paddy@112 | 289 if expectedBody != strings.TrimSpace(w.Body.String()) { |
| paddy@112 | 290 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String())) |
| paddy@112 | 291 } |
| paddy@112 | 292 |
| paddy@112 | 293 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 294 if err != nil { |
| paddy@112 | 295 t.Fatal("Can't build request:", err) |
| paddy@112 | 296 } |
| paddy@112 | 297 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 298 req.SetBasicAuth(client.ID.String(), client.Secret) |
| paddy@112 | 299 w = httptest.NewRecorder() |
| paddy@112 | 300 params = url.Values{} |
| paddy@112 | 301 params.Set("code", code2.Code) |
| paddy@112 | 302 params.Set("redirect_uri", code2.RedirectURI) |
| paddy@112 | 303 body = bytes.NewBufferString(params.Encode()) |
| paddy@112 | 304 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 305 err = req.ParseForm() |
| paddy@112 | 306 if err != nil { |
| paddy@112 | 307 t.Log(err) |
| paddy@112 | 308 } |
| paddy@112 | 309 scope, profileID, valid = authCodeGrantValidate(w, req, testContext) |
| paddy@112 | 310 if !valid { |
| paddy@112 | 311 t.Fatalf("Expected valid auth code, was not valid.") |
| paddy@111 | 312 } |
| paddy@111 | 313 } |
| paddy@112 | 314 |
| paddy@112 | 315 func TestAuthCodeGrantInvalidate(t *testing.T) { |
| paddy@112 | 316 t.Parallel() |
| paddy@112 | 317 store := NewMemstore() |
| paddy@112 | 318 testContext := Context{ |
| paddy@112 | 319 clients: store, |
| paddy@112 | 320 authCodes: store, |
| paddy@112 | 321 profiles: store, |
| paddy@112 | 322 tokens: store, |
| paddy@112 | 323 sessions: store, |
| paddy@112 | 324 } |
| paddy@112 | 325 code := AuthorizationCode{ |
| paddy@112 | 326 Code: "myauthcode", |
| paddy@112 | 327 Created: time.Now(), |
| paddy@112 | 328 ExpiresIn: 180, |
| paddy@112 | 329 ClientID: uuid.NewID(), |
| paddy@112 | 330 Scope: "scope", |
| paddy@112 | 331 RedirectURI: "redirectURI", |
| paddy@112 | 332 State: "state", |
| paddy@112 | 333 } |
| paddy@112 | 334 err := testContext.SaveAuthorizationCode(code) |
| paddy@112 | 335 if err != nil { |
| paddy@112 | 336 t.Fatal("Can't add auth code:", err) |
| paddy@112 | 337 } |
| paddy@112 | 338 req, err := http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 339 if err != nil { |
| paddy@112 | 340 t.Fatal("Can't build request:", err) |
| paddy@112 | 341 } |
| paddy@112 | 342 err = authCodeGrantInvalidate(req, testContext) |
| paddy@112 | 343 if err != ErrAuthorizationCodeNotFound { |
| paddy@112 | 344 t.Errorf("Expected `%s`, got `%+v`", ErrAuthorizationCodeNotFound, err) |
| paddy@112 | 345 } |
| paddy@112 | 346 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 347 if err != nil { |
| paddy@112 | 348 t.Fatal("Can't build request:", err) |
| paddy@112 | 349 } |
| paddy@112 | 350 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 351 params := url.Values{} |
| paddy@112 | 352 params.Set("code", "notmycode") |
| paddy@112 | 353 body := bytes.NewBufferString(params.Encode()) |
| paddy@112 | 354 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 355 err = authCodeGrantInvalidate(req, testContext) |
| paddy@112 | 356 if err != ErrAuthorizationCodeNotFound { |
| paddy@112 | 357 t.Errorf("Expected `%s`, got `%+v`", ErrAuthorizationCodeNotFound, err) |
| paddy@112 | 358 } |
| paddy@112 | 359 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 360 if err != nil { |
| paddy@112 | 361 t.Fatal("Can't build request:", err) |
| paddy@112 | 362 } |
| paddy@112 | 363 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 364 params.Set("code", code.Code) |
| paddy@112 | 365 body = bytes.NewBufferString(params.Encode()) |
| paddy@112 | 366 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 367 err = authCodeGrantInvalidate(req, testContext) |
| paddy@112 | 368 if err != nil { |
| paddy@112 | 369 t.Error("Error invalidating auth code:", err) |
| paddy@112 | 370 } |
| paddy@112 | 371 authCode, err := testContext.GetAuthorizationCode(code.Code) |
| paddy@112 | 372 if err != nil { |
| paddy@112 | 373 t.Error("Error retrieving auth code:", err) |
| paddy@112 | 374 } |
| paddy@112 | 375 if !authCode.Used { |
| paddy@112 | 376 t.Error("Expected auth code to be used, was not.") |
| paddy@112 | 377 } |
| paddy@112 | 378 } |