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 // Routes
mux.HandleFunc("/login", handler.LoginHandler) mux.HandleFunc("/login", handler.LoginHandler)
mux.HandleFunc("/user", handler.UserHandler) mux.HandleFunc("/user", handler.UserHandler)
mux.HandleFunc("/signup", handler.SignUpHandler)
// Apply middleware (CORS first, then logging) // Apply middleware (CORS first, then logging)
muxMiddleware := handlers.LoggingMiddleware(handlers.CORSMiddleware(mux)) muxMiddleware := handlers.LoggingMiddleware(handlers.CORSMiddleware(mux))

View file

@ -1,6 +1,7 @@
package auth package auth
import ( import (
"crypto/rand"
"crypto/subtle" "crypto/subtle"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
@ -9,6 +10,32 @@ import (
"golang.org/x/crypto/argon2" "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 { func VerifyPassword(hashedPassword, password string) bool {
// Parse the Argon2 hash format: $argon2id$v=19$m=65536,t=3,p=4$salt$hash // Parse the Argon2 hash format: $argon2id$v=19$m=65536,t=3,p=4$salt$hash
parts := strings.Split(hashedPassword, "$") parts := strings.Split(hashedPassword, "$")

View file

@ -2,7 +2,9 @@ package handlers
import ( import (
"encoding/json" "encoding/json"
"go-backend/internal/auth"
"go-backend/internal/database/sqlc" "go-backend/internal/database/sqlc"
"log"
"net/http" "net/http"
) )
@ -36,12 +38,46 @@ func (h *Handler) SignUpHandler(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError) http.Error(w, "Internal server error", http.StatusInternalServerError)
return
} }
if user != nil { if user != nil {
http.Error(w, "Username already taken", http.StatusBadRequest) 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" "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) { func (h *Handler) UserHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet { if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
@ -43,6 +49,12 @@ func (h *Handler) UserHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
resp := UserResponse{
ID: user.ID,
Username: user.Username,
Email: user.Email,
}
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user) json.NewEncoder(w).Encode(resp)
} }