Add an endpoint to validate and register profiles.
Add a newProfileRequest object that defines the user-specified properties of a
new Profile.
Add a helper that validates a newProfileRequest and modifies it for
sanitization, mostly just removing leading and trailing whitespace.
Add MaxNameLength, MaxUsernameLength, and MaxEmailLength constants to hold the
maximum length for those properties.
Add errors to be returned when a users attempts to log in with a profile that is
compromised or locked.
Add the bare bones of a CreateProfileHandler that validates a profile
registration request adn uses it to create a Profile and at least one Login.
Create a requestError struct that is used for returning API errors, along with
constants for the slugs we'll use to signal those errors.
14 "code.secondbit.org/uuid"
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("Get auth grant")),
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",
49 uri, err := url.Parse("https://test.secondbit.org/redirect")
51 t.Fatal("Can't parse URL:", err)
59 err = testContext.SaveClient(client)
61 t.Fatal("Can't store client:", err)
63 err = testContext.AddEndpoint(client.ID, endpoint)
65 t.Fatal("Can't store endpoint:", err)
70 err = testContext.SaveProfile(profile)
72 t.Fatal("Can't store profile:", err)
77 ProfileID: profile.ID,
79 err = testContext.CreateSession(session)
81 t.Fatal("Can't store session:", err)
83 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
85 t.Fatal("Can't build request:", err)
87 cookie := &http.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())
99 params.Set("redirect_uri", endpoint.URI.String())
102 params.Set("scope", "testscope")
105 params.Set("state", "my super secure state string")
107 req.URL.RawQuery = params.Encode()
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())
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())
119 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
120 w = httptest.NewRecorder()
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())
129 redirectedTo := w.Header().Get("Location")
130 red, err := url.Parse(redirectedTo)
132 t.Fatalf(`Being redirected to a non-URL "%s" threw error "%s" for "%s"\n`, redirectedTo, err, req.URL.String())
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())
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())
141 err = testContext.DeleteAuthorizationCode(red.Query().Get("code"))
143 t.Logf(`Unexpected error "%s" deleting grant "%s" for %s`, err, red.Query().Get("code"), req.URL.String())
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())
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())
156 func TestGetAuthorizationCodeCodeInvalidClient(t *testing.T) {
158 store := NewMemstore()
159 testContext := Context{
160 template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ .error }}")),
169 Secret: "super secret!",
170 OwnerID: uuid.NewID(),
171 Name: "My test client",
174 err := testContext.SaveClient(client)
176 t.Fatal("Can't store client:", err)
182 err = testContext.CreateSession(session)
184 t.Fatal("Can't store session:", err)
186 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
188 t.Fatal("Can't build request:", err)
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,
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)
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())
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)
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())
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)
224 if w.Body.String() != "The specified Client couldn’t be found." {
225 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The specified Client couldn’t be found.", w.Body.String())
229 func TestGetAuthorizationCodeCodeInvalidURI(t *testing.T) {
231 store := NewMemstore()
232 testContext := Context{
233 template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ .error }}")),
242 Secret: "super secret!",
243 OwnerID: uuid.NewID(),
244 Name: "My test client",
247 uri, err := url.Parse("https://test.secondbit.org/redirect")
249 t.Fatal("Can't parse URL:", err)
251 err = testContext.SaveClient(client)
253 t.Fatal("Can't store client:", err)
259 err = testContext.CreateSession(session)
261 t.Fatal("Can't store session:", err)
263 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
265 t.Fatal("Can't build request:", err)
267 cookie := &http.Cookie{
268 Name: authCookieName,
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)
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())
284 endpoint := Endpoint{
290 err = testContext.AddEndpoint(client.ID, endpoint)
292 t.Fatal("Can't store endpoint:", err)
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)
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())
304 endpoint2 := Endpoint{
310 err = testContext.AddEndpoint(client.ID, endpoint2)
312 t.Fatal("Can't store endpoint:", err)
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)
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())
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)
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())
336 func TestGetAuthorizationCodeCodeInvalidResponseType(t *testing.T) {
338 store := NewMemstore()
339 testContext := Context{
340 template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ .error }}")),
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",
356 uri, err := url.Parse("https://test.secondbit.org/redirect")
358 t.Fatal("Can't parse URL:", err)
360 endpoint := Endpoint{
366 err = testContext.SaveClient(client)
368 t.Fatal("Can't store client:", err)
370 err = testContext.AddEndpoint(client.ID, endpoint)
372 t.Fatal("Can't store endpoint:", err)
378 err = testContext.CreateSession(session)
380 t.Fatal("Can't store session:", err)
382 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
384 t.Fatal("Can't build request:", err)
386 cookie := &http.Cookie{
387 Name: authCookieName,
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)
403 redirectedTo := w.Header().Get("Location")
404 red, err := url.Parse(redirectedTo)
406 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
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"))
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"))
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())
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)
425 redirectedTo = w.Header().Get("Location")
426 red, err = url.Parse(redirectedTo)
428 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
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"))
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"))
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())
443 func TestGetAuthorizationCodeCodeDenied(t *testing.T) {
445 store := NewMemstore()
446 testContext := Context{
447 template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ .error }}")),
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",
463 uri, err := url.Parse("https://test.secondbit.org/redirect")
465 t.Fatal("Can't parse URL:", err)
467 endpoint := Endpoint{
473 err = testContext.SaveClient(client)
475 t.Fatal("Can't store client:", err)
477 err = testContext.AddEndpoint(client.ID, endpoint)
479 t.Fatal("Can't store endpoint:", err)
485 err = testContext.CreateSession(session)
487 t.Fatal("Can't store session:", err)
489 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
491 t.Fatal("Can't build request:", err)
493 cookie := &http.Cookie{
494 Name: authCookieName,
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")
505 data.Set("grant", "denied")
506 req.URL.RawQuery = params.Encode()
507 req.Body = ioutil.NopCloser(bytes.NewBufferString(data.Encode()))
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)
514 redirectedTo := w.Header().Get("Location")
515 red, err := url.Parse(redirectedTo)
517 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
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"))
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"))
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())
532 func TestGetAuthorizationCodeCodeLoginRedirect(t *testing.T) {
534 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
536 t.Fatal("Can't build request:", err)
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)
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())
547 uri, err := url.Parse("https://client.secondbit.org/")
549 t.Error("Unexpected error", err)
551 testContext := Context{
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)
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)
566 func TestCheckCookie(t *testing.T) {
568 req, err := http.NewRequest("GET", "https://auth.secondbit.org", nil)
570 t.Error("Unexpected error creating base request:", err)
572 store := NewMemstore()
573 testContext := Context{
576 session, err := checkCookie(req, testContext)
577 if err != ErrNoSession {
578 t.Errorf("Expected ErrNoSession, got %s", err)
584 err = testContext.CreateSession(session)
586 t.Error("Unexpected error persisting session:", err)
588 invalidSession := Session{
592 err = testContext.CreateSession(invalidSession)
594 t.Error("Unexpected error persisting session:", err)
596 result, err := checkCookie(req, testContext)
597 if err != ErrNoSession {
598 t.Errorf("Expected ErrNoSession, got %s", err)
600 req.AddCookie(&http.Cookie{
602 Value: "wrong value",
604 result, err = checkCookie(req, testContext)
605 if err != ErrNoSession {
606 t.Error("Expected ErrNoSession, got", err)
608 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
610 t.Error("Unexpected error creating base request:", err)
612 req.AddCookie(&http.Cookie{
613 Name: "Stillwrongcookie",
616 result, err = checkCookie(req, testContext)
617 if err != ErrNoSession {
618 t.Error("Expected ErrNoSession, got", err)
620 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
622 t.Error("Unexpected error creating base request:", err)
624 req.AddCookie(&http.Cookie{
625 Name: authCookieName,
626 Value: "wrong value",
628 result, err = checkCookie(req, testContext)
629 if err != ErrInvalidSession {
630 t.Error("Expected ErrInvalidSession, got", err)
632 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
634 t.Error("Unexpected error creating base request:", err)
636 req.AddCookie(&http.Cookie{
637 Name: authCookieName,
638 Value: invalidSession.ID,
640 result, err = checkCookie(req, testContext)
641 if err != ErrInvalidSession {
642 t.Error("Expected ErrInvalidSession, got", err)
644 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
646 t.Error("Unexpected error creating base request:", err)
648 req.AddCookie(&http.Cookie{
649 Name: authCookieName,
652 result, err = checkCookie(req, testContext)
654 t.Error("Unexpected error:", err)
656 success, field, expectation, outcome := compareSessions(session, result)
658 t.Errorf(`Expected field %s to be %v, but got %v`, field, expectation, outcome)
662 func TestBuildLoginRedirect(t *testing.T) {
664 req, err := http.NewRequest("GET", "https://client.secondbit.org/my/awesome/path?has=query¶ms=to&screw=this&all=up", nil)
666 t.Error("Unexpected error creating base request:", err)
668 result := buildLoginRedirect(req, Context{})
670 t.Error("Expected empty string as the result, got", result)
672 uri, err := url.Parse("https://auth.secondbit.org/login?query=string&other=param")
674 t.Error("Unexpected error parsing URL:", err)
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)
684 func TestAuthenticateHelper(t *testing.T) {
686 store := NewMemstore()
693 Passphrase: "55d87acb9adff90a0d8e4c9b77f239c2d6e3a1945dbd09b0270467411198db25",
695 Salt: "this is a super secure random salt",
698 LockedUntil: time.Time{},
700 PassphraseResetCreated: time.Time{},
702 LastSeen: time.Time{},
706 Value: "test@example.com",
707 ProfileID: profile.ID,
709 LastUsed: time.Time{},
711 err := context.SaveProfile(profile)
713 t.Error("Error saving profile:", err)
715 err = context.AddLogin(login)
717 t.Error("Error adding login:", err)
719 response, err := authenticate("test@example.com", "a really secure password", context)
721 t.Error("Unexpected error:", err)
723 success, field, expectation, result := compareProfiles(profile, response)
725 t.Errorf(`Expected field %s to be "%v", got "%v"`, field, expectation, result)
727 response, err = authenticate("test2@example.com", "a really secure password", context)
728 if err != ErrIncorrectAuth {
729 t.Error("Expected ErrIncorrectAuth, got", err)
731 response, err = authenticate("test@example.com", "not the right password", context)
732 if err != ErrIncorrectAuth {
733 t.Error("Expected ErrIncorrectAuth, got", err)
736 phrase := "doesn't really matter, the scheme doesn't exist"
737 change := ProfileChange{
738 PassphraseScheme: &scheme,
741 err = context.UpdateProfile(profile.ID, change)
743 t.Error("Unexpected error:", err)
745 response, err = authenticate("test@example.com", "not the right password", context)
746 if err != ErrInvalidPassphraseScheme {
747 t.Error("Expected ErrIncorrectAuth, got", err)
751 func TestGetTokenHandler(t *testing.T) {
753 store := NewMemstore()
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",
768 authCode := AuthorizationCode{
774 RedirectURI: "https://client.secondbit.org/",
776 ProfileID: uuid.NewID(),
778 err := context.SaveAuthorizationCode(authCode)
780 t.Error("Error saving auth code:", err)
782 err = context.SaveClient(client)
784 t.Error("Error saving client:", err)
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))
793 t.Error("Error constructing request:", err)
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)
802 t.Error("Error unmarshalling response:", err)
803 t.Error("Response:", w.Body.String())
805 if resp.AccessToken == "" {
806 t.Error("Got blank access token back")
808 if resp.RefreshToken == "" {
809 t.Error("Got blank refresh token back")
811 if resp.TokenType == "" {
812 t.Error("Got blank token type back")
814 if resp.ExpiresIn == 0 {
815 t.Error("Got blank expires in back")
817 tokens, err := context.GetTokensByProfileID(authCode.ProfileID, 1, 0)
819 t.Error("Error retrieving token:", err)
821 if len(tokens) != 1 {
822 t.Fatalf("Expected %d tokens, got %d", 1, len(tokens))
824 if tokens[0].AccessToken != resp.AccessToken {
825 t.Errorf(`Expected access token to be "%s", got "%s"`, tokens[0].AccessToken, resp.AccessToken)
827 if tokens[0].RefreshToken != resp.RefreshToken {
828 t.Errorf(`Expected refresh token to be "%s", got "%s"`, tokens[0].RefreshToken, resp.RefreshToken)
830 if tokens[0].ExpiresIn != resp.ExpiresIn {
831 t.Errorf(`Expected expires in to be %d, got %d`, tokens[0].ExpiresIn, resp.ExpiresIn)
833 if tokens[0].TokenType != resp.TokenType {
834 t.Errorf(`Expected token type to be %s, got %s`, tokens[0].TokenType, resp.TokenType)