auth
auth/authcode_test.go
Support email verification. The bulk of this commit is auto-modifying files to export variables (mostly our request error types and our response type) so that they can be reused in a Go client for that API. We also implement the beginnings of a Go client for that API, implementing the bare minimum we need for our immediate purposes: the ability to retrieve information about a Login. This, of course, means we need an API endpoint that will return information about a Login, which in turn required us to implement a GetLogin method in our profileStore. Which got in-memory and postgres implementations. That done, we could add the Verification field and Verified field to the Login type, to keep track of whether we've verified the user's ownership of those communication methods (if the Login is, in fact, a communication method). This required us to update sql/postgres_init.sql to account for the new fields we're tracking. It also means that when creating a Login, we had to generate a UUID to use as the Verification field. To make things complete, we needed a verifyLogin method on the profileStore to mark a Login as verified. That, in turn, required an endpoint to control this through the API. While doing so, I lumped things together in an UpdateLogin handler just so we could reuse the endpoint and logic when resending a verification email that may have never reached the user, for whatever reason (the quintessential "send again" button). Finally, we implemented an email_verification listener that will pull email_verification events off NSQ, check for the requisite data integrity, and use mailgun to email out a verification/welcome email.
| 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@156 | 9 "os" |
| paddy@111 | 10 "strings" |
| paddy@29 | 11 "testing" |
| paddy@29 | 12 "time" |
| paddy@29 | 13 |
| paddy@107 | 14 "code.secondbit.org/uuid.hg" |
| paddy@29 | 15 ) |
| paddy@29 | 16 |
| paddy@156 | 17 func init() { |
| paddy@156 | 18 if os.Getenv("PG_TEST_DB") != "" { |
| paddy@156 | 19 p, err := NewPostgres(os.Getenv("PG_TEST_DB")) |
| paddy@156 | 20 if err != nil { |
| paddy@156 | 21 panic(err) |
| paddy@156 | 22 } |
| paddy@156 | 23 authCodeStores = append(authCodeStores, &p) |
| paddy@156 | 24 } |
| paddy@156 | 25 } |
| paddy@156 | 26 |
| paddy@87 | 27 var authCodeStores = []authorizationCodeStore{NewMemstore()} |
| paddy@29 | 28 |
| paddy@87 | 29 func compareAuthorizationCodes(authCode1, authCode2 AuthorizationCode) (success bool, field string, authCode1val, authCode2val interface{}) { |
| paddy@87 | 30 if authCode1.Code != authCode2.Code { |
| paddy@87 | 31 return false, "code", authCode1.Code, authCode2.Code |
| paddy@34 | 32 } |
| paddy@87 | 33 if !authCode1.Created.Equal(authCode2.Created) { |
| paddy@87 | 34 return false, "created", authCode1.Created, authCode2.Created |
| paddy@34 | 35 } |
| paddy@87 | 36 if authCode1.ExpiresIn != authCode2.ExpiresIn { |
| paddy@87 | 37 return false, "expires in", authCode1.ExpiresIn, authCode2.ExpiresIn |
| paddy@34 | 38 } |
| paddy@87 | 39 if !authCode1.ClientID.Equal(authCode2.ClientID) { |
| paddy@87 | 40 return false, "client ID", authCode1.ClientID, authCode2.ClientID |
| paddy@34 | 41 } |
| paddy@135 | 42 if len(authCode1.Scopes) != len(authCode2.Scopes) { |
| paddy@135 | 43 return false, "scopes", authCode1.Scopes, authCode2.Scopes |
| paddy@135 | 44 } |
| paddy@135 | 45 for pos, scope := range authCode1.Scopes { |
| paddy@135 | 46 if scope != authCode2.Scopes[pos] { |
| paddy@135 | 47 return false, "scopes", authCode1.Scopes, authCode2.Scopes |
| paddy@135 | 48 } |
| paddy@34 | 49 } |
| paddy@87 | 50 if authCode1.RedirectURI != authCode2.RedirectURI { |
| paddy@87 | 51 return false, "redirect URI", authCode1.RedirectURI, authCode2.RedirectURI |
| paddy@34 | 52 } |
| paddy@87 | 53 if authCode1.State != authCode2.State { |
| paddy@87 | 54 return false, "state", authCode1.State, authCode2.State |
| paddy@34 | 55 } |
| paddy@111 | 56 if !authCode1.ProfileID.Equal(authCode2.ProfileID) { |
| paddy@111 | 57 return false, "profile ID", authCode1.ProfileID, authCode2.ProfileID |
| paddy@111 | 58 } |
| paddy@111 | 59 if authCode1.Used != authCode2.Used { |
| paddy@111 | 60 return false, "used", authCode1.Used, authCode2.Used |
| paddy@111 | 61 } |
| paddy@34 | 62 return true, "", nil, nil |
| paddy@34 | 63 } |
| paddy@34 | 64 |
| paddy@111 | 65 func TestAuthorizationCodeStore(t *testing.T) { |
| paddy@36 | 66 t.Parallel() |
| paddy@87 | 67 authCode := AuthorizationCode{ |
| paddy@29 | 68 Code: "code", |
| paddy@149 | 69 Created: time.Now().Round(time.Millisecond), |
| paddy@29 | 70 ExpiresIn: 180, |
| paddy@29 | 71 ClientID: uuid.NewID(), |
| paddy@163 | 72 Scopes: stringsToScopes([]string{"scope"}), |
| paddy@29 | 73 RedirectURI: "redirectURI", |
| paddy@29 | 74 State: "state", |
| paddy@29 | 75 } |
| paddy@87 | 76 for _, store := range authCodeStores { |
| paddy@116 | 77 context := Context{authCodes: store} |
| paddy@116 | 78 err := context.SaveAuthorizationCode(authCode) |
| paddy@29 | 79 if err != nil { |
| paddy@87 | 80 t.Errorf("Error saving auth code to %T: %s", store, err) |
| paddy@34 | 81 } |
| paddy@116 | 82 err = context.SaveAuthorizationCode(authCode) |
| paddy@87 | 83 if err != ErrAuthorizationCodeAlreadyExists { |
| paddy@87 | 84 t.Errorf("Expected ErrAuthorizationCodeAlreadyExists from %T, got %+v", store, err) |
| paddy@29 | 85 } |
| paddy@116 | 86 retrieved, err := context.GetAuthorizationCode(authCode.Code) |
| paddy@29 | 87 if err != nil { |
| paddy@87 | 88 t.Errorf("Error retrieving auth code from %T: %s", store, err) |
| paddy@29 | 89 } |
| paddy@87 | 90 match, field, expectation, result := compareAuthorizationCodes(authCode, retrieved) |
| paddy@34 | 91 if !match { |
| paddy@87 | 92 t.Errorf("Expected `%v` in the `%s` field of auth code retrieved from %T, got `%v`", expectation, field, store, result) |
| paddy@34 | 93 } |
| paddy@116 | 94 err = context.UseAuthorizationCode(authCode.Code) |
| paddy@111 | 95 if err != nil { |
| paddy@111 | 96 t.Errorf("Error retrieving auth code from %T: %s", store, err) |
| paddy@111 | 97 } |
| paddy@116 | 98 retrieved, err = context.GetAuthorizationCode(authCode.Code) |
| paddy@111 | 99 if err != nil { |
| paddy@111 | 100 t.Errorf("Error retrieving auth code from %T: %s", store, err) |
| paddy@111 | 101 } |
| paddy@111 | 102 authCode.Used = true |
| paddy@111 | 103 match, field, expectation, result = compareAuthorizationCodes(authCode, retrieved) |
| paddy@111 | 104 if !match { |
| paddy@111 | 105 t.Errorf("Expected `%v` in the `%s` field of auth code retrieved from %T, got `%v`", expectation, field, store, result) |
| paddy@111 | 106 } |
| paddy@116 | 107 err = context.DeleteAuthorizationCode(authCode.Code) |
| paddy@29 | 108 if err != nil { |
| paddy@87 | 109 t.Errorf("Error removing auth code from %T: %s", store, err) |
| paddy@29 | 110 } |
| paddy@116 | 111 retrieved, err = context.GetAuthorizationCode(authCode.Code) |
| paddy@87 | 112 if err != ErrAuthorizationCodeNotFound { |
| paddy@87 | 113 t.Errorf("Expected ErrAuthorizationCodeNotFound from %T, got %+v and %+v", store, retrieved, err) |
| paddy@34 | 114 } |
| paddy@116 | 115 err = context.DeleteAuthorizationCode(authCode.Code) |
| paddy@87 | 116 if err != ErrAuthorizationCodeNotFound { |
| paddy@87 | 117 t.Errorf("Expected ErrAuthorizationCodeNotFound from %T, got %+v", store, err) |
| paddy@29 | 118 } |
| paddy@116 | 119 err = context.UseAuthorizationCode(authCode.Code) |
| paddy@111 | 120 if err != ErrAuthorizationCodeNotFound { |
| paddy@111 | 121 t.Errorf("Expected ErrAuthorizationCodeNotFound from %T, got %+v", store, err) |
| paddy@111 | 122 } |
| paddy@29 | 123 } |
| paddy@29 | 124 } |
| paddy@111 | 125 |
| paddy@111 | 126 func TestAuthCodeGrantValidate(t *testing.T) { |
| paddy@111 | 127 t.Parallel() |
| paddy@111 | 128 store := NewMemstore() |
| paddy@111 | 129 testContext := Context{ |
| paddy@111 | 130 clients: store, |
| paddy@111 | 131 authCodes: store, |
| paddy@111 | 132 profiles: store, |
| paddy@111 | 133 tokens: store, |
| paddy@111 | 134 sessions: store, |
| paddy@111 | 135 } |
| paddy@111 | 136 client := Client{ |
| paddy@111 | 137 ID: uuid.NewID(), |
| paddy@111 | 138 Secret: "super secret!", |
| paddy@111 | 139 OwnerID: uuid.NewID(), |
| paddy@111 | 140 Name: "My test client", |
| paddy@111 | 141 Logo: "https://secondbit.org/logo.png", |
| paddy@111 | 142 Website: "https://secondbit.org/", |
| paddy@111 | 143 Type: "public", |
| paddy@111 | 144 } |
| paddy@111 | 145 endpoint := Endpoint{ |
| paddy@111 | 146 ID: uuid.NewID(), |
| paddy@111 | 147 ClientID: client.ID, |
| paddy@116 | 148 URI: "https://test.secondbit.org/redirect", |
| paddy@149 | 149 Added: time.Now().Round(time.Millisecond), |
| paddy@111 | 150 } |
| paddy@116 | 151 err := testContext.SaveClient(client) |
| paddy@111 | 152 if err != nil { |
| paddy@111 | 153 t.Fatal("Can't store client:", err) |
| paddy@111 | 154 } |
| paddy@151 | 155 err = testContext.AddEndpoints([]Endpoint{endpoint}) |
| paddy@111 | 156 if err != nil { |
| paddy@111 | 157 t.Fatal("Can't store endpoint:", err) |
| paddy@111 | 158 } |
| paddy@111 | 159 code := AuthorizationCode{ |
| paddy@111 | 160 Code: "myauthcode", |
| paddy@149 | 161 Created: time.Now().Round(time.Millisecond), |
| paddy@111 | 162 ExpiresIn: 180, |
| paddy@111 | 163 ClientID: uuid.NewID(), |
| paddy@163 | 164 Scopes: stringsToScopes([]string{"scope"}), |
| paddy@111 | 165 RedirectURI: "redirectURI", |
| paddy@111 | 166 State: "state", |
| paddy@111 | 167 } |
| paddy@111 | 168 err = testContext.SaveAuthorizationCode(code) |
| paddy@111 | 169 if err != nil { |
| paddy@111 | 170 t.Fatal("Can't add auth code:", err) |
| paddy@111 | 171 } |
| paddy@112 | 172 code2 := code |
| paddy@112 | 173 code2.Code = "otherauthcode" |
| paddy@112 | 174 code2.ClientID = client.ID |
| paddy@112 | 175 err = testContext.SaveAuthorizationCode(code2) |
| paddy@112 | 176 if err != nil { |
| paddy@112 | 177 t.Fatal("Can't add second auth code:", err) |
| paddy@112 | 178 } |
| paddy@111 | 179 req, err := http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@111 | 180 if err != nil { |
| paddy@111 | 181 t.Fatal("Can't build request:", err) |
| paddy@111 | 182 } |
| paddy@112 | 183 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@111 | 184 w := httptest.NewRecorder() |
| paddy@111 | 185 params := url.Values{} |
| paddy@111 | 186 body := bytes.NewBufferString(params.Encode()) |
| paddy@111 | 187 req.Body = ioutil.NopCloser(body) |
| paddy@111 | 188 scope, profileID, valid := authCodeGrantValidate(w, req, testContext) |
| paddy@111 | 189 if valid { |
| paddy@112 | 190 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID) |
| paddy@111 | 191 } |
| paddy@111 | 192 if w.Code != http.StatusBadRequest { |
| paddy@111 | 193 t.Errorf("Expected status %d, got %d", http.StatusBadRequest, w.Code) |
| paddy@111 | 194 } |
| paddy@112 | 195 expectedBody := `{"error":"invalid_request"}` |
| paddy@112 | 196 if strings.TrimSpace(w.Body.String()) != expectedBody { |
| paddy@112 | 197 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String())) |
| paddy@112 | 198 } |
| paddy@112 | 199 |
| paddy@112 | 200 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 201 if err != nil { |
| paddy@112 | 202 t.Fatal("Can't build request:", err) |
| paddy@112 | 203 } |
| paddy@112 | 204 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 205 w = httptest.NewRecorder() |
| paddy@112 | 206 params = url.Values{} |
| paddy@112 | 207 params.Set("code", "notmycode") |
| paddy@112 | 208 body = bytes.NewBufferString(params.Encode()) |
| paddy@112 | 209 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 210 err = req.ParseForm() |
| paddy@112 | 211 if err != nil { |
| paddy@112 | 212 t.Log(err) |
| paddy@112 | 213 } |
| paddy@112 | 214 scope, profileID, valid = authCodeGrantValidate(w, req, testContext) |
| paddy@112 | 215 if valid { |
| paddy@112 | 216 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID) |
| paddy@112 | 217 } |
| paddy@112 | 218 if w.Code != http.StatusUnauthorized { |
| paddy@112 | 219 t.Errorf("Expected status %d, got %d", http.StatusUnauthorized, w.Code) |
| paddy@112 | 220 } |
| paddy@112 | 221 expectedBody = `{"error":"invalid_client"}` |
| paddy@112 | 222 if expectedBody != strings.TrimSpace(w.Body.String()) { |
| paddy@112 | 223 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String())) |
| paddy@112 | 224 } |
| paddy@112 | 225 |
| paddy@112 | 226 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 227 if err != nil { |
| paddy@112 | 228 t.Fatal("Can't build request:", err) |
| paddy@112 | 229 } |
| paddy@112 | 230 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 231 req.SetBasicAuth(client.ID.String(), client.Secret) |
| paddy@112 | 232 w = httptest.NewRecorder() |
| paddy@112 | 233 params = url.Values{} |
| paddy@112 | 234 params.Set("code", "notmycode") |
| paddy@112 | 235 body = bytes.NewBufferString(params.Encode()) |
| paddy@112 | 236 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 237 err = req.ParseForm() |
| paddy@112 | 238 if err != nil { |
| paddy@112 | 239 t.Log(err) |
| paddy@112 | 240 } |
| paddy@112 | 241 scope, profileID, valid = authCodeGrantValidate(w, req, testContext) |
| paddy@112 | 242 if valid { |
| paddy@112 | 243 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID) |
| paddy@112 | 244 } |
| paddy@112 | 245 if w.Code != http.StatusBadRequest { |
| paddy@112 | 246 t.Errorf("Expected status %d, got %d", http.StatusUnauthorized, w.Code) |
| paddy@112 | 247 } |
| paddy@112 | 248 expectedBody = `{"error":"invalid_grant"}` |
| paddy@112 | 249 if expectedBody != strings.TrimSpace(w.Body.String()) { |
| paddy@112 | 250 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String())) |
| paddy@112 | 251 } |
| paddy@112 | 252 |
| paddy@112 | 253 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 254 if err != nil { |
| paddy@112 | 255 t.Fatal("Can't build request:", err) |
| paddy@112 | 256 } |
| paddy@112 | 257 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 258 req.SetBasicAuth(client.ID.String(), client.Secret) |
| paddy@112 | 259 w = httptest.NewRecorder() |
| paddy@112 | 260 params = url.Values{} |
| paddy@112 | 261 params.Set("code", code.Code) |
| paddy@112 | 262 params.Set("redirect_uri", "not my redirectURI") |
| paddy@112 | 263 body = bytes.NewBufferString(params.Encode()) |
| paddy@112 | 264 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 265 err = req.ParseForm() |
| paddy@112 | 266 if err != nil { |
| paddy@112 | 267 t.Log(err) |
| paddy@112 | 268 } |
| paddy@112 | 269 scope, profileID, valid = authCodeGrantValidate(w, req, testContext) |
| paddy@112 | 270 if valid { |
| paddy@112 | 271 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID) |
| paddy@112 | 272 } |
| paddy@112 | 273 if w.Code != http.StatusBadRequest { |
| paddy@112 | 274 t.Errorf("Expected status %d, got %d", http.StatusUnauthorized, w.Code) |
| paddy@112 | 275 } |
| paddy@112 | 276 expectedBody = `{"error":"invalid_grant"}` |
| paddy@112 | 277 if expectedBody != strings.TrimSpace(w.Body.String()) { |
| paddy@112 | 278 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String())) |
| paddy@112 | 279 } |
| paddy@112 | 280 |
| paddy@112 | 281 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 282 if err != nil { |
| paddy@112 | 283 t.Fatal("Can't build request:", err) |
| paddy@112 | 284 } |
| paddy@112 | 285 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 286 req.SetBasicAuth(client.ID.String(), client.Secret) |
| paddy@112 | 287 w = httptest.NewRecorder() |
| paddy@112 | 288 params = url.Values{} |
| paddy@112 | 289 params.Set("code", code.Code) |
| paddy@112 | 290 params.Set("redirect_uri", code.RedirectURI) |
| paddy@112 | 291 body = bytes.NewBufferString(params.Encode()) |
| paddy@112 | 292 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 293 err = req.ParseForm() |
| paddy@112 | 294 if err != nil { |
| paddy@112 | 295 t.Log(err) |
| paddy@112 | 296 } |
| paddy@112 | 297 scope, profileID, valid = authCodeGrantValidate(w, req, testContext) |
| paddy@112 | 298 if valid { |
| paddy@112 | 299 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID) |
| paddy@112 | 300 } |
| paddy@112 | 301 if w.Code != http.StatusBadRequest { |
| paddy@112 | 302 t.Errorf("Expected status %d, got %d", http.StatusUnauthorized, w.Code) |
| paddy@112 | 303 } |
| paddy@112 | 304 expectedBody = `{"error":"invalid_grant"}` |
| paddy@112 | 305 if expectedBody != strings.TrimSpace(w.Body.String()) { |
| paddy@112 | 306 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String())) |
| paddy@112 | 307 } |
| paddy@112 | 308 |
| paddy@112 | 309 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 310 if err != nil { |
| paddy@112 | 311 t.Fatal("Can't build request:", err) |
| paddy@112 | 312 } |
| paddy@112 | 313 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 314 req.SetBasicAuth(client.ID.String(), client.Secret) |
| paddy@112 | 315 w = httptest.NewRecorder() |
| paddy@112 | 316 params = url.Values{} |
| paddy@112 | 317 params.Set("code", code2.Code) |
| paddy@112 | 318 params.Set("redirect_uri", code2.RedirectURI) |
| paddy@112 | 319 body = bytes.NewBufferString(params.Encode()) |
| paddy@112 | 320 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 321 err = req.ParseForm() |
| paddy@112 | 322 if err != nil { |
| paddy@112 | 323 t.Log(err) |
| paddy@112 | 324 } |
| paddy@112 | 325 scope, profileID, valid = authCodeGrantValidate(w, req, testContext) |
| paddy@112 | 326 if !valid { |
| paddy@112 | 327 t.Fatalf("Expected valid auth code, was not valid.") |
| paddy@111 | 328 } |
| paddy@111 | 329 } |
| paddy@112 | 330 |
| paddy@112 | 331 func TestAuthCodeGrantInvalidate(t *testing.T) { |
| paddy@112 | 332 t.Parallel() |
| paddy@112 | 333 store := NewMemstore() |
| paddy@112 | 334 testContext := Context{ |
| paddy@112 | 335 clients: store, |
| paddy@112 | 336 authCodes: store, |
| paddy@112 | 337 profiles: store, |
| paddy@112 | 338 tokens: store, |
| paddy@112 | 339 sessions: store, |
| paddy@112 | 340 } |
| paddy@112 | 341 code := AuthorizationCode{ |
| paddy@112 | 342 Code: "myauthcode", |
| paddy@149 | 343 Created: time.Now().Round(time.Millisecond), |
| paddy@112 | 344 ExpiresIn: 180, |
| paddy@112 | 345 ClientID: uuid.NewID(), |
| paddy@163 | 346 Scopes: stringsToScopes([]string{"scope"}), |
| paddy@112 | 347 RedirectURI: "redirectURI", |
| paddy@112 | 348 State: "state", |
| paddy@112 | 349 } |
| paddy@112 | 350 err := testContext.SaveAuthorizationCode(code) |
| paddy@112 | 351 if err != nil { |
| paddy@112 | 352 t.Fatal("Can't add auth code:", err) |
| paddy@112 | 353 } |
| paddy@112 | 354 req, err := http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 355 if err != nil { |
| paddy@112 | 356 t.Fatal("Can't build request:", err) |
| paddy@112 | 357 } |
| 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 := url.Values{} |
| paddy@112 | 368 params.Set("code", "notmycode") |
| paddy@112 | 369 body := bytes.NewBufferString(params.Encode()) |
| paddy@112 | 370 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 371 err = authCodeGrantInvalidate(req, testContext) |
| paddy@112 | 372 if err != ErrAuthorizationCodeNotFound { |
| paddy@112 | 373 t.Errorf("Expected `%s`, got `%+v`", ErrAuthorizationCodeNotFound, err) |
| paddy@112 | 374 } |
| paddy@112 | 375 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@112 | 376 if err != nil { |
| paddy@112 | 377 t.Fatal("Can't build request:", err) |
| paddy@112 | 378 } |
| paddy@112 | 379 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@112 | 380 params.Set("code", code.Code) |
| paddy@112 | 381 body = bytes.NewBufferString(params.Encode()) |
| paddy@112 | 382 req.Body = ioutil.NopCloser(body) |
| paddy@112 | 383 err = authCodeGrantInvalidate(req, testContext) |
| paddy@112 | 384 if err != nil { |
| paddy@112 | 385 t.Error("Error invalidating auth code:", err) |
| paddy@112 | 386 } |
| paddy@112 | 387 authCode, err := testContext.GetAuthorizationCode(code.Code) |
| paddy@112 | 388 if err != nil { |
| paddy@112 | 389 t.Error("Error retrieving auth code:", err) |
| paddy@112 | 390 } |
| paddy@112 | 391 if !authCode.Used { |
| paddy@112 | 392 t.Error("Expected auth code to be used, was not.") |
| paddy@112 | 393 } |
| paddy@112 | 394 } |