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