@@ -33,21 +33,21 @@ package auth
3333
3434import (
3535 " context"
36- " crypto/rand"
3736 " database/sql"
38- " encoding/base64"
3937 " fmt"
4038 " log"
4139 " net/http"
42- " net/url"
4340 " os"
4441 " time"
4542
4643 " {{.ModuleName}}/database/models"
44+ " github.com/livetemplate/lvt/pkg/cookie"
4745 " github.com/livetemplate/lvt/pkg/email"
46+ " github.com/livetemplate/lvt/pkg/flash"
4847 {{- if .EnablePassword }}
4948 " github.com/livetemplate/lvt/pkg/password"
5049 {{- end }}
50+ " github.com/livetemplate/lvt/pkg/token"
5151 " github.com/go-playground/validator/v10"
5252 " github.com/google/uuid"
5353 " github.com/livetemplate/livetemplate"
@@ -630,58 +630,32 @@ func (c *{{.StructName}}Controller) HandlePasswordLogin(w http.ResponseWriter, r
630630 }
631631
632632 // Create session token
633- token , err := c.generateToken (user.ID , " session" , 30*24*time.Hour )
633+ tok , err := c.generateToken (user.ID , " session" , 30*24*time.Hour )
634634 if err != nil {
635635 log.Printf (" Generate session token error: %v " , err)
636636 http.Redirect (w, r, " /auth?error=login_failed" , http.StatusSeeOther )
637637 return
638638 }
639639
640- // Set session cookie
641- http.SetCookie (w, &http.Cookie {
642- Name: " {{.TableName}}_token" ,
643- Value: token,
644- Path: " /" ,
645- MaxAge: 30 * 24 * 60 * 60,
646- HttpOnly: true,
647- Secure: false, // Set to true in production with HTTPS
648- SameSite: http.SameSiteLaxMode ,
649- })
640+ // Set session cookie (30 days)
641+ cookie.SetSession (w, " {{.TableName}}_token" , tok, 30)
650642
651643 // Redirect to home
652644 http.Redirect (w, r, " /" , http.StatusSeeOther )
653645}
654646
655647// HandleLogout handles user logout (HTTP-only, no LiveTemplate)
656648func (c *{{.StructName }}Controller) HandleLogout(w http.ResponseWriter , r *http.Request ) {
657- // Get session token from cookie
658- cookie, err := r.Cookie (" {{.TableName}}_token" )
659- if err == nil {
660- // Delete the token from database
661- c.queries.Delete {{.StructName }}Token(context.Background (), cookie.Value )
649+ // Get session token from cookie and delete from database
650+ if tok := cookie.Get (r, " {{.TableName}}_token" ); tok != " " {
651+ c.queries.Delete {{.StructName }}Token(context.Background (), tok)
662652 }
663653
664654 // Clear auth cookie
665- http.SetCookie (w, &http.Cookie {
666- Name: " {{.TableName}}_token" ,
667- Value: " " ,
668- Path: " /" ,
669- MaxAge: -1,
670- HttpOnly: true,
671- Secure: true,
672- SameSite: http.SameSiteStrictMode ,
673- })
655+ cookie.ClearSecure (w, " {{.TableName}}_token" )
674656
675657 // Clear LiveTemplate session cookie to force fresh state on home page
676- // This ensures the home page gets a new session with IsLoggedIn= false
677- http.SetCookie (w, &http.Cookie {
678- Name: " livetemplate-id" ,
679- Value: " " ,
680- Path: " /" ,
681- MaxAge: -1,
682- HttpOnly: true,
683- SameSite: http.SameSiteLaxMode ,
684- })
658+ cookie.ClearLiveTemplateSession (w)
685659
686660 // Redirect to home
687661 http.Redirect (w, r, " /" , http.StatusSeeOther )
@@ -690,21 +664,20 @@ func (c *{{.StructName}}Controller) HandleLogout(w http.ResponseWriter, r *http.
690664// generateToken creates a random token and stores it
691665func (c *{{.StructName }}Controller) generateToken(userID, tokenContext string, duration time.Duration ) (string, error) {
692666 // Generate random token
693- b := make([]byte, 32 )
694- if _, err := rand .Read (b); err != nil {
667+ tok, err := token .Generate ( )
668+ if err != nil {
695669 return " " , err
696670 }
697- token := base64.URLEncoding.EncodeToString (b)
698671
699672 // Store token
700673 tokenID := uuid.New ().String ()
701674 now := time.Now ()
702675 expiresAt := sql.NullTime {Time: now.Add (duration), Valid: true}
703676
704- _, err : = c.queries.Create {{.StructName }}Token(context.Background (), models.Create {{.StructName }}TokenParams{
677+ _, err = c.queries.Create {{.StructName }}Token(context.Background (), models.Create {{.StructName }}TokenParams{
705678 ID: tokenID,
706679 {{.StructName }}ID: userID,
707- Token: token ,
680+ Token: tok ,
708681 Context: tokenContext,
709682 CreatedAt: now,
710683 ExpiresAt: expiresAt,
@@ -713,18 +686,18 @@ func (c *{{.StructName}}Controller) generateToken(userID, tokenContext string, d
713686 return " " , err
714687 }
715688
716- return token , nil
689+ return tok , nil
717690}
718691
719692// GetCurrentUser returns the authenticated user or nil
720693func (c *{{.StructName }}Controller) GetCurrentUser(r *http.Request ) (*models. {{.StructName }}, error) {
721- cookie, err := r .Cookie ( " {{.TableName}}_token" )
722- if err ! = nil {
723- return nil , err
694+ tok := cookie .Get (r, " {{.TableName}}_token" )
695+ if tok == " " {
696+ return nil , http .ErrNoCookie
724697 }
725698
726699 userToken, err := c.queries.Get {{.StructName }}Token(context.Background (), models.Get {{.StructName }}TokenParams{
727- Token: cookie .Value ,
700+ Token: tok ,
728701 ExpiresAt: sql.NullTime {Time: time.Now (), Valid: true},
729702 })
730703 if err != nil {
@@ -771,46 +744,22 @@ func Handler(queries *models.Queries) http.Handler {
771744
772745 if errCode != " " || successCode != " " {
773746 // Clear livetemplate-id to force new session on next request
774- // This ensures the flash message will be properly displayed
775- http.SetCookie (w, &http.Cookie {
776- Name: " livetemplate-id" ,
777- Value: " " ,
778- Path: " /" ,
779- MaxAge: -1,
780- })
781-
782- // Set flash message in cookie (URL-encoded to avoid quoting issues)
747+ cookie.ClearLiveTemplateSession (w)
748+
749+ // Set flash message from error/success code
783750 if errCode != " " {
784751 if msg, ok := errorMessages[errCode]; ok {
785- http.SetCookie (w, &http.Cookie {
786- Name: " flash_error" ,
787- Value: url.QueryEscape (msg),
788- Path: " /" ,
789- MaxAge: 10, // Short-lived
790- HttpOnly: true,
791- })
752+ flash.Error (w, msg)
792753 }
793754 }
794755 if successCode != " " {
795756 if msg, ok := successMessages[successCode]; ok {
796- http.SetCookie (w, &http.Cookie {
797- Name: " flash_success" ,
798- Value: url.QueryEscape (msg),
799- Path: " /" ,
800- MaxAge: 10,
801- HttpOnly: true,
802- })
757+ flash.Success (w, msg)
803758 }
804759 }
805760
806761 // Set marker so we know flash was triggered (for one-time display)
807- http.SetCookie (w, &http.Cookie {
808- Name: " flash_pending" ,
809- Value: " 1" ,
810- Path: " /" ,
811- MaxAge: 10,
812- HttpOnly: true,
813- })
762+ flash.SetPending (w)
814763
815764 // Redirect to clean URL
816765 http.Redirect (w, r, " /auth" , http.StatusSeeOther )
@@ -826,60 +775,26 @@ func Handler(queries *models.Queries) http.Handler {
826775
827776 // Only process flash logic for GET requests (not HEAD/WebSocket preflight)
828777 if r.Method == " GET" {
829- // Check if this is a fresh flash display or a reload
830- _, errFlashPending := r.Cookie (" flash_pending" )
831- _, errSession := r.Cookie (" livetemplate-id" )
832-
833- hasFlashPending := errFlashPending == nil // true if cookie exists
834- hasSession := errSession == nil // true if cookie exists
778+ hasFlashPending := flash.IsPending (r)
779+ hasSession := cookie.Get (r, " livetemplate-id" ) != " "
835780
836781 // If we have a session but no flash_pending marker, this is a reload
837782 // Force new session to clear any stale flash from previous session
838783 if hasSession && !hasFlashPending {
839- http.SetCookie (w, &http.Cookie {
840- Name: " livetemplate-id" ,
841- Value: " " ,
842- Path: " /" ,
843- MaxAge: -1,
844- })
784+ cookie.ClearLiveTemplateSession (w)
845785 }
846786
847787 // Clear the flash_pending marker after first use
848788 if hasFlashPending {
849- http.SetCookie (w, &http.Cookie {
850- Name: " flash_pending" ,
851- Value: " " ,
852- Path: " /" ,
853- MaxAge: -1,
854- })
789+ flash.ClearPending (w)
855790 }
856791 }
857792
858793 // Read and clear flash cookies, set in initial state
859794 state := New{{.StructName }}State()
860-
861- if cookie, err := r.Cookie (" flash_error" ); err == nil && cookie.Value != " " {
862- if msg, err := url.QueryUnescape (cookie.Value ); err == nil {
863- state.FlashError = msg
864- }
865- http.SetCookie (w, &http.Cookie {
866- Name: " flash_error" ,
867- Value: " " ,
868- Path: " /" ,
869- MaxAge: -1,
870- })
871- }
872- if cookie, err := r.Cookie (" flash_success" ); err == nil && cookie.Value != " " {
873- if msg, err := url.QueryUnescape (cookie.Value ); err == nil {
874- state.FlashSuccess = msg
875- }
876- http.SetCookie (w, &http.Cookie {
877- Name: " flash_success" ,
878- Value: " " ,
879- Path: " /" ,
880- MaxAge: -1,
881- })
882- }
795+ msgs := flash.GetAll (r, w)
796+ state.FlashError = msgs.Error
797+ state.FlashSuccess = msgs.Success
883798
884799 tmpl.Handle (controller, livetemplate.AsState (state)).ServeHTTP (w, r)
885800 })
0 commit comments