func handleEmailVerifyFunc(verifiedTpl *template.Template, issuer url.URL, keysFunc func() ([]key.PublicKey, error), userManager *user.Manager) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { q := r.URL.Query() token := q.Get("token") keys, err := keysFunc() if err != nil { execTemplateWithStatus(w, verifiedTpl, emailVerifiedTemplateData{ Error: "There's been an error processing your request.", Message: "Plesae try again later.", }, http.StatusInternalServerError) return } ev, err := user.ParseAndVerifyEmailVerificationToken(token, issuer, keys) if err != nil { execTemplateWithStatus(w, verifiedTpl, emailVerifiedTemplateData{ Error: "Bad Email Verification Token", Message: "That was not a verifiable token.", }, http.StatusBadRequest) return } cbURL, err := userManager.VerifyEmail(ev) if err != nil { switch err { case user.ErrorEmailAlreadyVerified: execTemplateWithStatus(w, verifiedTpl, emailVerifiedTemplateData{ Error: "Invalid Verification Link", Message: "Your email link has expired or has already been verified.", }, http.StatusBadRequest) case user.ErrorEVEmailDoesntMatch: execTemplateWithStatus(w, verifiedTpl, emailVerifiedTemplateData{ Error: "Invalid Verification Link", Message: "Your email link does not match the email address on file. Perhaps you have a more recent verification link?", }, http.StatusBadRequest) default: execTemplateWithStatus(w, verifiedTpl, emailVerifiedTemplateData{ Error: "Error Processing Request", Message: "Please try again later.", }, http.StatusInternalServerError) } return } http.SetCookie(w, &http.Cookie{ HttpOnly: true, Name: "ShowEmailVerifiedMessage", MaxAge: int(60 * 5), Expires: time.Now().Add(time.Minute * 5), }) http.Redirect(w, r, cbURL.String(), http.StatusSeeOther) } }
func TestSendEmailVerificationEmail(t *testing.T) { tests := []struct { userID string hasEmailer bool wantEmailAddress string wantURL bool wantEmail bool wantErr bool }{ { // typical case with an emailer. userID: "ID-1", hasEmailer: true, wantURL: false, wantEmailAddress: "*****@*****.**", wantEmail: true, }, { // typical case without an emailer. userID: "ID-1", hasEmailer: false, wantURL: true, wantEmailAddress: "*****@*****.**", wantEmail: false, }, { // no such user. userID: "*****@*****.**", hasEmailer: false, wantErr: true, }, { // user with no local password. userID: "*****@*****.**", hasEmailer: false, wantErr: true, }, } for i, tt := range tests { ue, emailer, pubKey := makeTestFixtures() if !tt.hasEmailer { ue.SetEmailer(nil) } verifyLink, err := ue.SendEmailVerification(tt.userID, clientID, redirURL) if tt.wantErr { if err == nil { t.Errorf("case %d: want non-nil err.", i) } continue } if tt.wantURL { if verifyLink == nil { t.Errorf("case %d: want non-nil verifyLink", i) continue } } else if verifyLink != nil { t.Errorf("case %d: want verifyLink==nil, got==%v", i, verifyLink.String()) continue } if tt.wantEmail { if !emailer.sent { t.Errorf("case %d: want emailer.sent", i) continue } // In this case the link is in the email. verifyLink, err = url.Parse(emailer.text) if err != nil { t.Errorf("case %d: want non-nil err, got: %q", i, err) } if tt.wantEmailAddress != emailer.to[0] { t.Errorf("case %d: want==%v, got==%v", i, tt.wantEmailAddress, emailer.to[0]) } if fromAddress != emailer.from { t.Errorf("case %d: want==%v, got==%v", i, fromAddress, emailer.from) } } else if emailer.sent { t.Errorf("case %d: want !emailer.sent", i) } token := verifyLink.Query().Get("token") ev, err := user.ParseAndVerifyEmailVerificationToken(token, issuerURL, []key.PublicKey{*pubKey}) if diff := pretty.Compare(redirURL, ev.Callback()); diff != "" { t.Errorf("case %d: Compare(want, got) = %v", i, diff) } if tt.wantEmailAddress != ev.Email() { t.Errorf("case %d: want==%v, got==%v", i, tt.wantEmailAddress, ev.UserID()) } } }