feat: complete email verification flow

This commit is contained in:
Florian Sylvain
2024-05-15 19:16:55 +02:00
parent aabdbfae95
commit a35e7c4283
13 changed files with 228 additions and 124 deletions
+11 -1
View File
@@ -47,6 +47,16 @@ func SetJwtCookie(w *http.ResponseWriter, userId uint32) error {
return nil
}
func SomeUsersVerified() bool {
users := Container.ListUsersUseCase.ListUsers()
for _, localUser := range users {
if localUser.IsVerified {
return true
}
}
return false
}
func IsUserTableEmpty() bool {
users := Container.ListUsersUseCase.ListUsers()
return len(users) == 0
@@ -131,7 +141,7 @@ func login(w http.ResponseWriter, r *http.Request) {
}
func register(w http.ResponseWriter, r *http.Request) {
if !IsLoggedIn(r) && !IsUserTableEmpty() {
if !IsLoggedIn(r) && !SomeUsersVerified() {
http.Error(w, "You are not allowed to create a user. Log in or reset database.", http.StatusForbidden)
return
}
+2
View File
@@ -17,6 +17,7 @@ type UseCases struct {
GetUserUseCase *useCases.GetUserUseCase
CreateUserUseCase *useCases.CreateUserUseCase
UpdateUserUseCase *useCases.UpdateUserUseCase
DeleteUserUseCase *useCases.DeleteUserUseCase
ListUsersUseCase *useCases.ListUsersUseCase
GetPageUseCase *useCases.GetPageUseCase
SendMailUseCase *useCases.SendMailUseCase
@@ -51,6 +52,7 @@ func InitContainer() {
GetUserUseCase: useCases.NewGetUserUseCase(db),
CreateUserUseCase: useCases.NewCreateUserUseCase(db),
UpdateUserUseCase: useCases.NewUpdateUserUseCase(db),
DeleteUserUseCase: useCases.NewDeleteUserUseCase(db),
ListUsersUseCase: useCases.NewListUsersUseCase(db),
GetPageUseCase: useCases.NewGetPageUseCase(),
SendMailUseCase: useCases.NewSendMailUseCase(),
+1
View File
@@ -113,6 +113,7 @@ func NewPageRouter() http.Handler {
r.Use(IsLoggedInMiddleware)
r.Use(IsNotVerifiedMiddleware)
r.Get("/register/pending", GetRegisterPendingPage)
r.Post("/register/pending", PostRegisterPendingPage)
r.Get("/register/validate", GetRegisterValidatePage)
})
+2 -2
View File
@@ -53,7 +53,7 @@ func PostLoginPage(w http.ResponseWriter, r *http.Request) {
if err != nil {
r.Method = http.MethodGet
GetLoginPageHandler(&LoginPage{
PageError: NewPageError("Invalid form data format."),
PageError: NewPageError("Invalid username or password format."),
Username: r.FormValue("username"),
})(w, r)
return
@@ -63,7 +63,7 @@ func PostLoginPage(w http.ResponseWriter, r *http.Request) {
if err != nil {
r.Method = http.MethodGet
GetLoginPageHandler(&LoginPage{
PageError: NewPageError("Invalid username or password."),
PageError: NewPageError("Invalid username or password combination."),
Username: r.FormValue("username"),
})(w, r)
return
+35 -15
View File
@@ -6,21 +6,31 @@ import (
"os"
)
type RegisterPageError struct {
Email bool `json:"email"`
Password bool `json:"password"`
Username bool `json:"username"`
}
type RegisterPage struct {
PageError *PageError `json:"error"`
Username string `json:"username"`
Email string `json:"email"`
PageError *RegisterPageError `json:"error"`
Username string `json:"username"`
Email string `json:"email"`
}
var EmptyRegisterPage = &RegisterPage{
PageError: NewPageError(""),
Username: "",
Email: "",
PageError: &RegisterPageError{
Email: false,
Password: false,
Username: false,
},
Username: "",
Email: "",
}
func GetRegisterPageHandler(registerPage *RegisterPage) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if IsLoggedIn(r) || !IsUserTableEmpty() {
if (IsLoggedIn(r) && IsVerified(r)) || SomeUsersVerified() {
http.Redirect(w, r, "/home", http.StatusSeeOther)
return
}
@@ -53,9 +63,13 @@ func PostRegisterPage(w http.ResponseWriter, r *http.Request) {
if err != nil {
r.Method = http.MethodGet
GetRegisterPageHandler(&RegisterPage{
PageError: NewPageError("Invalid register form data format."),
Username: r.FormValue("username"),
Email: r.FormValue("email"),
PageError: &RegisterPageError{
Email: true,
Password: true,
Username: true,
},
Username: r.FormValue("username"),
Email: r.FormValue("email"),
})(w, r)
return
}
@@ -63,17 +77,23 @@ func PostRegisterPage(w http.ResponseWriter, r *http.Request) {
verificationCode := uuid.NewString()
createdUser, err := getNewUser(credentials, verificationCode)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
r.Method = http.MethodGet
GetRegisterPageHandler(&RegisterPage{
PageError: &RegisterPageError{
Email: true,
Password: false,
Username: false,
},
Username: r.FormValue("username"),
Email: r.FormValue("email"),
})(w, r)
return
}
err = Container.SendMailUseCase.SendMail(createdUser.Email, "mailValidation", map[string]string{
_ = Container.SendMailUseCase.SendMail(createdUser.Email, "mailValidation", map[string]string{
"Host": os.Getenv("HOST"),
"VerificationCode": verificationCode,
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
_ = SetJwtCookie(&w, createdUser.ID)
+24 -1
View File
@@ -1,6 +1,29 @@
package api
import "net/http"
import (
"github.com/go-chi/jwtauth/v5"
"log"
"net/http"
)
func PostRegisterPendingPage(w http.ResponseWriter, r *http.Request) {
token, _ := jwtauth.VerifyRequest(
TokenAuth, r,
jwtauth.TokenFromCookie,
jwtauth.TokenFromHeader,
jwtauth.TokenFromQuery)
userId := token.PrivateClaims()["user_id"].(float64)
err := Container.DeleteUserUseCase.DeleteUser(uint32(userId))
if err != nil {
log.Println(err)
r.Method = http.MethodGet
http.Redirect(w, r, "/register/pending", http.StatusSeeOther)
}
RemoveJwtCookie(w)
http.Redirect(w, r, "/register", http.StatusSeeOther)
}
func GetRegisterPendingPage(w http.ResponseWriter, _ *http.Request) {
registerPendingTmpl, _ := Container.GetPageUseCase.GetPage("registerPending", map[string]interface{}{