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)