11package oauth
22
33import (
4+ "context"
45 "crypto/sha256"
56 "encoding/json"
67 "fmt"
@@ -582,35 +583,52 @@ func TestNewCallbackHandler(t *testing.T) {
582583 }
583584}
584585
585- // TestTokenSavedUnderOriginalIdentifier verifies that when the canonical
586- // Gmail address differs from the user-supplied identifier (e.g. dotted
587- // alias), the token is saved under the original identifier — not the
588- // canonical one. This is the key invariant enforced by authorize():
589- // sameGoogleAccount() accepts the alias, and saveToken() uses the
590- // original email.
586+ // TestTokenSavedUnderOriginalIdentifier verifies that when the Gmail
587+ // profile API returns a canonical address that differs from the
588+ // user-supplied identifier (e.g. dotted alias), the token is saved
589+ // under the original identifier — not the canonical one.
590+ //
591+ // This test exercises the same resolveTokenEmail + saveToken path
592+ // that authorize() uses, with a mock Gmail profile server. If
593+ // authorize() ever switches to saving under canonicalEmail, this
594+ // test fails.
591595//
592596// Regression test: a previous version saved under canonicalEmail,
593597// breaking HasToken/TokenSource lookups elsewhere in the app.
594598func TestTokenSavedUnderOriginalIdentifier (t * testing.T ) {
599+ const canonicalEmail = "firstlast@gmail.com"
600+
601+ // Mock Gmail profile endpoint returning the canonical address.
602+ srv := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
603+ w .Header ().Set ("Content-Type" , "application/json" )
604+ _ , _ = fmt .Fprintf (w , `{"emailAddress": %q}` , canonicalEmail )
605+ }))
606+ defer srv .Close ()
607+
595608 mgr := setupTestManager (t , Scopes )
609+ mgr .profileURL = srv .URL
596610
597611 token := & oauth2.Token {
598612 AccessToken : "test-access-token" ,
599613 TokenType : "Bearer" ,
600614 Expiry : time .Now ().Add (time .Hour ),
601615 }
602616
603- // Simulate what authorize() does: validate via sameGoogleAccount,
604- // then save under the original identifier.
617+ // Run the same logic as authorize(): resolve, then save under
618+ // original identifier.
605619 inputEmail := "first.last@gmail.com"
606- canonicalEmail := "firstlast@gmail.com"
607-
608- if ! sameGoogleAccount (inputEmail , canonicalEmail ) {
609- t .Fatalf ("sameGoogleAccount(%q, %q) = false, want true" ,
610- inputEmail , canonicalEmail )
620+ resolvedEmail , err := mgr .resolveTokenEmail (
621+ context .Background (), inputEmail , token ,
622+ )
623+ if err != nil {
624+ t .Fatalf ("resolveTokenEmail: %v" , err )
625+ }
626+ if resolvedEmail != canonicalEmail {
627+ t .Fatalf ("resolveTokenEmail = %q, want %q" ,
628+ resolvedEmail , canonicalEmail )
611629 }
612630
613- // Save under original identifier (the authorize() contract)
631+ // authorize() saves under the original identifier, NOT canonical
614632 if err := mgr .saveToken (inputEmail , token , Scopes ); err != nil {
615633 t .Fatalf ("saveToken: %v" , err )
616634 }
@@ -626,7 +644,37 @@ func TestTokenSavedUnderOriginalIdentifier(t *testing.T) {
626644
627645 // Token must NOT exist under the canonical email
628646 if _ , err := mgr .loadToken (canonicalEmail ); err == nil {
629- t .Errorf ("token should NOT exist under canonical %q" , canonicalEmail )
647+ t .Errorf ("token should NOT exist under canonical %q" ,
648+ canonicalEmail )
649+ }
650+ }
651+
652+ // TestResolveTokenEmail_RejectsMismatch verifies that resolveTokenEmail
653+ // rejects tokens where the profile email is for a different account.
654+ func TestResolveTokenEmail_RejectsMismatch (t * testing.T ) {
655+ srv := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
656+ w .Header ().Set ("Content-Type" , "application/json" )
657+ _ , _ = fmt .Fprintf (w , `{"emailAddress": "wrong@gmail.com"}` )
658+ }))
659+ defer srv .Close ()
660+
661+ mgr := setupTestManager (t , Scopes )
662+ mgr .profileURL = srv .URL
663+
664+ token := & oauth2.Token {
665+ AccessToken : "test" ,
666+ TokenType : "Bearer" ,
667+ Expiry : time .Now ().Add (time .Hour ),
668+ }
669+
670+ _ , err := mgr .resolveTokenEmail (
671+ context .Background (), "expected@gmail.com" , token ,
672+ )
673+ if err == nil {
674+ t .Fatal ("expected error for mismatched email" )
675+ }
676+ if ! strings .Contains (err .Error (), "token mismatch" ) {
677+ t .Errorf ("error should contain 'token mismatch': %q" , err .Error ())
630678 }
631679}
632680
0 commit comments