mirror of
https://github.com/gofiber/fiber.git
synced 2025-07-29 05:30:43 +00:00
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
871 lines
24 KiB
Markdown
871 lines
24 KiB
Markdown
---
|
|
id: session
|
|
---
|
|
|
|
# Session
|
|
|
|
The Session middleware provides robust session management for Fiber applications, utilizing the [Storage](https://github.com/gofiber/storage) package for multi-database support via a unified interface. By default, session data is stored in memory, but custom storage options are easily configurable.
|
|
|
|
## Table of Contents
|
|
|
|
- [Quick Start](#quick-start)
|
|
- [Usage Patterns](#usage-patterns)
|
|
- [Session Security](#session-security)
|
|
- [Session ID Extractors](#session-id-extractors)
|
|
- [Configuration](#configuration)
|
|
- [Migration Guide](#migration-guide)
|
|
- [API Reference](#api-reference)
|
|
- [Examples](#examples)
|
|
|
|
## Quick Start
|
|
|
|
```go
|
|
import (
|
|
"fmt"
|
|
"github.com/gofiber/fiber/v3"
|
|
"github.com/gofiber/fiber/v3/middleware/session"
|
|
)
|
|
|
|
// Basic usage
|
|
app.Use(session.New())
|
|
|
|
app.Get("/", func(c fiber.Ctx) error {
|
|
sess := session.FromContext(c)
|
|
|
|
// Get and update visits count
|
|
var visits int
|
|
if v := sess.Get("visits"); v != nil {
|
|
// Use type assertion with an ok check to prevent a panic
|
|
if vInt, ok := v.(int); ok {
|
|
visits = vInt
|
|
}
|
|
}
|
|
visits++
|
|
sess.Set("visits", visits)
|
|
return c.SendString(fmt.Sprintf("Visits: %d", visits))
|
|
})
|
|
```
|
|
|
|
### Production Configuration
|
|
|
|
```go
|
|
import (
|
|
"time"
|
|
"github.com/gofiber/storage/redis"
|
|
)
|
|
|
|
storage := redis.New(redis.Config{
|
|
Host: "localhost",
|
|
Port: 6379,
|
|
})
|
|
|
|
app.Use(session.New(session.Config{
|
|
Storage: storage,
|
|
CookieSecure: true, // HTTPS only
|
|
CookieHTTPOnly: true, // Prevent XSS
|
|
CookieSameSite: "Lax", // CSRF protection
|
|
IdleTimeout: 30 * time.Minute, // Session timeout
|
|
AbsoluteTimeout: 24 * time.Hour, // Maximum session life
|
|
Extractor: session.FromCookie("__Host-session_id"),
|
|
}))
|
|
```
|
|
|
|
## Usage Patterns
|
|
|
|
### Middleware Pattern (Recommended)
|
|
|
|
The middleware pattern automatically manages session lifecycle and is the recommended approach for most applications.
|
|
|
|
```go
|
|
// Setup middleware
|
|
app.Use(session.New())
|
|
|
|
// Use in handlers
|
|
app.Post("/login", func(c fiber.Ctx) error {
|
|
sess := session.FromContext(c)
|
|
|
|
// Session is automatically saved when handler returns
|
|
sess.Set("user_id", 123)
|
|
sess.Set("authenticated", true)
|
|
|
|
return c.Redirect("/dashboard")
|
|
})
|
|
```
|
|
|
|
**Benefits:**
|
|
|
|
- Automatic session saving
|
|
- Automatic resource cleanup
|
|
- No manual lifecycle management
|
|
- Thread-safe operations
|
|
|
|
### Store Pattern (Advanced)
|
|
|
|
Use the store pattern for background tasks or when you need direct session access.
|
|
|
|
```go
|
|
import (
|
|
"context"
|
|
"log"
|
|
"time"
|
|
)
|
|
|
|
store := session.NewStore()
|
|
|
|
// In background tasks
|
|
func backgroundTask(sessionID string) {
|
|
sess, err := store.GetByID(context.Background(), sessionID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer sess.Release() // Important: Manual cleanup required
|
|
|
|
// Modify session
|
|
sess.Set("last_task", time.Now())
|
|
|
|
// Manual save required
|
|
if err := sess.Save(); err != nil {
|
|
log.Printf("Failed to save session: %v", err)
|
|
}
|
|
}
|
|
```
|
|
|
|
**Requirements:**
|
|
|
|
- Must call `sess.Release()` when done
|
|
- Must call `sess.Save()` to persist changes
|
|
- Handle errors manually
|
|
|
|
## Session Security
|
|
|
|
### Authentication Flow
|
|
|
|
Understanding session lifecycle during authentication is crucial for security.
|
|
|
|
#### Basic Login/Logout
|
|
|
|
```go
|
|
app.Post("/login", func(c fiber.Ctx) error {
|
|
sess := session.FromContext(c)
|
|
|
|
email := c.FormValue("email")
|
|
password := c.FormValue("password")
|
|
|
|
// Simple credential validation (use proper authentication in production)
|
|
if email == "admin@example.com" && password == "secret" {
|
|
// CRITICAL: Regenerate session ID to prevent session fixation
|
|
// This changes the session ID while preserving existing data
|
|
if err := sess.Regenerate(); err != nil {
|
|
return c.Status(500).SendString("Session error")
|
|
}
|
|
|
|
// Add authentication data to existing session
|
|
sess.Set("user_id", 1)
|
|
sess.Set("authenticated", true)
|
|
|
|
return c.Redirect("/dashboard")
|
|
}
|
|
|
|
return c.Status(401).SendString("Invalid credentials")
|
|
})
|
|
|
|
app.Post("/logout", func(c fiber.Ctx) error {
|
|
sess := session.FromContext(c)
|
|
|
|
// Complete session reset (clears all data + new session ID)
|
|
if err := sess.Reset(); err != nil {
|
|
return c.Status(500).SendString("Session error")
|
|
}
|
|
|
|
return c.Redirect("/")
|
|
})
|
|
```
|
|
|
|
#### Cart Preservation During Login
|
|
|
|
```go
|
|
app.Post("/login", func(c fiber.Ctx) error {
|
|
sess := session.FromContext(c)
|
|
|
|
// Validate credentials (implement your own validation)
|
|
email := c.FormValue("email")
|
|
password := c.FormValue("password")
|
|
if !isValidUser(email, password) {
|
|
return c.Status(401).JSON(fiber.Map{"error": "Invalid credentials"})
|
|
}
|
|
|
|
// CRITICAL: Regenerate session ID to prevent session fixation
|
|
// This changes the session ID while preserving existing data
|
|
if err := sess.Regenerate(); err != nil {
|
|
return c.Status(500).JSON(fiber.Map{"error": "Session error"})
|
|
}
|
|
|
|
// Add authentication data to existing session
|
|
sess.Set("user_id", getUserID(email))
|
|
sess.Set("authenticated", true)
|
|
sess.Set("login_time", time.Now())
|
|
|
|
return c.JSON(fiber.Map{"status": "logged in"})
|
|
})
|
|
```
|
|
|
|
### Security Methods Comparison
|
|
|
|
| Method | Session ID | Session Data | Use Case |
|
|
|--------|------------|--------------|----------|
|
|
| `Regenerate()` | ✅ Changes | ✅ Preserved | Login, privilege escalation |
|
|
| `Reset()` | ✅ Changes | ❌ Cleared | Logout, security breach |
|
|
| `Destroy()` | ⚪ Unchanged | ❌ Cleared | Clear data only |
|
|
|
|
### Common Security Mistakes
|
|
|
|
❌ **Session Fixation Vulnerability:**
|
|
|
|
```go
|
|
// DANGEROUS: Keeping same session ID after login
|
|
app.Post("/login", func(c fiber.Ctx) error {
|
|
sess := session.FromContext(c)
|
|
// Validate user...
|
|
sess.Set("user_id", userID) // Attacker can hijack this session!
|
|
return c.Redirect("/dashboard")
|
|
})
|
|
```
|
|
|
|
✅ **Secure Implementation:**
|
|
|
|
```go
|
|
// SECURE: Always regenerate session ID after authentication
|
|
app.Post("/login", func(c fiber.Ctx) error {
|
|
sess := session.FromContext(c)
|
|
// Validate user...
|
|
if err := sess.Regenerate(); err != nil { // Prevents session fixation
|
|
return err
|
|
}
|
|
sess.Set("user_id", userID)
|
|
return c.Redirect("/dashboard")
|
|
})
|
|
```
|
|
|
|
### Authentication Middleware
|
|
|
|
This is a basic example of an authentication middleware that checks if a user is logged in before accessing protected routes.
|
|
|
|
```go
|
|
// Authentication check middleware
|
|
func RequireAuth(c fiber.Ctx) error {
|
|
sess := session.FromContext(c)
|
|
if sess == nil {
|
|
return c.Redirect("/login")
|
|
}
|
|
|
|
// Check if user is authenticated
|
|
if sess.Get("authenticated") != true {
|
|
return c.Redirect("/login")
|
|
}
|
|
|
|
return c.Next()
|
|
}
|
|
|
|
// Usage
|
|
app.Use("/dashboard", RequireAuth)
|
|
app.Use("/admin", RequireAuth)
|
|
```
|
|
|
|
### Automatic Session Expiration
|
|
|
|
Sessions automatically expire based on your configuration:
|
|
|
|
```go
|
|
app.Use(session.New(session.Config{
|
|
IdleTimeout: 30 * time.Minute, // Auto-expire after 30 min of inactivity
|
|
AbsoluteTimeout: 24 * time.Hour, // Force expire after 24 hours regardless of activity
|
|
}))
|
|
```
|
|
|
|
**How it works:**
|
|
|
|
- `IdleTimeout`: Storage automatically removes sessions after inactivity period
|
|
- Any route that uses the middleware will reset the idle timer
|
|
- Calling `sess.Save()` will also reset the idle timer
|
|
- `AbsoluteTimeout`: Sessions are forcibly expired after maximum duration
|
|
- No manual cleanup required - the storage layer handles this
|
|
|
|
## Session ID Extractors
|
|
|
|
### Built-in Extractors
|
|
|
|
```go
|
|
// Cookie-based (recommended for web apps)
|
|
session.FromCookie("session_id")
|
|
|
|
// Header-based (recommended for APIs)
|
|
session.FromHeader("X-Session-ID")
|
|
|
|
// Form data
|
|
session.FromForm("session_id")
|
|
|
|
// URL query parameter
|
|
session.FromQuery("session_id")
|
|
|
|
// URL path parameter
|
|
session.FromParam("id")
|
|
```
|
|
|
|
**Response Behavior with Extractors:**
|
|
|
|
- **Cookie extractors**: Set cookie in response
|
|
- **Header extractors**: Set header in response
|
|
- **Query/Form/Param extractors**: Read-only, do not set response values
|
|
|
|
### Multiple Sources with Fallback
|
|
|
|
```go
|
|
app.Use(session.New(session.Config{
|
|
Extractor: session.Chain(
|
|
session.FromCookie("session_id"), // Try cookie first
|
|
session.FromHeader("X-Session-ID"), // Then header
|
|
session.FromQuery("session_id"), // Finally query
|
|
),
|
|
}))
|
|
```
|
|
|
|
**Response Behavior with Chained Extractors:**
|
|
|
|
The session middleware intelligently sets response values based on the extractors in your chain:
|
|
|
|
- **Cookie + Header extractors**: Both cookie and header are set in the response
|
|
- **Only Cookie extractors**: Only cookie is set in the response
|
|
- **Only Header extractors**: Only header is set in the response
|
|
- **Only Query/Form/Param extractors**: No response values are set (read-only)
|
|
- **Mixed extractors**: Only cookie and header extractors set response values
|
|
|
|
```go
|
|
// This will set both cookie and header in response
|
|
session.Chain(
|
|
session.FromCookie("session_id"),
|
|
session.FromHeader("X-Session-ID")
|
|
)
|
|
|
|
// This will set only cookie in response
|
|
session.Chain(
|
|
session.FromCookie("session_id"),
|
|
session.FromQuery("session_id") // Ignored for response
|
|
)
|
|
|
|
// This will set nothing in response (read-only mode)
|
|
session.Chain(
|
|
session.FromQuery("session_id"),
|
|
session.FromForm("session_id")
|
|
)
|
|
```
|
|
|
|
### Custom Extractor
|
|
|
|
You can create custom extractors by returning a `session.Extractor` struct that defines how to extract the session ID from the request and how the middleware should handle responses.
|
|
|
|
The `Source` field is crucial as it controls whether the middleware sets response values:
|
|
|
|
- `SourceCookie`: Sets cookies in the response
|
|
- `SourceHeader`: Sets headers in the response
|
|
- `SourceOther`: Read-only, no response values set
|
|
|
|
```go
|
|
// Custom extractor for Authorization Bearer tokens
|
|
func FromAuthorization() session.Extractor {
|
|
return session.Extractor{
|
|
Extract: func(c fiber.Ctx) (string, error) {
|
|
auth := c.Get("Authorization")
|
|
if strings.HasPrefix(auth, "Bearer ") {
|
|
sessionID := strings.TrimPrefix(auth, "Bearer ")
|
|
if sessionID != "" {
|
|
return sessionID, nil
|
|
}
|
|
}
|
|
return "", session.ErrMissingSessionIDInHeader
|
|
},
|
|
Source: session.SourceHeader, // This will set response headers
|
|
Key: "Authorization",
|
|
}
|
|
}
|
|
|
|
app.Use(session.New(session.Config{
|
|
Extractor: FromAuthorization(), // Will set Authorization header in response
|
|
}))
|
|
```
|
|
|
|
```go
|
|
// Custom read-only extractor (no response setting)
|
|
func FromCustomParam() session.Extractor {
|
|
return session.Extractor{
|
|
Extract: func(c fiber.Ctx) (string, error) {
|
|
sessionID := c.Get("X-Custom-Session")
|
|
if sessionID == "" {
|
|
return "", session.ErrMissingSessionIDInHeader
|
|
}
|
|
return sessionID, nil
|
|
},
|
|
Source: session.SourceOther, // Read-only, won't set responses
|
|
Key: "X-Custom-Session",
|
|
}
|
|
}
|
|
|
|
app.Use(session.New(session.Config{
|
|
Extractor: FromCustomParam(), // Will not set any response values
|
|
}))
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### Storage Options
|
|
|
|
```go
|
|
import (
|
|
"github.com/gofiber/storage/redis"
|
|
"github.com/gofiber/storage/postgres"
|
|
)
|
|
|
|
// Redis (recommended for production)
|
|
redisStorage := redis.New(redis.Config{
|
|
Host: "localhost",
|
|
Port: 6379,
|
|
Password: "",
|
|
Database: 0,
|
|
})
|
|
|
|
// PostgreSQL
|
|
pgStorage := postgres.New(postgres.Config{
|
|
Host: "localhost",
|
|
Port: 5432,
|
|
Database: "sessions",
|
|
Username: "user",
|
|
Password: "pass",
|
|
})
|
|
|
|
app.Use(session.New(session.Config{
|
|
Storage: redisStorage,
|
|
}))
|
|
```
|
|
|
|
### Production Security Settings
|
|
|
|
```go
|
|
import (
|
|
"log"
|
|
"time"
|
|
"github.com/gofiber/utils/v2"
|
|
)
|
|
|
|
app.Use(session.New(session.Config{
|
|
// Storage
|
|
Storage: redisStorage,
|
|
|
|
// Security
|
|
CookieSecure: true, // HTTPS only (required in production)
|
|
CookieHTTPOnly: true, // No JavaScript access (prevents XSS)
|
|
CookieSameSite: "Lax", // CSRF protection
|
|
|
|
// Session Management
|
|
IdleTimeout: 30 * time.Minute, // Inactivity timeout
|
|
AbsoluteTimeout: 24 * time.Hour, // Maximum session duration
|
|
|
|
// Cookie Settings
|
|
CookiePath: "/",
|
|
CookieDomain: "example.com",
|
|
CookieSessionOnly: false, // Persist across browser restarts
|
|
|
|
// Session ID
|
|
Extractor: session.FromCookie("__Host-session_id"),
|
|
KeyGenerator: utils.UUIDv4,
|
|
|
|
// Error Handling
|
|
ErrorHandler: func(c fiber.Ctx, err error) {
|
|
log.Printf("Session error: %v", err)
|
|
},
|
|
}))
|
|
```
|
|
|
|
### Custom Types
|
|
|
|
Session data supports basic Go types by default:
|
|
|
|
- `string`, `int`, `int8`, `int16`, `int32`, `int64`
|
|
- `uint`, `uint8`, `uint16`, `uint32`, `uint64`
|
|
- `bool`, `float32`, `float64`
|
|
- `[]byte`, `complex64`, `complex128`
|
|
- `interface{}`
|
|
|
|
For custom types (structs, maps, slices), you must register them for encoding/decoding:
|
|
|
|
```go
|
|
import "fmt"
|
|
|
|
type User struct {
|
|
ID int `json:"id"`
|
|
Name string `json:"name"`
|
|
Role string `json:"role"`
|
|
}
|
|
|
|
// Method 1: Using NewWithStore
|
|
func main() {
|
|
app := fiber.New()
|
|
|
|
sessionMiddleware, store := session.NewWithStore()
|
|
store.RegisterType(User{}) // Register custom type
|
|
|
|
app.Use(sessionMiddleware)
|
|
|
|
app.Get("/", func(c fiber.Ctx) error {
|
|
sess := session.FromContext(c)
|
|
|
|
// Use custom type
|
|
sess.Set("user", User{ID: 123, Name: "John", Role: "admin"})
|
|
|
|
user, ok := sess.Get("user").(User)
|
|
if ok {
|
|
return c.JSON(fiber.Map{"user": user.Name, "role": user.Role})
|
|
}
|
|
return c.SendString("No user found")
|
|
})
|
|
|
|
app.Listen(":3000")
|
|
}
|
|
```
|
|
|
|
```go
|
|
// Method 2: Using separate store
|
|
store := session.NewStore()
|
|
store.RegisterType(User{})
|
|
|
|
app.Use(session.New(session.Config{
|
|
Store: store,
|
|
}))
|
|
|
|
// Usage in handlers
|
|
sess.Set("user", User{ID: 123, Name: "John", Role: "admin"})
|
|
user, ok := sess.Get("user").(User)
|
|
if ok {
|
|
fmt.Printf("User: %s (Role: %s)", user.Name, user.Role)
|
|
}
|
|
```
|
|
|
|
**Important Notes:**
|
|
|
|
- Custom types must be registered before using them in sessions
|
|
- Registration must happen during application startup
|
|
- All instances of the application must register the same types
|
|
- Types are encoded using Go's `gob` package
|
|
|
|
## Migration Guide
|
|
|
|
### v2 to v3 Breaking Changes
|
|
|
|
1. **Function Signature**: `session.New()` now returns middleware handler, not store
|
|
2. **Session ID Extraction**: `KeyLookup` replaced with `Extractor` functions
|
|
3. **Lifecycle Management**: Manual `Release()` required for store pattern
|
|
4. **Timeout Handling**: `Expiration` split into `IdleTimeout` and `AbsoluteTimeout`
|
|
|
|
### Migration Examples
|
|
|
|
**v2 Code:**
|
|
|
|
```go
|
|
store := session.New(session.Config{
|
|
KeyLookup: "cookie:session_id",
|
|
})
|
|
|
|
app.Get("/", func(c fiber.Ctx) error {
|
|
sess, err := store.Get(c)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Session automatically saved and released
|
|
sess.Set("key", "value")
|
|
return nil
|
|
})
|
|
```
|
|
|
|
**v3 Middleware Pattern (Recommended):**
|
|
|
|
```go
|
|
app.Use(session.New(session.Config{
|
|
Extractor: session.FromCookie("session_id"),
|
|
}))
|
|
|
|
app.Get("/", func(c fiber.Ctx) error {
|
|
sess := session.FromContext(c)
|
|
// Session automatically saved and released
|
|
sess.Set("key", "value")
|
|
return nil
|
|
})
|
|
```
|
|
|
|
**v3 Store Pattern (Advanced):**
|
|
|
|
```go
|
|
store := session.NewStore(session.Config{
|
|
Extractor: session.FromCookie("session_id"),
|
|
})
|
|
|
|
app.Get("/", func(c fiber.Ctx) error {
|
|
sess, err := store.Get(c)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer sess.Release() // Manual cleanup required
|
|
|
|
sess.Set("key", "value")
|
|
return sess.Save() // Manual save required
|
|
})
|
|
```
|
|
|
|
### KeyLookup to Extractor Migration
|
|
|
|
| v2 KeyLookup | v3 Extractor |
|
|
|---------------------------------|-------------------------------------------------------------------------|
|
|
| `"cookie:session_id"` | `session.FromCookie("session_id")` |
|
|
| `"header:X-Session-ID"` | `session.FromHeader("X-Session-ID")` |
|
|
| `"query:session_id"` | `session.FromQuery("session_id")` |
|
|
| `"form:session_id"` | `session.FromForm("session_id")` |
|
|
| `"cookie:sid,header:X-Sid"` | `session.Chain(session.FromCookie("sid"), session.FromHeader("X-Sid"))` |
|
|
|
|
## API Reference
|
|
|
|
### Middleware Methods (Recommended)
|
|
|
|
```go
|
|
sess := session.FromContext(c)
|
|
|
|
// Data operations
|
|
sess.Get(key any) any
|
|
sess.Set(key, value any)
|
|
sess.Delete(key any)
|
|
sess.Keys() []any
|
|
|
|
// Session management
|
|
sess.ID() string
|
|
sess.Fresh() bool
|
|
sess.Regenerate() error // Change ID, keep data
|
|
sess.Reset() error // Change ID, clear data
|
|
sess.Destroy() error // Keep ID, clear data
|
|
|
|
// Store access
|
|
sess.Store() *session.Store
|
|
```
|
|
|
|
### Store Methods
|
|
|
|
```go
|
|
store := session.NewStore()
|
|
|
|
// Store operations
|
|
store.Get(c fiber.Ctx) (*session.Session, error)
|
|
store.GetByID(ctx context.Context, sessionID string) (*session.Session, error)
|
|
store.Reset(c fiber.Ctx) error
|
|
store.Delete(sessionID string) error
|
|
|
|
// Type registration
|
|
store.RegisterType(interface{})
|
|
```
|
|
|
|
### Session Methods (Store Pattern)
|
|
|
|
```go
|
|
sess, err := store.Get(c)
|
|
defer sess.Release() // Required!
|
|
|
|
// Same methods as middleware, plus:
|
|
sess.Save() error // Manual save required
|
|
sess.SetIdleTimeout(duration) // Per-session timeout
|
|
sess.Release() // Manual cleanup required
|
|
```
|
|
|
|
### Extractor Functions
|
|
|
|
```go
|
|
// Built-in extractors
|
|
session.FromCookie(key string) session.Extractor
|
|
session.FromHeader(key string) session.Extractor
|
|
session.FromQuery(key string) session.Extractor
|
|
session.FromForm(key string) session.Extractor
|
|
session.FromParam(key string) session.Extractor
|
|
|
|
// Chaining
|
|
session.Chain(extractors ...session.Extractor) session.Extractor
|
|
```
|
|
|
|
### Config Properties
|
|
|
|
| Property | Type | Description | Default |
|
|
|---------------------|-----------------------------|-----------------------------|---------------------------|
|
|
| `Storage` | `fiber.Storage` | Session storage backend | `memory.New()` |
|
|
| `Extractor` | `session.Extractor` | Session ID extraction | `FromCookie("session_id")`|
|
|
| `KeyGenerator` | `func() string` | Session ID generator | `utils.UUIDv4` |
|
|
| `IdleTimeout` | `time.Duration` | Inactivity timeout | `30 * time.Minute` |
|
|
| `AbsoluteTimeout` | `time.Duration` | Maximum session duration | `0` (unlimited) |
|
|
| `CookieSecure` | `bool` | HTTPS only | `false` |
|
|
| `CookieHTTPOnly` | `bool` | No JavaScript access | `false` |
|
|
| `CookieSameSite` | `string` | SameSite attribute | `"Lax"` |
|
|
| `CookiePath` | `string` | Cookie path | `""` |
|
|
| `CookieDomain` | `string` | Cookie domain | `""` |
|
|
| `CookieSessionOnly` | `bool` | Session cookie | `false` |
|
|
| `ErrorHandler` | `func(fiber.Ctx, error)` | Error callback | `DefaultErrorHandler` |
|
|
|
|
## Examples
|
|
|
|
### E-commerce with Cart Persistence
|
|
|
|
```go
|
|
import (
|
|
"time"
|
|
"github.com/gofiber/fiber/v3"
|
|
"github.com/gofiber/fiber/v3/middleware/session"
|
|
"github.com/gofiber/storage/redis"
|
|
)
|
|
|
|
func main() {
|
|
app := fiber.New()
|
|
|
|
// Session middleware
|
|
app.Use(session.New(session.Config{
|
|
Storage: redis.New(),
|
|
CookieSecure: true,
|
|
CookieHTTPOnly: true,
|
|
CookieSameSite: "Lax",
|
|
IdleTimeout: 30 * time.Minute,
|
|
AbsoluteTimeout: 24 * time.Hour,
|
|
Extractor: session.FromCookie("__Host-cart_session"),
|
|
}))
|
|
|
|
// Add to cart (anonymous user)
|
|
app.Post("/cart/add", func(c fiber.Ctx) error {
|
|
sess := session.FromContext(c)
|
|
|
|
cart, _ := sess.Get("cart").([]string)
|
|
cart = append(cart, c.FormValue("item_id"))
|
|
sess.Set("cart", cart)
|
|
|
|
return c.JSON(fiber.Map{"items": len(cart)})
|
|
})
|
|
|
|
// Login (preserve session data)
|
|
app.Post("/login", func(c fiber.Ctx) error {
|
|
sess := session.FromContext(c)
|
|
|
|
// Simple validation (implement proper authentication)
|
|
email := c.FormValue("email")
|
|
password := c.FormValue("password")
|
|
if email != "user@example.com" || password != "password" {
|
|
return c.Status(401).JSON(fiber.Map{"error": "Invalid credentials"})
|
|
}
|
|
|
|
// Regenerate session ID for security
|
|
// This changes the session ID while preserving existing data
|
|
if err := sess.Regenerate(); err != nil {
|
|
return c.Status(500).JSON(fiber.Map{"error": "Session error"})
|
|
}
|
|
|
|
sess.Set("user_id", 1)
|
|
sess.Set("authenticated", true)
|
|
|
|
return c.JSON(fiber.Map{"status": "logged in"})
|
|
})
|
|
|
|
// Logout (clear everything)
|
|
app.Post("/logout", func(c fiber.Ctx) error {
|
|
sess := session.FromContext(c)
|
|
|
|
// Reset clears all data and generates new session ID
|
|
if err := sess.Reset(); err != nil {
|
|
return c.Status(500).JSON(fiber.Map{"error": "Session error"})
|
|
}
|
|
|
|
return c.JSON(fiber.Map{"status": "logged out"})
|
|
})
|
|
|
|
app.Listen(":3000")
|
|
}
|
|
|
|
// Helper functions (implement these properly in production)
|
|
func isValidUser(email, password string) bool {
|
|
return email == "user@example.com" && password == "password"
|
|
}
|
|
|
|
func getUserID(email string) int {
|
|
return 1 // Return actual user ID from database
|
|
}
|
|
```
|
|
|
|
### API with Header-based Sessions
|
|
|
|
```go
|
|
import (
|
|
"time"
|
|
"github.com/gofiber/fiber/v3"
|
|
"github.com/gofiber/fiber/v3/middleware/session"
|
|
"github.com/gofiber/storage/redis"
|
|
)
|
|
|
|
func main() {
|
|
app := fiber.New()
|
|
|
|
// API session middleware with header extraction
|
|
app.Use(session.New(session.Config{
|
|
Storage: redis.New(),
|
|
Extractor: session.FromHeader("X-Session-Token"),
|
|
IdleTimeout: time.Hour,
|
|
}))
|
|
|
|
// API endpoint
|
|
app.Post("/api/data", func(c fiber.Ctx) error {
|
|
sess := session.FromContext(c)
|
|
|
|
// Track API usage
|
|
count, _ := sess.Get("api_calls").(int)
|
|
count++
|
|
sess.Set("api_calls", count)
|
|
sess.Set("last_call", time.Now())
|
|
|
|
return c.JSON(fiber.Map{
|
|
"data": "some data",
|
|
"calls": count,
|
|
})
|
|
})
|
|
|
|
app.Listen(":3000")
|
|
}
|
|
```
|
|
|
|
### Multi-source Session ID Support
|
|
|
|
```go
|
|
import (
|
|
"github.com/gofiber/fiber/v3"
|
|
"github.com/gofiber/fiber/v3/middleware/session"
|
|
)
|
|
|
|
func main() {
|
|
app := fiber.New()
|
|
|
|
// Support multiple sources with priority
|
|
app.Use(session.New(session.Config{
|
|
Extractor: session.Chain(
|
|
session.FromCookie("session_id"), // 1st: Cookie (web)
|
|
session.FromHeader("X-Session-ID"), // 2nd: Header (API)
|
|
session.FromQuery("session_id"), // 3rd: Query (fallback)
|
|
),
|
|
}))
|
|
|
|
app.Get("/", func(c fiber.Ctx) error {
|
|
sess := session.FromContext(c)
|
|
|
|
// Works with any of the above methods
|
|
return c.JSON(fiber.Map{
|
|
"session_id": sess.ID(),
|
|
"source": "multi-source",
|
|
})
|
|
})
|
|
|
|
app.Listen(":3000")
|
|
}
|
|
```
|