auth

Paddy 2015-01-10 Parent:224f0610d3e7 Child:fa8ee6a4507c

112:da2a0954e8d3 Go to Latest

auth/authcode_test.go

Flesh out auth code grant (in)validation. Flesh out our tests for functions that do the validation and invalidation of the authorization code grant type's authorization codes. Basically, make sure that the auth code's are being checked right and that marking them as used after they're used works.

History
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@111 142 err = testContext.AddEndpoint(client.ID, 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 }