auth
auth/authcode_test.go
Turn AddEndpoint into AddEndpoints. Because one is a special case of many, it makes sense to be able to add multiple endpoints in a single call to the database. So we've converted the AddEndpoint method into an AddEndpoints method and updated our tests appropriately. We also filled in the errors when creating a client through the API, and moved things around to optimize for the maximum number of errors returned in a single call.
| 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@87 | 61 err := store.saveAuthorizationCode(authCode) |
| paddy@29 | 62 if err != nil { |
| paddy@87 | 63 t.Errorf("Error saving auth code to %T: %s", store, err) |
| paddy@34 | 64 } |
| paddy@87 | 65 err = store.saveAuthorizationCode(authCode) |
| paddy@87 | 66 if err != ErrAuthorizationCodeAlreadyExists { |
| paddy@87 | 67 t.Errorf("Expected ErrAuthorizationCodeAlreadyExists from %T, got %+v", store, err) |
| paddy@29 | 68 } |
| paddy@87 | 69 retrieved, err := store.getAuthorizationCode(authCode.Code) |
| paddy@29 | 70 if err != nil { |
| paddy@87 | 71 t.Errorf("Error retrieving auth code from %T: %s", store, err) |
| paddy@29 | 72 } |
| paddy@87 | 73 match, field, expectation, result := compareAuthorizationCodes(authCode, retrieved) |
| paddy@34 | 74 if !match { |
| paddy@87 | 75 t.Errorf("Expected `%v` in the `%s` field of auth code retrieved from %T, got `%v`", expectation, field, store, result) |
| paddy@34 | 76 } |
| paddy@111 | 77 err = store.useAuthorizationCode(authCode.Code) |
| paddy@111 | 78 if err != nil { |
| paddy@111 | 79 t.Errorf("Error retrieving auth code from %T: %s", store, err) |
| paddy@111 | 80 } |
| paddy@111 | 81 retrieved, err = store.getAuthorizationCode(authCode.Code) |
| paddy@111 | 82 if err != nil { |
| paddy@111 | 83 t.Errorf("Error retrieving auth code from %T: %s", store, err) |
| paddy@111 | 84 } |
| paddy@111 | 85 authCode.Used = true |
| paddy@111 | 86 match, field, expectation, result = compareAuthorizationCodes(authCode, retrieved) |
| paddy@111 | 87 if !match { |
| paddy@111 | 88 t.Errorf("Expected `%v` in the `%s` field of auth code retrieved from %T, got `%v`", expectation, field, store, result) |
| paddy@111 | 89 } |
| paddy@87 | 90 err = store.deleteAuthorizationCode(authCode.Code) |
| paddy@29 | 91 if err != nil { |
| paddy@87 | 92 t.Errorf("Error removing auth code from %T: %s", store, err) |
| paddy@29 | 93 } |
| paddy@87 | 94 retrieved, err = store.getAuthorizationCode(authCode.Code) |
| paddy@87 | 95 if err != ErrAuthorizationCodeNotFound { |
| paddy@87 | 96 t.Errorf("Expected ErrAuthorizationCodeNotFound from %T, got %+v and %+v", store, retrieved, err) |
| paddy@34 | 97 } |
| paddy@87 | 98 err = store.deleteAuthorizationCode(authCode.Code) |
| paddy@87 | 99 if err != ErrAuthorizationCodeNotFound { |
| paddy@87 | 100 t.Errorf("Expected ErrAuthorizationCodeNotFound from %T, got %+v", store, err) |
| paddy@29 | 101 } |
| paddy@111 | 102 err = store.useAuthorizationCode(authCode.Code) |
| paddy@111 | 103 if err != ErrAuthorizationCodeNotFound { |
| paddy@111 | 104 t.Errorf("Expected ErrAuthorizationCodeNotFound from %T, got %+v", store, err) |
| paddy@111 | 105 } |
| paddy@29 | 106 } |
| paddy@29 | 107 } |
| paddy@111 | 108 |
| paddy@111 | 109 func TestAuthCodeGrantValidate(t *testing.T) { |
| paddy@111 | 110 t.Parallel() |
| paddy@111 | 111 store := NewMemstore() |
| paddy@111 | 112 testContext := Context{ |
| paddy@111 | 113 clients: store, |
| paddy@111 | 114 authCodes: store, |
| paddy@111 | 115 profiles: store, |
| paddy@111 | 116 tokens: store, |
| paddy@111 | 117 sessions: store, |
| paddy@111 | 118 } |
| paddy@111 | 119 client := Client{ |
| paddy@111 | 120 ID: uuid.NewID(), |
| paddy@111 | 121 Secret: "super secret!", |
| paddy@111 | 122 OwnerID: uuid.NewID(), |
| paddy@111 | 123 Name: "My test client", |
| paddy@111 | 124 Logo: "https://secondbit.org/logo.png", |
| paddy@111 | 125 Website: "https://secondbit.org/", |
| paddy@111 | 126 Type: "public", |
| paddy@111 | 127 } |
| paddy@111 | 128 uri, err := url.Parse("https://test.secondbit.org/redirect") |
| paddy@111 | 129 if err != nil { |
| paddy@111 | 130 t.Fatal("Can't parse URL:", err) |
| paddy@111 | 131 } |
| paddy@111 | 132 endpoint := Endpoint{ |
| paddy@111 | 133 ID: uuid.NewID(), |
| paddy@111 | 134 ClientID: client.ID, |
| paddy@111 | 135 URI: *uri, |
| paddy@111 | 136 Added: time.Now(), |
| paddy@111 | 137 } |
| paddy@111 | 138 err = testContext.SaveClient(client) |
| paddy@111 | 139 if err != nil { |
| paddy@111 | 140 t.Fatal("Can't store client:", err) |
| paddy@111 | 141 } |
| paddy@115 | 142 err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint}) |
| paddy@111 | 143 if err != nil { |
| paddy@111 | 144 t.Fatal("Can't store endpoint:", err) |
| paddy@111 | 145 } |
| paddy@111 | 146 code := AuthorizationCode{ |
| paddy@111 | 147 Code: "myauthcode", |
| paddy@111 | 148 Created: time.Now(), |
| paddy@111 | 149 ExpiresIn: 180, |
| paddy@111 | 150 ClientID: uuid.NewID(), |
| paddy@111 | 151 Scope: "scope", |
| paddy@111 | 152 RedirectURI: "redirectURI", |
| paddy@111 | 153 State: "state", |
| paddy@111 | 154 } |
| paddy@111 | 155 err = testContext.SaveAuthorizationCode(code) |
| paddy@111 | 156 if err != nil { |
| paddy@111 | 157 t.Fatal("Can't add auth code:", err) |
| paddy@111 | 158 } |
| paddy@112 | 159 code2 := code |
| paddy@112 | 160 code2.Code = "otherauthcode" |
| paddy@112 | 161 code2.ClientID = client.ID |
| paddy@112 | 162 err = testContext.SaveAuthorizationCode(code2) |
| paddy@112 | 163 if err != nil { |
| paddy@112 | 164 t.Fatal("Can't add second auth code:", err) |
| paddy@112 | 165 } |
| paddy@111 | 166 req, err := http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@111 | 167 if err != nil { |
| paddy@111 | 168 t.Fatal("Can't build request:", err) |
| paddy@111 | 169 } |
| paddy@112 | 170 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@111 | 171 w := httptest.NewRecorder() |
| paddy@111 | 172 params := url.Values{} |
| paddy@111 | 173 body := bytes.NewBufferString(params.Encode()) |
| paddy@111 | 174 req.Body = ioutil.NopCloser(body) |
| paddy@111 | 175 scope, profileID, valid := authCodeGrantValidate(w, req, testContext) |
| paddy@111 | 176 if valid { |
| paddy@112 | 177 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID) |
| paddy@111 | 178 } |
| paddy@111 | 179 if w.Code != http.StatusBadRequest { |
| paddy@111 | 180 t.Errorf("Expected status %d, got %d", http.StatusBadRequest, w.Code) |
| paddy@111 | 181 } |
| paddy@112 | 182 expectedBody := `{"error":"invalid_request"}` |
| paddy@112 | 183 if strings.TrimSpace(w.Body.String()) != expectedBody { |
| paddy@112 | 184 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String())) |
| paddy@112 | 185 } |
| paddy@112 | 186 |
| paddy@112 | 187 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 188 if err != nil { |
| paddy@112 | 189 t.Fatal("Can't build request:", err) |
| paddy@112 | 190 } |
| paddy@112 | 191 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 192 w = httptest.NewRecorder() |
| paddy@112 | 193 params = url.Values{} |
| paddy@112 | 194 params.Set("code", "notmycode") |
| paddy@112 | 195 body = bytes.NewBufferString(params.Encode()) |
| paddy@112 | 196 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 197 err = req.ParseForm() |
| paddy@112 | 198 if err != nil { |
| paddy@112 | 199 t.Log(err) |
| paddy@112 | 200 } |
| paddy@112 | 201 scope, profileID, valid = authCodeGrantValidate(w, req, testContext) |
| paddy@112 | 202 if valid { |
| paddy@112 | 203 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID) |
| paddy@112 | 204 } |
| paddy@112 | 205 if w.Code != http.StatusUnauthorized { |
| paddy@112 | 206 t.Errorf("Expected status %d, got %d", http.StatusUnauthorized, w.Code) |
| paddy@112 | 207 } |
| paddy@112 | 208 expectedBody = `{"error":"invalid_client"}` |
| paddy@112 | 209 if expectedBody != strings.TrimSpace(w.Body.String()) { |
| paddy@112 | 210 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String())) |
| paddy@112 | 211 } |
| paddy@112 | 212 |
| paddy@112 | 213 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 214 if err != nil { |
| paddy@112 | 215 t.Fatal("Can't build request:", err) |
| paddy@112 | 216 } |
| paddy@112 | 217 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 218 req.SetBasicAuth(client.ID.String(), client.Secret) |
| paddy@112 | 219 w = httptest.NewRecorder() |
| paddy@112 | 220 params = url.Values{} |
| paddy@112 | 221 params.Set("code", "notmycode") |
| paddy@112 | 222 body = bytes.NewBufferString(params.Encode()) |
| paddy@112 | 223 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 224 err = req.ParseForm() |
| paddy@112 | 225 if err != nil { |
| paddy@112 | 226 t.Log(err) |
| paddy@112 | 227 } |
| paddy@112 | 228 scope, profileID, valid = authCodeGrantValidate(w, req, testContext) |
| paddy@112 | 229 if valid { |
| paddy@112 | 230 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID) |
| paddy@112 | 231 } |
| paddy@112 | 232 if w.Code != http.StatusBadRequest { |
| paddy@112 | 233 t.Errorf("Expected status %d, got %d", http.StatusUnauthorized, w.Code) |
| paddy@112 | 234 } |
| paddy@112 | 235 expectedBody = `{"error":"invalid_grant"}` |
| paddy@112 | 236 if expectedBody != strings.TrimSpace(w.Body.String()) { |
| paddy@112 | 237 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String())) |
| paddy@112 | 238 } |
| paddy@112 | 239 |
| paddy@112 | 240 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 241 if err != nil { |
| paddy@112 | 242 t.Fatal("Can't build request:", err) |
| paddy@112 | 243 } |
| paddy@112 | 244 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 245 req.SetBasicAuth(client.ID.String(), client.Secret) |
| paddy@112 | 246 w = httptest.NewRecorder() |
| paddy@112 | 247 params = url.Values{} |
| paddy@112 | 248 params.Set("code", code.Code) |
| paddy@112 | 249 params.Set("redirect_uri", "not my redirectURI") |
| paddy@112 | 250 body = bytes.NewBufferString(params.Encode()) |
| paddy@112 | 251 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 252 err = req.ParseForm() |
| paddy@112 | 253 if err != nil { |
| paddy@112 | 254 t.Log(err) |
| paddy@112 | 255 } |
| paddy@112 | 256 scope, profileID, valid = authCodeGrantValidate(w, req, testContext) |
| paddy@112 | 257 if valid { |
| paddy@112 | 258 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID) |
| paddy@112 | 259 } |
| paddy@112 | 260 if w.Code != http.StatusBadRequest { |
| paddy@112 | 261 t.Errorf("Expected status %d, got %d", http.StatusUnauthorized, w.Code) |
| paddy@112 | 262 } |
| paddy@112 | 263 expectedBody = `{"error":"invalid_grant"}` |
| paddy@112 | 264 if expectedBody != strings.TrimSpace(w.Body.String()) { |
| paddy@112 | 265 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String())) |
| paddy@112 | 266 } |
| paddy@112 | 267 |
| paddy@112 | 268 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 269 if err != nil { |
| paddy@112 | 270 t.Fatal("Can't build request:", err) |
| paddy@112 | 271 } |
| paddy@112 | 272 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 273 req.SetBasicAuth(client.ID.String(), client.Secret) |
| paddy@112 | 274 w = httptest.NewRecorder() |
| paddy@112 | 275 params = url.Values{} |
| paddy@112 | 276 params.Set("code", code.Code) |
| paddy@112 | 277 params.Set("redirect_uri", code.RedirectURI) |
| paddy@112 | 278 body = bytes.NewBufferString(params.Encode()) |
| paddy@112 | 279 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 280 err = req.ParseForm() |
| paddy@112 | 281 if err != nil { |
| paddy@112 | 282 t.Log(err) |
| paddy@112 | 283 } |
| paddy@112 | 284 scope, profileID, valid = authCodeGrantValidate(w, req, testContext) |
| paddy@112 | 285 if valid { |
| paddy@112 | 286 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID) |
| paddy@112 | 287 } |
| paddy@112 | 288 if w.Code != http.StatusBadRequest { |
| paddy@112 | 289 t.Errorf("Expected status %d, got %d", http.StatusUnauthorized, w.Code) |
| paddy@112 | 290 } |
| paddy@112 | 291 expectedBody = `{"error":"invalid_grant"}` |
| paddy@112 | 292 if expectedBody != strings.TrimSpace(w.Body.String()) { |
| paddy@112 | 293 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String())) |
| paddy@112 | 294 } |
| paddy@112 | 295 |
| paddy@112 | 296 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 297 if err != nil { |
| paddy@112 | 298 t.Fatal("Can't build request:", err) |
| paddy@112 | 299 } |
| paddy@112 | 300 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 301 req.SetBasicAuth(client.ID.String(), client.Secret) |
| paddy@112 | 302 w = httptest.NewRecorder() |
| paddy@112 | 303 params = url.Values{} |
| paddy@112 | 304 params.Set("code", code2.Code) |
| paddy@112 | 305 params.Set("redirect_uri", code2.RedirectURI) |
| paddy@112 | 306 body = bytes.NewBufferString(params.Encode()) |
| paddy@112 | 307 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 308 err = req.ParseForm() |
| paddy@112 | 309 if err != nil { |
| paddy@112 | 310 t.Log(err) |
| paddy@112 | 311 } |
| paddy@112 | 312 scope, profileID, valid = authCodeGrantValidate(w, req, testContext) |
| paddy@112 | 313 if !valid { |
| paddy@112 | 314 t.Fatalf("Expected valid auth code, was not valid.") |
| paddy@111 | 315 } |
| paddy@111 | 316 } |
| paddy@112 | 317 |
| paddy@112 | 318 func TestAuthCodeGrantInvalidate(t *testing.T) { |
| paddy@112 | 319 t.Parallel() |
| paddy@112 | 320 store := NewMemstore() |
| paddy@112 | 321 testContext := Context{ |
| paddy@112 | 322 clients: store, |
| paddy@112 | 323 authCodes: store, |
| paddy@112 | 324 profiles: store, |
| paddy@112 | 325 tokens: store, |
| paddy@112 | 326 sessions: store, |
| paddy@112 | 327 } |
| paddy@112 | 328 code := AuthorizationCode{ |
| paddy@112 | 329 Code: "myauthcode", |
| paddy@112 | 330 Created: time.Now(), |
| paddy@112 | 331 ExpiresIn: 180, |
| paddy@112 | 332 ClientID: uuid.NewID(), |
| paddy@112 | 333 Scope: "scope", |
| paddy@112 | 334 RedirectURI: "redirectURI", |
| paddy@112 | 335 State: "state", |
| paddy@112 | 336 } |
| paddy@112 | 337 err := testContext.SaveAuthorizationCode(code) |
| paddy@112 | 338 if err != nil { |
| paddy@112 | 339 t.Fatal("Can't add auth code:", err) |
| paddy@112 | 340 } |
| paddy@112 | 341 req, err := http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 342 if err != nil { |
| paddy@112 | 343 t.Fatal("Can't build request:", err) |
| paddy@112 | 344 } |
| paddy@112 | 345 err = authCodeGrantInvalidate(req, testContext) |
| paddy@112 | 346 if err != ErrAuthorizationCodeNotFound { |
| paddy@112 | 347 t.Errorf("Expected `%s`, got `%+v`", ErrAuthorizationCodeNotFound, err) |
| paddy@112 | 348 } |
| paddy@112 | 349 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 350 if err != nil { |
| paddy@112 | 351 t.Fatal("Can't build request:", err) |
| paddy@112 | 352 } |
| paddy@112 | 353 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 354 params := url.Values{} |
| paddy@112 | 355 params.Set("code", "notmycode") |
| paddy@112 | 356 body := bytes.NewBufferString(params.Encode()) |
| paddy@112 | 357 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 358 err = authCodeGrantInvalidate(req, testContext) |
| paddy@112 | 359 if err != ErrAuthorizationCodeNotFound { |
| paddy@112 | 360 t.Errorf("Expected `%s`, got `%+v`", ErrAuthorizationCodeNotFound, err) |
| paddy@112 | 361 } |
| paddy@112 | 362 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 363 if err != nil { |
| paddy@112 | 364 t.Fatal("Can't build request:", err) |
| paddy@112 | 365 } |
| paddy@112 | 366 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 367 params.Set("code", code.Code) |
| paddy@112 | 368 body = bytes.NewBufferString(params.Encode()) |
| paddy@112 | 369 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 370 err = authCodeGrantInvalidate(req, testContext) |
| paddy@112 | 371 if err != nil { |
| paddy@112 | 372 t.Error("Error invalidating auth code:", err) |
| paddy@112 | 373 } |
| paddy@112 | 374 authCode, err := testContext.GetAuthorizationCode(code.Code) |
| paddy@112 | 375 if err != nil { |
| paddy@112 | 376 t.Error("Error retrieving auth code:", err) |
| paddy@112 | 377 } |
| paddy@112 | 378 if !authCode.Used { |
| paddy@112 | 379 t.Error("Expected auth code to be used, was not.") |
| paddy@112 | 380 } |
| paddy@112 | 381 } |