Make all tests that deal with the store interfaces go through the Context. This
is mainly important so that pre- and post- save/retrieval/deletion/whatever
transforms can be done without doing them in every single implementation of the
store.
Change the Endpoint URI property to be a string, not a *url.URL. This makes
testing easier, JSON responses cleaner, and is all around just a better
strategy. Just because we turn it into a URL every now and then doesn't mean
that's how we need to store it.
Add JSON tags to the Client type and Endpoint type.
Create normalizeURI and normalizeURIString methods to... well, normalize the
Endpoint URIs. This makes it so that we can compare them, and forgive some
arbitrary user behaviour (like slashes, etc.)
Add a NormalizedURI property to the Endpoint type. This is where we store the
NormalizedURI, which is what we'll be using when we want to check if an endpoint
is valid or not. For the sake of tests and predictability, however, we always
want to redirect to the URI, not the NormalizedURI.
Add checks to the Client creation API endpoint to give better errors. Now
leaving out the Type won't be considered an invalid type, it will be considered
a missing parameter. An empty name will be reported as a missing parameter, a
name with too few characters will be reported as an insufficient name, and a
name with too many characters will be reported as an overflow name. We gather as
many of these errors as apply before returning.
Check if an Endpoint URI is absolute before adding it as an endpoint, or return
an invalid value error if it is not.
Always return the errors array when creating a client. We could succeed in
creating one or more things and still have errors. We should return anything
that's created _as well as_ any errors encountered.
Add unit testing for our CreateClientHandler.
Fix our oauth2 tests so that if there's an error in the body, it's in the test
logs. This should help debugging significantly.
Fix our oauth2 tests so that the Profile only requires 1 iteration for its
password hashing. This means each time we want to validate a session, it doesn't
add a full second to our test runs. This is a big speed improvement for our
tests.
Add test helper methods for comparing API errors, API responses, and filling in
server-generated information in a response that it's impossible to have an
expectation around (e.g., IDs) so that we can use our comparison helpers to
check if a response is as we expect it.
Fix a typo in our Context helpers that was reporting no sessionStore being set
_only_ when a sessionStore was set. So yes, the opposite of what we wanted.
Oops. This was discovered by passing all our tests through the context. methods
instead of operating on the stores themselves.
14 "code.secondbit.org/uuid.hg"
23 func stripParam(param string, u *url.URL) {
26 u.RawQuery = q.Encode()
29 func TestGetAuthorizationCodeCodeSuccess(t *testing.T) {
31 store := NewMemstore()
32 testContext := Context{
33 template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ if not .error }}Get auth grant{{ else }}{{ .error }}{{ end }}")),
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",
52 URI: "https://test.secondbit.org/redirect",
55 err := testContext.SaveClient(client)
57 t.Fatal("Can't store client:", err)
59 err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint})
61 t.Fatal("Can't store endpoint:", err)
66 err = testContext.SaveProfile(profile)
68 t.Fatal("Can't store profile:", err)
73 ProfileID: profile.ID,
75 err = testContext.CreateSession(session)
77 t.Fatal("Can't store session:", err)
79 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
81 t.Fatal("Can't build request:", err)
83 cookie := &http.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())
95 params.Set("redirect_uri", endpoint.URI)
98 params.Set("scope", "testscope")
101 params.Set("state", "my super secure state string")
103 req.URL.RawQuery = params.Encode()
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())
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())
116 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
117 w = httptest.NewRecorder()
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())
126 redirectedTo := w.Header().Get("Location")
127 red, err := url.Parse(redirectedTo)
129 t.Fatalf(`Being redirected to a non-URL "%s" threw error "%s" for "%s"\n`, redirectedTo, err, req.URL.String())
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())
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())
138 err = testContext.DeleteAuthorizationCode(red.Query().Get("code"))
140 t.Logf(`Unexpected error "%s" deleting grant "%s" for %s`, err, red.Query().Get("code"), req.URL.String())
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())
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())
153 func TestGetAuthorizationCodeCodeInvalidClient(t *testing.T) {
155 store := NewMemstore()
156 testContext := Context{
157 template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ .error }}")),
166 Secret: "super secret!",
167 OwnerID: uuid.NewID(),
168 Name: "My test client",
171 err := testContext.SaveClient(client)
173 t.Fatal("Can't store client:", err)
179 err = testContext.CreateSession(session)
181 t.Fatal("Can't store session:", err)
183 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
185 t.Fatal("Can't build request:", err)
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,
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)
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())
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)
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())
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)
221 if w.Body.String() != "The specified Client couldn’t be found." {
222 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The specified Client couldn’t be found.", w.Body.String())
226 func TestGetAuthorizationCodeCodeInvalidURI(t *testing.T) {
228 store := NewMemstore()
229 testContext := Context{
230 template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ .error }}")),
239 Secret: "super secret!",
240 OwnerID: uuid.NewID(),
241 Name: "My test client",
244 err := testContext.SaveClient(client)
246 t.Fatal("Can't store client:", err)
252 err = testContext.CreateSession(session)
254 t.Fatal("Can't store session:", err)
256 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
258 t.Fatal("Can't build request:", err)
260 cookie := &http.Cookie{
261 Name: authCookieName,
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)
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())
277 endpoint := Endpoint{
280 URI: "https://test.secondbit.org/redirect",
283 err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint})
285 t.Fatal("Can't store endpoint:", err)
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)
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())
297 endpoint2 := Endpoint{
300 URI: "https://test.secondbit.org/redirect",
303 err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint2})
305 t.Fatal("Can't store endpoint:", err)
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)
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())
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)
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())
329 func TestGetAuthorizationCodeCodeInvalidResponseType(t *testing.T) {
331 store := NewMemstore()
332 testContext := Context{
333 template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ .error }}")),
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",
349 endpoint := Endpoint{
352 URI: "https://test.secondbit.org/redirect",
355 err := testContext.SaveClient(client)
357 t.Fatal("Can't store client:", err)
359 err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint})
361 t.Fatal("Can't store endpoint:", err)
367 err = testContext.CreateSession(session)
369 t.Fatal("Can't store session:", err)
371 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
373 t.Fatal("Can't build request:", err)
375 cookie := &http.Cookie{
376 Name: authCookieName,
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)
392 redirectedTo := w.Header().Get("Location")
393 red, err := url.Parse(redirectedTo)
395 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
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"))
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"))
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())
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)
414 redirectedTo = w.Header().Get("Location")
415 red, err = url.Parse(redirectedTo)
417 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
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"))
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"))
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())
432 func TestGetAuthorizationCodeCodeDenied(t *testing.T) {
434 store := NewMemstore()
435 testContext := Context{
436 template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ .error }}")),
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",
452 endpoint := Endpoint{
455 URI: "https://test.secondbit.org/redirect",
458 err := testContext.SaveClient(client)
460 t.Fatal("Can't store client:", err)
462 err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint})
464 t.Fatal("Can't store endpoint:", err)
470 err = testContext.CreateSession(session)
472 t.Fatal("Can't store session:", err)
474 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
476 t.Fatal("Can't build request:", err)
478 cookie := &http.Cookie{
479 Name: authCookieName,
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")
490 data.Set("grant", "denied")
491 req.URL.RawQuery = params.Encode()
492 req.Body = ioutil.NopCloser(bytes.NewBufferString(data.Encode()))
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)
499 redirectedTo := w.Header().Get("Location")
500 red, err := url.Parse(redirectedTo)
502 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
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"))
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"))
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())
517 func TestGetAuthorizationCodeCodeLoginRedirect(t *testing.T) {
519 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
521 t.Fatal("Can't build request:", err)
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)
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())
532 uri, err := url.Parse("https://client.secondbit.org/")
534 t.Error("Unexpected error", err)
536 testContext := Context{
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)
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)
551 func TestCheckCookie(t *testing.T) {
553 req, err := http.NewRequest("GET", "https://auth.secondbit.org", nil)
555 t.Error("Unexpected error creating base request:", err)
557 store := NewMemstore()
558 testContext := Context{
561 session, err := checkCookie(req, testContext)
562 if err != ErrNoSession {
563 t.Errorf("Expected ErrNoSession, got %s", err)
569 err = testContext.CreateSession(session)
571 t.Error("Unexpected error persisting session:", err)
573 invalidSession := Session{
577 err = testContext.CreateSession(invalidSession)
579 t.Error("Unexpected error persisting session:", err)
581 result, err := checkCookie(req, testContext)
582 if err != ErrNoSession {
583 t.Errorf("Expected ErrNoSession, got %s", err)
585 req.AddCookie(&http.Cookie{
587 Value: "wrong value",
589 result, err = checkCookie(req, testContext)
590 if err != ErrNoSession {
591 t.Error("Expected ErrNoSession, got", err)
593 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
595 t.Error("Unexpected error creating base request:", err)
597 req.AddCookie(&http.Cookie{
598 Name: "Stillwrongcookie",
601 result, err = checkCookie(req, testContext)
602 if err != ErrNoSession {
603 t.Error("Expected ErrNoSession, got", err)
605 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
607 t.Error("Unexpected error creating base request:", err)
609 req.AddCookie(&http.Cookie{
610 Name: authCookieName,
611 Value: "wrong value",
613 result, err = checkCookie(req, testContext)
614 if err != ErrInvalidSession {
615 t.Error("Expected ErrInvalidSession, got", err)
617 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
619 t.Error("Unexpected error creating base request:", err)
621 req.AddCookie(&http.Cookie{
622 Name: authCookieName,
623 Value: invalidSession.ID,
625 result, err = checkCookie(req, testContext)
626 if err != ErrInvalidSession {
627 t.Error("Expected ErrInvalidSession, got", err)
629 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
631 t.Error("Unexpected error creating base request:", err)
633 req.AddCookie(&http.Cookie{
634 Name: authCookieName,
637 result, err = checkCookie(req, testContext)
639 t.Error("Unexpected error:", err)
641 success, field, expectation, outcome := compareSessions(session, result)
643 t.Errorf(`Expected field %s to be %v, but got %v`, field, expectation, outcome)
647 func TestBuildLoginRedirect(t *testing.T) {
649 req, err := http.NewRequest("GET", "https://client.secondbit.org/my/awesome/path?has=query¶ms=to&screw=this&all=up", nil)
651 t.Error("Unexpected error creating base request:", err)
653 result := buildLoginRedirect(req, Context{})
655 t.Error("Expected empty string as the result, got", result)
657 uri, err := url.Parse("https://auth.secondbit.org/login?query=string&other=param")
659 t.Error("Unexpected error parsing URL:", err)
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)
669 func TestAuthenticateHelper(t *testing.T) {
671 store := NewMemstore()
678 Passphrase: "f3a4ac4f1d657b2e6e776d24213e39406d50a87a52691a2a78891425af1271d0",
680 Salt: "d82d92cfa8bfb5a08270ebbf39a3710d24b352b937fcc8959ebcb40384cc616b",
683 LockedUntil: time.Time{},
685 PassphraseResetCreated: time.Time{},
687 LastSeen: time.Time{},
691 Value: "test@example.com",
692 ProfileID: profile.ID,
694 LastUsed: time.Time{},
696 err := context.SaveProfile(profile)
698 t.Error("Error saving profile:", err)
700 err = context.AddLogin(login)
702 t.Error("Error adding login:", err)
704 response, err := authenticate("test@example.com", "mysecurepassphrase", context)
706 t.Error("Unexpected error:", err)
708 success, field, expectation, result := compareProfiles(profile, response)
710 t.Errorf(`Expected field %s to be "%v", got "%v"`, field, expectation, result)
712 response, err = authenticate("test2@example.com", "mysecurepassphrase", context)
713 if err != ErrIncorrectAuth {
714 t.Error("Expected ErrIncorrectAuth, got", err)
716 response, err = authenticate("test@example.com", "not the right password", context)
717 if err != ErrIncorrectAuth {
718 t.Error("Expected ErrIncorrectAuth, got", err)
721 phrase := "doesn't really matter, the scheme doesn't exist"
722 change := ProfileChange{
723 PassphraseScheme: &scheme,
726 err = context.UpdateProfile(profile.ID, change)
728 t.Error("Unexpected error:", err)
730 response, err = authenticate("test@example.com", "not the right password", context)
731 if err != ErrInvalidPassphraseScheme {
732 t.Error("Expected ErrIncorrectAuth, got", err)
736 func TestGetTokenHandler(t *testing.T) {
738 store := NewMemstore()
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",
753 authCode := AuthorizationCode{
759 RedirectURI: "https://client.secondbit.org/",
761 ProfileID: uuid.NewID(),
763 err := context.SaveAuthorizationCode(authCode)
765 t.Error("Error saving auth code:", err)
767 err = context.SaveClient(client)
769 t.Error("Error saving client:", err)
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))
778 t.Error("Error constructing request:", err)
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)
787 t.Error("Error unmarshalling response:", err)
788 t.Error("Response:", w.Body.String())
790 if resp.AccessToken == "" {
791 t.Error("Got blank access token back")
793 if resp.RefreshToken == "" {
794 t.Error("Got blank refresh token back")
796 if resp.TokenType == "" {
797 t.Error("Got blank token type back")
799 if resp.ExpiresIn == 0 {
800 t.Error("Got blank expires in back")
802 tokens, err := context.GetTokensByProfileID(authCode.ProfileID, 1, 0)
804 t.Error("Error retrieving token:", err)
806 if len(tokens) != 1 {
807 t.Fatalf("Expected %d tokens, got %d", 1, len(tokens))
809 if tokens[0].AccessToken != resp.AccessToken {
810 t.Errorf(`Expected access token to be "%s", got "%s"`, tokens[0].AccessToken, resp.AccessToken)
812 if tokens[0].RefreshToken != resp.RefreshToken {
813 t.Errorf(`Expected refresh token to be "%s", got "%s"`, tokens[0].RefreshToken, resp.RefreshToken)
815 if tokens[0].ExpiresIn != resp.ExpiresIn {
816 t.Errorf(`Expected expires in to be %d, got %d`, tokens[0].ExpiresIn, resp.ExpiresIn)
818 if tokens[0].TokenType != resp.TokenType {
819 t.Errorf(`Expected token type to be %s, got %s`, tokens[0].TokenType, resp.TokenType)