auth

Paddy 2015-01-19 Parent:e000b1c24fc0 Child:d30a3a12d387

128:23c1a07c8a61 Go to Latest

auth/authcode_test.go

Add our BUG notices. Rather than keeping the list of things to implement or test on sticky notes attached to my monitor, let's give them BUG designations within the code. Now `godoc . bugs` will list them out for us. Isn't that nice?

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@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 }