auth

Paddy 2015-01-18 Parent:e000b1c24fc0 Child:23c1a07c8a61

117:da77e083cf02 Go to Latest

auth/oauth2_test.go

Change TODO to BUG. This is a refactor, not code to be added. It should show up in the gofmt bugs list.

History
1 package auth
3 import (
4 "bytes"
5 "encoding/json"
6 "html/template"
7 "io/ioutil"
8 "net/http"
9 "net/http/httptest"
10 "net/url"
11 "testing"
12 "time"
14 "code.secondbit.org/uuid.hg"
15 )
17 const (
18 scopeSet = 1 << iota
19 stateSet
20 uriSet
21 )
23 func stripParam(param string, u *url.URL) {
24 q := u.Query()
25 q.Del(param)
26 u.RawQuery = q.Encode()
27 }
29 func TestGetAuthorizationCodeCodeSuccess(t *testing.T) {
30 t.Parallel()
31 store := NewMemstore()
32 testContext := Context{
33 template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ if not .error }}Get auth grant{{ else }}{{ .error }}{{ end }}")),
34 clients: store,
35 authCodes: store,
36 profiles: store,
37 tokens: store,
38 sessions: store,
39 }
40 client := Client{
41 ID: uuid.NewID(),
42 Secret: "super secret!",
43 OwnerID: uuid.NewID(),
44 Name: "My test client",
45 Logo: "https://secondbit.org/logo.png",
46 Website: "https://secondbit.org",
47 Type: "public",
48 }
49 endpoint := Endpoint{
50 ID: uuid.NewID(),
51 ClientID: client.ID,
52 URI: "https://test.secondbit.org/redirect",
53 Added: time.Now(),
54 }
55 err := testContext.SaveClient(client)
56 if err != nil {
57 t.Fatal("Can't store client:", err)
58 }
59 err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint})
60 if err != nil {
61 t.Fatal("Can't store endpoint:", err)
62 }
63 profile := Profile{
64 ID: uuid.NewID(),
65 }
66 err = testContext.SaveProfile(profile)
67 if err != nil {
68 t.Fatal("Can't store profile:", err)
69 }
70 session := Session{
71 ID: "testsession",
72 Active: true,
73 ProfileID: profile.ID,
74 }
75 err = testContext.CreateSession(session)
76 if err != nil {
77 t.Fatal("Can't store session:", err)
78 }
79 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
80 if err != nil {
81 t.Fatal("Can't build request:", err)
82 }
83 cookie := &http.Cookie{
84 Name: authCookieName,
85 Value: session.ID,
86 }
87 req.AddCookie(cookie)
88 for i := 0; i < 1<<3; i++ {
89 w := httptest.NewRecorder()
90 params := url.Values{}
91 // see OAuth 2.0 spec, section 4.1.1
92 params.Set("response_type", "code")
93 params.Set("client_id", client.ID.String())
94 if i&uriSet != 0 {
95 params.Set("redirect_uri", endpoint.URI)
96 }
97 if i&scopeSet != 0 {
98 params.Set("scope", "testscope")
99 }
100 if i&stateSet != 0 {
101 params.Set("state", "my super secure state string")
102 }
103 req.URL.RawQuery = params.Encode()
104 req.Method = "GET"
105 req.Body = nil
106 req.Header.Del("Content-Type")
107 GetAuthorizationCodeHandler(w, req, testContext)
108 if w.Code != http.StatusOK {
109 t.Log("Response body:", w.Body.String())
110 t.Errorf("Expected status code to be %d, got %d for %s", http.StatusOK, w.Code, req.URL.String())
111 }
112 if w.Body.String() != "Get auth grant" {
113 t.Errorf("Expected body to be `%s`, got `%s` for %s", "Get auth grant", w.Body.String(), req.URL.String())
114 }
115 req.Method = "POST"
116 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
117 w = httptest.NewRecorder()
118 data := url.Values{}
119 data.Set("grant", "approved")
120 body := bytes.NewBufferString(data.Encode())
121 req.Body = ioutil.NopCloser(body)
122 GetAuthorizationCodeHandler(w, req, testContext)
123 if w.Code != http.StatusFound {
124 t.Errorf("Expected status code to be %d, got %d for %s", http.StatusFound, w.Code, req.URL.String())
125 }
126 redirectedTo := w.Header().Get("Location")
127 red, err := url.Parse(redirectedTo)
128 if err != nil {
129 t.Fatalf(`Being redirected to a non-URL "%s" threw error "%s" for "%s"\n`, redirectedTo, err, req.URL.String())
130 }
131 t.Log("Redirected to", redirectedTo)
132 if red.Query().Get("code") == "" {
133 t.Fatalf(`Expected code param in redirect URL to be set, but it wasn't for %s`, req.URL.String())
134 }
135 if _, err := testContext.GetAuthorizationCode(red.Query().Get("code")); err != nil {
136 t.Fatalf(`Unexpected error "%s: retrieving the grant "%s" supplied in the redirect URL for %s`, err, red.Query().Get("code"), req.URL.String())
137 }
138 err = testContext.DeleteAuthorizationCode(red.Query().Get("code"))
139 if err != nil {
140 t.Logf(`Unexpected error "%s" deleting grant "%s" for %s`, err, red.Query().Get("code"), req.URL.String())
141 }
142 stripParam("code", red)
143 if red.Query().Get("state") != params.Get("state") {
144 t.Errorf(`Expected state param in redirect URL to be "%s", got "%s" for %s`, params.Get("state"), red.Query().Get("state"), req.URL.String())
145 }
146 stripParam("state", red)
147 if red.String() != endpoint.URI {
148 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI, red.String())
149 }
150 }
151 }
153 func TestGetAuthorizationCodeCodeInvalidClient(t *testing.T) {
154 t.Parallel()
155 store := NewMemstore()
156 testContext := Context{
157 template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ .error }}")),
158 clients: store,
159 authCodes: store,
160 profiles: store,
161 tokens: store,
162 sessions: store,
163 }
164 client := Client{
165 ID: uuid.NewID(),
166 Secret: "super secret!",
167 OwnerID: uuid.NewID(),
168 Name: "My test client",
169 Type: "public",
170 }
171 err := testContext.SaveClient(client)
172 if err != nil {
173 t.Fatal("Can't store client:", err)
174 }
175 session := Session{
176 ID: "testsession",
177 Active: true,
178 }
179 err = testContext.CreateSession(session)
180 if err != nil {
181 t.Fatal("Can't store session:", err)
182 }
183 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
184 if err != nil {
185 t.Fatal("Can't build request:", err)
186 }
187 w := httptest.NewRecorder()
188 params := url.Values{}
189 params.Set("response_type", "code")
190 params.Set("redirect_uri", "https://test.secondbit.org/")
191 req.URL.RawQuery = params.Encode()
192 cookie := &http.Cookie{
193 Name: authCookieName,
194 Value: session.ID,
195 }
196 req.AddCookie(cookie)
197 GetAuthorizationCodeHandler(w, req, testContext)
198 if w.Code != http.StatusBadRequest {
199 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
200 }
201 if w.Body.String() != "Client ID must be specified in the request." {
202 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "Client ID must be specified in the request.", w.Body.String())
203 }
204 w = httptest.NewRecorder()
205 params.Set("client_id", "Not an ID")
206 req.URL.RawQuery = params.Encode()
207 GetAuthorizationCodeHandler(w, req, testContext)
208 if w.Code != http.StatusBadRequest {
209 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
210 }
211 if w.Body.String() != "client_id is not a valid Client ID." {
212 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "client_id is not a valid Client ID.", w.Body.String())
213 }
214 w = httptest.NewRecorder()
215 params.Set("client_id", uuid.NewID().String())
216 req.URL.RawQuery = params.Encode()
217 GetAuthorizationCodeHandler(w, req, testContext)
218 if w.Code != http.StatusBadRequest {
219 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
220 }
221 if w.Body.String() != "The specified Client couldn&rsquo;t be found." {
222 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The specified Client couldn&rsquo;t be found.", w.Body.String())
223 }
224 }
226 func TestGetAuthorizationCodeCodeInvalidURI(t *testing.T) {
227 t.Parallel()
228 store := NewMemstore()
229 testContext := Context{
230 template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ .error }}")),
231 clients: store,
232 authCodes: store,
233 profiles: store,
234 tokens: store,
235 sessions: store,
236 }
237 client := Client{
238 ID: uuid.NewID(),
239 Secret: "super secret!",
240 OwnerID: uuid.NewID(),
241 Name: "My test client",
242 Type: "public",
243 }
244 err := testContext.SaveClient(client)
245 if err != nil {
246 t.Fatal("Can't store client:", err)
247 }
248 session := Session{
249 ID: "testsession",
250 Active: true,
251 }
252 err = testContext.CreateSession(session)
253 if err != nil {
254 t.Fatal("Can't store session:", err)
255 }
256 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
257 if err != nil {
258 t.Fatal("Can't build request:", err)
259 }
260 cookie := &http.Cookie{
261 Name: authCookieName,
262 Value: session.ID,
263 }
264 req.AddCookie(cookie)
265 w := httptest.NewRecorder()
266 params := url.Values{}
267 params.Set("response_type", "code")
268 params.Set("client_id", client.ID.String())
269 req.URL.RawQuery = params.Encode()
270 GetAuthorizationCodeHandler(w, req, testContext)
271 if w.Code != http.StatusBadRequest {
272 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
273 }
274 if w.Body.String() != "The redirect_uri specified is not valid." {
275 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
276 }
277 endpoint := Endpoint{
278 ID: uuid.NewID(),
279 ClientID: client.ID,
280 URI: "https://test.secondbit.org/redirect",
281 Added: time.Now(),
282 }
283 err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint})
284 if err != nil {
285 t.Fatal("Can't store endpoint:", err)
286 }
287 w = httptest.NewRecorder()
288 params.Set("redirect_uri", "https://test.secondbit.org/wrong")
289 req.URL.RawQuery = params.Encode()
290 GetAuthorizationCodeHandler(w, req, testContext)
291 if w.Code != http.StatusBadRequest {
292 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
293 }
294 if w.Body.String() != "The redirect_uri specified is not valid." {
295 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
296 }
297 endpoint2 := Endpoint{
298 ID: uuid.NewID(),
299 ClientID: client.ID,
300 URI: "https://test.secondbit.org/redirect",
301 Added: time.Now(),
302 }
303 err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint2})
304 if err != nil {
305 t.Fatal("Can't store endpoint:", err)
306 }
307 w = httptest.NewRecorder()
308 params.Set("redirect_uri", "")
309 req.URL.RawQuery = params.Encode()
310 GetAuthorizationCodeHandler(w, req, testContext)
311 if w.Code != http.StatusBadRequest {
312 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
313 }
314 if w.Body.String() != "The redirect_uri specified is not valid." {
315 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
316 }
317 w = httptest.NewRecorder()
318 params.Set("redirect_uri", "://not a URL")
319 req.URL.RawQuery = params.Encode()
320 GetAuthorizationCodeHandler(w, req, testContext)
321 if w.Code != http.StatusBadRequest {
322 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
323 }
324 if w.Body.String() != "The redirect_uri specified is not valid." {
325 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
326 }
327 }
329 func TestGetAuthorizationCodeCodeInvalidResponseType(t *testing.T) {
330 t.Parallel()
331 store := NewMemstore()
332 testContext := Context{
333 template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ .error }}")),
334 clients: store,
335 authCodes: store,
336 profiles: store,
337 tokens: store,
338 sessions: store,
339 }
340 client := Client{
341 ID: uuid.NewID(),
342 Secret: "super secret!",
343 OwnerID: uuid.NewID(),
344 Name: "My test client",
345 Logo: "https://secondbit.org/logo.png",
346 Website: "https://secondbit.org",
347 Type: "public",
348 }
349 endpoint := Endpoint{
350 ID: uuid.NewID(),
351 ClientID: client.ID,
352 URI: "https://test.secondbit.org/redirect",
353 Added: time.Now(),
354 }
355 err := testContext.SaveClient(client)
356 if err != nil {
357 t.Fatal("Can't store client:", err)
358 }
359 err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint})
360 if err != nil {
361 t.Fatal("Can't store endpoint:", err)
362 }
363 session := Session{
364 ID: "testsession",
365 Active: true,
366 }
367 err = testContext.CreateSession(session)
368 if err != nil {
369 t.Fatal("Can't store session:", err)
370 }
371 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
372 if err != nil {
373 t.Fatal("Can't build request:", err)
374 }
375 cookie := &http.Cookie{
376 Name: authCookieName,
377 Value: session.ID,
378 }
379 req.AddCookie(cookie)
380 params := url.Values{}
381 params.Set("response_type", "totally not code")
382 params.Set("client_id", client.ID.String())
383 params.Set("redirect_uri", endpoint.URI)
384 params.Set("scope", "testscope")
385 params.Set("state", "my super secure state string")
386 req.URL.RawQuery = params.Encode()
387 w := httptest.NewRecorder()
388 GetAuthorizationCodeHandler(w, req, testContext)
389 if w.Code != http.StatusFound {
390 t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code)
391 }
392 redirectedTo := w.Header().Get("Location")
393 red, err := url.Parse(redirectedTo)
394 if err != nil {
395 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
396 }
397 if red.Query().Get("error") != "invalid_request" {
398 t.Errorf(`Expected error param in redirect URL to be "%s", got "%s"`, "invalid_request", red.Query().Get("error"))
399 }
400 stripParam("error", red)
401 if red.Query().Get("state") != params.Get("state") {
402 t.Errorf(`Expected state param in redirect URL to be "%s", got "%s"`, params.Get("state"), red.Query().Get("state"))
403 }
404 stripParam("state", red)
405 if red.String() != endpoint.URI {
406 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI, red.String())
407 }
408 stripParam("response_type", req.URL)
409 w = httptest.NewRecorder()
410 GetAuthorizationCodeHandler(w, req, testContext)
411 if w.Code != http.StatusFound {
412 t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code)
413 }
414 redirectedTo = w.Header().Get("Location")
415 red, err = url.Parse(redirectedTo)
416 if err != nil {
417 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
418 }
419 if red.Query().Get("error") != "invalid_request" {
420 t.Errorf(`Expected error param in redirect URL to be "%s", got "%s"`, "invalid_request", red.Query().Get("error"))
421 }
422 stripParam("error", red)
423 if red.Query().Get("state") != params.Get("state") {
424 t.Errorf(`Expected state param in redirect URL to be "%s", got "%s"`, params.Get("state"), red.Query().Get("state"))
425 }
426 stripParam("state", red)
427 if red.String() != endpoint.URI {
428 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI, red.String())
429 }
430 }
432 func TestGetAuthorizationCodeCodeDenied(t *testing.T) {
433 t.Parallel()
434 store := NewMemstore()
435 testContext := Context{
436 template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ .error }}")),
437 clients: store,
438 authCodes: store,
439 profiles: store,
440 tokens: store,
441 sessions: store,
442 }
443 client := Client{
444 ID: uuid.NewID(),
445 Secret: "super secret!",
446 OwnerID: uuid.NewID(),
447 Name: "My test client",
448 Logo: "https://secondbit.org/logo.png",
449 Website: "https://secondbit.org",
450 Type: "public",
451 }
452 endpoint := Endpoint{
453 ID: uuid.NewID(),
454 ClientID: client.ID,
455 URI: "https://test.secondbit.org/redirect",
456 Added: time.Now(),
457 }
458 err := testContext.SaveClient(client)
459 if err != nil {
460 t.Fatal("Can't store client:", err)
461 }
462 err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint})
463 if err != nil {
464 t.Fatal("Can't store endpoint:", err)
465 }
466 session := Session{
467 ID: "testsession",
468 Active: true,
469 }
470 err = testContext.CreateSession(session)
471 if err != nil {
472 t.Fatal("Can't store session:", err)
473 }
474 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
475 if err != nil {
476 t.Fatal("Can't build request:", err)
477 }
478 cookie := &http.Cookie{
479 Name: authCookieName,
480 Value: session.ID,
481 }
482 req.AddCookie(cookie)
483 params := url.Values{}
484 params.Set("response_type", "code")
485 params.Set("client_id", client.ID.String())
486 params.Set("redirect_uri", endpoint.URI)
487 params.Set("scope", "testscope")
488 params.Set("state", "my super secure state string")
489 data := url.Values{}
490 data.Set("grant", "denied")
491 req.URL.RawQuery = params.Encode()
492 req.Body = ioutil.NopCloser(bytes.NewBufferString(data.Encode()))
493 req.Method = "POST"
494 w := httptest.NewRecorder()
495 GetAuthorizationCodeHandler(w, req, testContext)
496 if w.Code != http.StatusFound {
497 t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code)
498 }
499 redirectedTo := w.Header().Get("Location")
500 red, err := url.Parse(redirectedTo)
501 if err != nil {
502 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
503 }
504 if red.Query().Get("error") != "access_denied" {
505 t.Errorf(`Expected error param in redirect URL to be "%s", got "%s"`, "access_denied", red.Query().Get("error"))
506 }
507 stripParam("error", red)
508 if red.Query().Get("state") != params.Get("state") {
509 t.Errorf(`Expected state param in redirect URL to be "%s", got "%s"`, params.Get("state"), red.Query().Get("state"))
510 }
511 stripParam("state", red)
512 if red.String() != endpoint.URI {
513 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI, red.String())
514 }
515 }
517 func TestGetAuthorizationCodeCodeLoginRedirect(t *testing.T) {
518 t.Parallel()
519 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
520 if err != nil {
521 t.Fatal("Can't build request:", err)
522 }
523 w := httptest.NewRecorder()
524 GetAuthorizationCodeHandler(w, req, Context{template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ .internal_error }}"))})
525 if w.Code != http.StatusInternalServerError {
526 t.Errorf("Expected status code to be %d, got %d", http.StatusInternalServerError, w.Code)
527 }
528 expectation := "Missing login URL."
529 if w.Body.String() != expectation {
530 t.Errorf(`Expected body to be "%s", got "%s"`, expectation, w.Body.String())
531 }
532 uri, err := url.Parse("https://client.secondbit.org/")
533 if err != nil {
534 t.Error("Unexpected error", err)
535 }
536 testContext := Context{
537 loginURI: uri,
538 }
539 w = httptest.NewRecorder()
540 GetAuthorizationCodeHandler(w, req, testContext)
541 if w.Code != http.StatusFound {
542 t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code)
543 }
544 redirectedTo := w.Header().Get("Location")
545 expectation = "https://client.secondbit.org/?from=https%3A%2F%2Ftest.auth.secondbit.org%2Foauth2%2Fgrant"
546 if redirectedTo != expectation {
547 t.Errorf("Expected to be redirected to '%s', was redirected to '%s'", expectation, redirectedTo)
548 }
549 }
551 func TestCheckCookie(t *testing.T) {
552 t.Parallel()
553 req, err := http.NewRequest("GET", "https://auth.secondbit.org", nil)
554 if err != nil {
555 t.Error("Unexpected error creating base request:", err)
556 }
557 store := NewMemstore()
558 testContext := Context{
559 sessions: store,
560 }
561 session, err := checkCookie(req, testContext)
562 if err != ErrNoSession {
563 t.Errorf("Expected ErrNoSession, got %s", err)
564 }
565 session = Session{
566 ID: "testsession",
567 Active: true,
568 }
569 err = testContext.CreateSession(session)
570 if err != nil {
571 t.Error("Unexpected error persisting session:", err)
572 }
573 invalidSession := Session{
574 ID: "testsession2",
575 Active: false,
576 }
577 err = testContext.CreateSession(invalidSession)
578 if err != nil {
579 t.Error("Unexpected error persisting session:", err)
580 }
581 result, err := checkCookie(req, testContext)
582 if err != ErrNoSession {
583 t.Errorf("Expected ErrNoSession, got %s", err)
584 }
585 req.AddCookie(&http.Cookie{
586 Name: "wrongcookie",
587 Value: "wrong value",
588 })
589 result, err = checkCookie(req, testContext)
590 if err != ErrNoSession {
591 t.Error("Expected ErrNoSession, got", err)
592 }
593 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
594 if err != nil {
595 t.Error("Unexpected error creating base request:", err)
596 }
597 req.AddCookie(&http.Cookie{
598 Name: "Stillwrongcookie",
599 Value: session.ID,
600 })
601 result, err = checkCookie(req, testContext)
602 if err != ErrNoSession {
603 t.Error("Expected ErrNoSession, got", err)
604 }
605 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
606 if err != nil {
607 t.Error("Unexpected error creating base request:", err)
608 }
609 req.AddCookie(&http.Cookie{
610 Name: authCookieName,
611 Value: "wrong value",
612 })
613 result, err = checkCookie(req, testContext)
614 if err != ErrInvalidSession {
615 t.Error("Expected ErrInvalidSession, got", err)
616 }
617 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
618 if err != nil {
619 t.Error("Unexpected error creating base request:", err)
620 }
621 req.AddCookie(&http.Cookie{
622 Name: authCookieName,
623 Value: invalidSession.ID,
624 })
625 result, err = checkCookie(req, testContext)
626 if err != ErrInvalidSession {
627 t.Error("Expected ErrInvalidSession, got", err)
628 }
629 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
630 if err != nil {
631 t.Error("Unexpected error creating base request:", err)
632 }
633 req.AddCookie(&http.Cookie{
634 Name: authCookieName,
635 Value: session.ID,
636 })
637 result, err = checkCookie(req, testContext)
638 if err != nil {
639 t.Error("Unexpected error:", err)
640 }
641 success, field, expectation, outcome := compareSessions(session, result)
642 if !success {
643 t.Errorf(`Expected field %s to be %v, but got %v`, field, expectation, outcome)
644 }
645 }
647 func TestBuildLoginRedirect(t *testing.T) {
648 t.Parallel()
649 req, err := http.NewRequest("GET", "https://client.secondbit.org/my/awesome/path?has=query&params=to&screw=this&all=up", nil)
650 if err != nil {
651 t.Error("Unexpected error creating base request:", err)
652 }
653 result := buildLoginRedirect(req, Context{})
654 if result != "" {
655 t.Error("Expected empty string as the result, got", result)
656 }
657 uri, err := url.Parse("https://auth.secondbit.org/login?query=string&other=param")
658 if err != nil {
659 t.Error("Unexpected error parsing URL:", err)
660 }
661 c := Context{loginURI: uri}
662 result = buildLoginRedirect(req, c)
663 expectation := "https://auth.secondbit.org/login?from=https%3A%2F%2Fclient.secondbit.org%2Fmy%2Fawesome%2Fpath%3Fhas%3Dquery%26params%3Dto%26screw%3Dthis%26all%3Dup&other=param&query=string"
664 if result != expectation {
665 t.Errorf(`Expected result string to be "%s", was "%s"`, expectation, result)
666 }
667 }
669 func TestAuthenticateHelper(t *testing.T) {
670 t.Parallel()
671 store := NewMemstore()
672 context := Context{
673 profiles: store,
674 }
675 profile := Profile{
676 ID: uuid.NewID(),
677 Name: "Test User",
678 Passphrase: "f3a4ac4f1d657b2e6e776d24213e39406d50a87a52691a2a78891425af1271d0",
679 Iterations: 1,
680 Salt: "d82d92cfa8bfb5a08270ebbf39a3710d24b352b937fcc8959ebcb40384cc616b",
681 PassphraseScheme: 1,
682 Compromised: false,
683 LockedUntil: time.Time{},
684 PassphraseReset: "",
685 PassphraseResetCreated: time.Time{},
686 Created: time.Now(),
687 LastSeen: time.Time{},
688 }
689 login := Login{
690 Type: "email",
691 Value: "test@example.com",
692 ProfileID: profile.ID,
693 Created: time.Now(),
694 LastUsed: time.Time{},
695 }
696 err := context.SaveProfile(profile)
697 if err != nil {
698 t.Error("Error saving profile:", err)
699 }
700 err = context.AddLogin(login)
701 if err != nil {
702 t.Error("Error adding login:", err)
703 }
704 response, err := authenticate("test@example.com", "mysecurepassphrase", context)
705 if err != nil {
706 t.Error("Unexpected error:", err)
707 }
708 success, field, expectation, result := compareProfiles(profile, response)
709 if !success {
710 t.Errorf(`Expected field %s to be "%v", got "%v"`, field, expectation, result)
711 }
712 response, err = authenticate("test2@example.com", "mysecurepassphrase", context)
713 if err != ErrIncorrectAuth {
714 t.Error("Expected ErrIncorrectAuth, got", err)
715 }
716 response, err = authenticate("test@example.com", "not the right password", context)
717 if err != ErrIncorrectAuth {
718 t.Error("Expected ErrIncorrectAuth, got", err)
719 }
720 scheme := 1000
721 phrase := "doesn't really matter, the scheme doesn't exist"
722 change := ProfileChange{
723 PassphraseScheme: &scheme,
724 Passphrase: &phrase,
725 }
726 err = context.UpdateProfile(profile.ID, change)
727 if err != nil {
728 t.Error("Unexpected error:", err)
729 }
730 response, err = authenticate("test@example.com", "not the right password", context)
731 if err != ErrInvalidPassphraseScheme {
732 t.Error("Expected ErrIncorrectAuth, got", err)
733 }
734 }
736 func TestGetTokenHandler(t *testing.T) {
737 t.Parallel()
738 store := NewMemstore()
739 context := Context{
740 clients: store,
741 authCodes: store,
742 tokens: store,
743 }
744 client := Client{
745 ID: uuid.NewID(),
746 Secret: "sometimes I feel like I don't know what I'm doing",
747 OwnerID: uuid.NewID(),
748 Name: "A Super Awesome Client!",
749 Logo: "https://logos.secondbit.org/client.png",
750 Website: "https://client.secondbit.org/",
751 Type: "confidential",
752 }
753 authCode := AuthorizationCode{
754 Code: "testcode",
755 Created: time.Now(),
756 ExpiresIn: 600,
757 ClientID: client.ID,
758 Scope: "testscope",
759 RedirectURI: "https://client.secondbit.org/",
760 State: "teststate",
761 ProfileID: uuid.NewID(),
762 }
763 err := context.SaveAuthorizationCode(authCode)
764 if err != nil {
765 t.Error("Error saving auth code:", err)
766 }
767 err = context.SaveClient(client)
768 if err != nil {
769 t.Error("Error saving client:", err)
770 }
771 data := url.Values{}
772 data.Set("grant_type", "authorization_code")
773 data.Set("code", authCode.Code)
774 data.Set("redirect_uri", authCode.RedirectURI)
775 body := bytes.NewBufferString(data.Encode())
776 req, err := http.NewRequest("POST", "https://auth.secondbit.org/", ioutil.NopCloser(body))
777 if err != nil {
778 t.Error("Error constructing request:", err)
779 }
780 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
781 req.SetBasicAuth(client.ID.String(), client.Secret)
782 w := httptest.NewRecorder()
783 GetTokenHandler(w, req, context)
784 resp := tokenResponse{}
785 err = json.Unmarshal(w.Body.Bytes(), &resp)
786 if err != nil {
787 t.Error("Error unmarshalling response:", err)
788 t.Error("Response:", w.Body.String())
789 }
790 if resp.AccessToken == "" {
791 t.Error("Got blank access token back")
792 }
793 if resp.RefreshToken == "" {
794 t.Error("Got blank refresh token back")
795 }
796 if resp.TokenType == "" {
797 t.Error("Got blank token type back")
798 }
799 if resp.ExpiresIn == 0 {
800 t.Error("Got blank expires in back")
801 }
802 tokens, err := context.GetTokensByProfileID(authCode.ProfileID, 1, 0)
803 if err != nil {
804 t.Error("Error retrieving token:", err)
805 }
806 if len(tokens) != 1 {
807 t.Fatalf("Expected %d tokens, got %d", 1, len(tokens))
808 }
809 if tokens[0].AccessToken != resp.AccessToken {
810 t.Errorf(`Expected access token to be "%s", got "%s"`, tokens[0].AccessToken, resp.AccessToken)
811 }
812 if tokens[0].RefreshToken != resp.RefreshToken {
813 t.Errorf(`Expected refresh token to be "%s", got "%s"`, tokens[0].RefreshToken, resp.RefreshToken)
814 }
815 if tokens[0].ExpiresIn != resp.ExpiresIn {
816 t.Errorf(`Expected expires in to be %d, got %d`, tokens[0].ExpiresIn, resp.ExpiresIn)
817 }
818 if tokens[0].TokenType != resp.TokenType {
819 t.Errorf(`Expected token type to be %s, got %s`, tokens[0].TokenType, resp.TokenType)
820 }
821 }