house/backend/routes/login.go
2025-06-14 23:47:44 -04:00

86 lines
1.9 KiB
Go

package routes
import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"net/http"
"time"
"git.red-panda.pet/pandaware/house/backend/db"
"git.red-panda.pet/pandaware/house/backend/middleware"
"git.red-panda.pet/pandaware/house/backend/router"
"golang.org/x/crypto/bcrypt"
)
func init() {
routes["POST"]["/login"] = new(Login)
}
type loginBody struct {
Username string `json:"username"`
Password string `json:"password"`
}
type loginResponse struct {
User db.GetUserRow `json:"user"`
}
type Login struct{}
// ValidateBody implements router.ValidatedBodyRoute.
func (l *Login) ValidateBody(ctx *router.Context) error {
return middleware.ParseJSONBody[loginBody](ctx)
}
// Handle implements router.Route.
func (l *Login) Handle(ctx *router.Context) error {
body := middleware.JSONBody[loginBody](l, ctx)
user, err := ctx.Query.GetUserPassword(ctx, body.Username)
if err != nil {
return ctx.Error(err, http.StatusBadRequest, "Invalid credentials")
}
err = bcrypt.CompareHashAndPassword(user.Password, []byte(body.Password))
if err != nil {
return ctx.Error(err, http.StatusBadRequest, "Invalid credentials")
}
tokenBs := make([]byte, 32)
_, err = rand.Read(tokenBs)
if err != nil {
return ctx.GenericError(err, http.StatusInternalServerError)
}
encodedToken := base64.StdEncoding.EncodeToString(tokenBs)
hashedToken := sha256.Sum256(tokenBs)
_, err = ctx.Query.CreateSession(ctx, db.CreateSessionParams{
Key: hashedToken[:],
UserID: user.ID,
CreatedAt: time.Now().Unix(),
})
if err != nil {
return ctx.GenericError(err, http.StatusInternalServerError)
}
loggedInUser, err := ctx.Query.GetUser(ctx, user.ID)
if err != nil {
return ctx.GenericError(err, http.StatusInternalServerError)
}
ctx.SetCookie(&http.Cookie{
Name: "session",
Value: encodedToken,
Path: "/",
HttpOnly: true,
})
return ctx.JSON(200, loginResponse{
User: loggedInUser,
})
}
var _ router.ValidatedBodyRoute = new(Login)