In the development of dynamic websites using Go Fiber, managing user sessions is crucial for maintaining state and ensuring authenticated interactions. When clients have JavaScript disabled, traditional client-side session handling methods become infeasible. This guide provides a detailed approach to implementing session management exclusively on the server-side using Fiber's official session middleware, github.com/gofiber/fiber/v2/middleware/session
.
Begin by installing the necessary Fiber packages using go get
:
go get github.com/gofiber/fiber/v2
go get github.com/gofiber/fiber/v2/middleware/session
Create a new Go file (e.g., main.go
) and initialize the Fiber application along with the session middleware:
package main
import (
"fmt"
"log"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/session"
)
var store = session.New(session.Config{
// Configuration options
CookieHTTPOnly: true,
CookieSecure: false, // Set to true in production
})
func main() {
app := fiber.New()
// Apply the session middleware globally
app.Use(sessionMiddleware)
// Define routes
app.Get("/", getHandler)
app.Post("/submit", postHandler)
log.Fatal(app.Listen(":3000"))
}
Implement a middleware function to manage session IDs for both GET and POST requests. This middleware will:
Here's the implementation:
func sessionMiddleware(c *fiber.Ctx) error {
sess, err := store.Get(c)
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Session error")
}
defer sess.Save()
if c.Method() == fiber.MethodGet {
// Retrieve session ID from query parameters
sessionID := c.Query("session")
if sessionID == "" {
// Generate a new session and redirect with session ID
sess.Set("user", "guest") // Set default session variables as needed
return c.Redirect(fmt.Sprintf("%s?session=%s", c.Path(), sess.ID()), fiber.StatusFound)
}
} else if c.Method() == fiber.MethodPost {
// Ensure session ID is present in form data
sessionID := c.FormValue("session")
if sessionID == "" {
return c.Status(fiber.StatusBadRequest).SendString("Missing session ID")
}
// Validate session ID
if sessionID != sess.ID() {
return c.Status(fiber.StatusUnauthorized).SendString("Invalid session ID")
}
}
// Proceed to next handler
return c.Next()
}
For GET requests, the session middleware checks for a session ID. If absent, it redirects the client to the same URL with the session ID appended. When serving the HTML page, embed the session ID within forms as a hidden input to ensure it's included in subsequent POST requests.
func getHandler(c *fiber.Ctx) error {
sess, err := store.Get(c)
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Session error")
}
// Example HTML with embedded session ID
html := fmt.Sprintf(`
<html>
<body>
<form action="/submit" method="POST">
<input type="hidden" name="session" value="%s">
<label for="data">Enter Data:</label>
<input type="text" id="data" name="data" required>
<button type="submit">Submit</button>
</form>
</body>
</html>
`, sess.ID())
return c.Type("html").SendString(html)
}
For POST requests, extract and validate the session ID from the form data. Upon successful validation, process the submitted data as needed.
func postHandler(c *fiber.Ctx) error {
sess, err := store.Get(c)
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Session error")
}
// Retrieve and validate session ID from form data
sessionID := c.FormValue("session")
if sessionID == "" {
return c.Status(fiber.StatusBadRequest).SendString("Missing session ID")
}
if sessionID != sess.ID() {
return c.Status(fiber.StatusUnauthorized).SendString("Invalid session ID")
}
// Retrieve form data
data := c.FormValue("data")
sess.Set("data", data)
if err := sess.Save(); err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Failed to save session")
}
// Respond to the client
response := fmt.Sprintf("Hello, %s! You submitted: %s", sess.Get("user"), data)
return c.SendString(response)
}
Below is the complete Go code integrating all the components discussed:
package main
import (
"fmt"
"log"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/session"
)
var store = session.New(session.Config{
CookieHTTPOnly: true,
CookieSecure: false, // Set to true in production
})
func main() {
app := fiber.New()
// Apply the session middleware globally
app.Use(sessionMiddleware)
// Define routes
app.Get("/", getHandler)
app.Post("/submit", postHandler)
log.Fatal(app.Listen(":3000"))
}
func sessionMiddleware(c *fiber.Ctx) error {
sess, err := store.Get(c)
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Session error")
}
defer sess.Save()
if c.Method() == fiber.MethodGet {
// Retrieve session ID from query parameters
sessionID := c.Query("session")
if sessionID == "" {
// Generate a new session and redirect with session ID
sess.Set("user", "guest") // Set default session variables as needed
return c.Redirect(fmt.Sprintf("%s?session=%s", c.Path(), sess.ID()), fiber.StatusFound)
}
} else if c.Method() == fiber.MethodPost {
// Ensure session ID is present in form data
sessionID := c.FormValue("session")
if sessionID == "" {
return c.Status(fiber.StatusBadRequest).SendString("Missing session ID")
}
// Validate session ID
if sessionID != sess.ID() {
return c.Status(fiber.StatusUnauthorized).SendString("Invalid session ID")
}
}
// Proceed to next handler
return c.Next()
}
func getHandler(c *fiber.Ctx) error {
sess, err := store.Get(c)
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Session error")
}
// Example HTML with embedded session ID
html := fmt.Sprintf(`
<html>
<body>
<form action="/submit" method="POST">
<input type="hidden" name="session" value="%s">
<label for="data">Enter Data:</label>
<input type="text" id="data" name="data" required>
<button type="submit">Submit</button>
</form>
</body>
</html>
`, sess.ID())
return c.Type("html").SendString(html)
}
func postHandler(c *fiber.Ctx) error {
sess, err := store.Get(c)
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Session error")
}
// Retrieve and validate session ID from form data
sessionID := c.FormValue("session")
if sessionID == "" {
return c.Status(fiber.StatusBadRequest).SendString("Missing session ID")
}
if sessionID != sess.ID() {
return c.Status(fiber.StatusUnauthorized).SendString("Invalid session ID")
}
// Retrieve form data
data := c.FormValue("data")
sess.Set("data", data)
if err := sess.Save(); err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Failed to save session")
}
// Respond to the client
response := fmt.Sprintf("Hello, %s! You submitted: %s", sess.Get("user"), data)
return c.SendString(response)
}
Compile and run your Go application:
go run main.go
Access the application by navigating to http://localhost:3000/ in your web browser.
CookieSecure
option is set to true
to enforce HTTPS, preventing session hijacking.
Managing sessions in a Go Fiber application without relying on JavaScript involves careful server-side handling of session IDs for all requests. By leveraging Fiber's session middleware and implementing custom middleware to manage session validation and redirection, developers can maintain robust and secure session management. Embedding session IDs directly into HTML forms ensures consistent session tracking, providing a seamless user experience even in environments where JavaScript is disabled.