auth

Paddy 2014-12-14 Parent:1fb166575e69 Child:0b45e6b9cb94

97:a8b86f5fba78 Go to Latest

auth/oauth2_test.go

Update token test for revocation and new properties. Add the new properties of Token to the compareTokens helper so they're tested for equality. Add a test for revoking tokens. We still need a test that checks revoking tokens by their refresh token.

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