Files
where_woof/frontend/internal/handlers/auth.go

111 lines
3.2 KiB
Go

package handlers
import (
"errors"
"net/http"
"strings"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jackc/pgx/v5/pgtype"
"wherewoof/frontend/internal/auth"
"wherewoof/frontend/internal/db"
)
func (a *App) RegisterPage(w http.ResponseWriter, r *http.Request) {
a.render(w, r, "register", "Create account", nil, "")
}
func (a *App) Register(w http.ResponseWriter, r *http.Request) {
email := strings.ToLower(strings.TrimSpace(r.FormValue("email")))
password := r.FormValue("password")
name := strings.TrimSpace(r.FormValue("name"))
if email == "" || password == "" {
a.render(w, r, "register", "Create account", nil, "Email and password are required.")
return
}
if confirm := strings.ToLower(strings.TrimSpace(r.FormValue("confirm_email"))); confirm != "" && confirm != email {
a.render(w, r, "register", "Create account", nil, "Emails do not match.")
return
}
if len(password) < 8 {
a.render(w, r, "register", "Create account", nil, "Password must be at least 8 characters.")
return
}
if len([]byte(password)) > 72 {
a.render(w, r, "register", "Create account", nil, "Password must be 72 bytes or fewer.")
return
}
hash, err := auth.HashPassword(password)
if err != nil {
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
user, err := a.Queries.CreateUser(r.Context(), db.CreateUserParams{
Email: email,
PasswordHash: hash,
Name: pgtype.Text{String: name, Valid: name != ""},
})
if err != nil {
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) && pgErr.Code == "23505" {
a.render(w, r, "register", "Create account", nil, "That email is already registered.")
return
}
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
if err := auth.SetUserID(w, r, user.ID); err != nil {
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
next := r.FormValue("next")
if next == "" || !strings.HasPrefix(next, "/") || strings.HasPrefix(next, "//") {
next = "/account"
}
http.Redirect(w, r, next, http.StatusSeeOther)
}
func (a *App) LoginPage(w http.ResponseWriter, r *http.Request) {
a.render(w, r, "login", "Log in", nil, "")
}
func (a *App) Login(w http.ResponseWriter, r *http.Request) {
email := strings.ToLower(strings.TrimSpace(r.FormValue("email")))
password := r.FormValue("password")
user, err := a.Queries.GetUserByEmail(r.Context(), email)
if err != nil {
if !errors.Is(err, pgx.ErrNoRows) {
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
a.render(w, r, "login", "Log in", nil, "Invalid email or password.")
return
}
if !auth.CheckPassword(user.PasswordHash, password) {
a.render(w, r, "login", "Log in", nil, "Invalid email or password.")
return
}
if err := auth.SetUserID(w, r, user.ID); err != nil {
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
next := r.FormValue("next")
if next == "" || !strings.HasPrefix(next, "/") || strings.HasPrefix(next, "//") {
next = "/account"
}
http.Redirect(w, r, next, http.StatusSeeOther)
}
func (a *App) Logout(w http.ResponseWriter, r *http.Request) {
_ = auth.Clear(w, r)
http.Redirect(w, r, "/", http.StatusSeeOther)
}