Add signup endpoint and handler

This commit is contained in:
Leo 2026-08-17 11:15:45 +03:00
parent 6b8f02fa0f
commit e1b63d5deb
4 changed files with 78 additions and 2 deletions

View file

@ -39,6 +39,7 @@ func main() {
// Routes
mux.HandleFunc("/login", handler.LoginHandler)
mux.HandleFunc("/user", handler.UserHandler)
mux.HandleFunc("/signup", handler.SignUpHandler)
// Apply middleware (CORS first, then logging)
muxMiddleware := handlers.LoggingMiddleware(handlers.CORSMiddleware(mux))

View file

@ -1,6 +1,7 @@
package auth
import (
"crypto/rand"
"crypto/subtle"
"encoding/base64"
"fmt"
@ -9,6 +10,32 @@ import (
"golang.org/x/crypto/argon2"
)
const (
argon2Time = 3
argon2Memory = 65536
argon2Threads = 4
argon2KeyLen = 32
)
func HashPassword(password string) (string, error) {
salt := make([]byte, 16)
_, err := rand.Read(salt)
if err != nil {
return "", err
}
hash := argon2.IDKey([]byte(password), salt, argon2Time, argon2Memory, argon2Threads, argon2KeyLen)
hashedPassword := fmt.Sprintf("$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s",
argon2.Version, argon2Memory, argon2Time, argon2Threads,
base64.RawStdEncoding.EncodeToString(salt),
base64.RawStdEncoding.EncodeToString(hash),
)
return hashedPassword, nil
}
func VerifyPassword(hashedPassword, password string) bool {
// Parse the Argon2 hash format: $argon2id$v=19$m=65536,t=3,p=4$salt$hash
parts := strings.Split(hashedPassword, "$")

View file

@ -2,7 +2,9 @@ package handlers
import (
"encoding/json"
"go-backend/internal/auth"
"go-backend/internal/database/sqlc"
"log"
"net/http"
)
@ -36,12 +38,46 @@ func (h *Handler) SignUpHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
if user != nil {
http.Error(w, "Username already taken", http.StatusBadRequest)
return
}
user, err = h.users.CreateUser(r.Context(), sqlc.InsertUserParams{Username: req.Username, Email: req.Email, PasswordHash: req.Password})
hashedPassword, err := auth.HashPassword(req.Password)
if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
user, err = h.users.CreateUser(r.Context(), sqlc.InsertUserParams{Username: req.Username, Email: req.Email, PasswordHash: hashedPassword})
if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
session, err := h.auth.CreateSession(r.Context(), user.ID)
if err != nil {
log.Printf("Error creating session: %v", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
cookie := &http.Cookie{
Name: "session",
Value: session.Token,
HttpOnly: true,
SameSite: http.SameSiteLaxMode,
MaxAge: 86400, // 24 hours
Path: "/",
}
http.SetCookie(w, cookie)
w.WriteHeader(http.StatusOK)
w.Write([]byte("Success"))
}

View file

@ -7,6 +7,12 @@ import (
"go-backend/internal/auth"
)
type UserResponse struct {
ID string `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
}
func (h *Handler) UserHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
@ -43,6 +49,12 @@ func (h *Handler) UserHandler(w http.ResponseWriter, r *http.Request) {
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)
resp := UserResponse{
ID: user.ID,
Username: user.Username,
Email: user.Email,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}