diff --git a/docs/core/middleware/csrf.md b/docs/core/middleware/csrf.md index a034f9dfd7a..81274324387 100644 --- a/docs/core/middleware/csrf.md +++ b/docs/core/middleware/csrf.md @@ -34,7 +34,7 @@ app.Use(csrf.New(csrf.Config{ KeyLookup: "header:X-Csrf-Token", CookieName: "csrf_", CookieSameSite: "Lax", - Expiration: 1 * time.Hour, + IdleTimeout: 30 * time.Minute, KeyGenerator: utils.UUIDv4, Extractor: func(c fiber.Ctx) (string, error) { ... }, })) @@ -106,15 +106,14 @@ func (h *Handler) DeleteToken(c fiber.Ctx) error | CookieSecure | `bool` | Indicates if the CSRF cookie is secure. | false | | CookieHTTPOnly | `bool` | Indicates if the CSRF cookie is HTTP-only. | false | | CookieSameSite | `string` | Value of SameSite cookie. | "Lax" | -| CookieSessionOnly | `bool` | Decides whether the cookie should last for only the browser session. Ignores Expiration if set to true. | false | -| Expiration | `time.Duration` | Expiration is the duration before the CSRF token will expire. | 1 * time.Hour | +| CookieSessionOnly | `bool` | Decides whether the cookie should last for only the browser session. (cookie expires on close). | false | +| IdleTimeout | `time.Duration` | IdleTimeout is the duration of inactivity before the CSRF token will expire. | 30 * time.Minute | | KeyGenerator | `func() string` | KeyGenerator creates a new CSRF token. | utils.UUID | | ErrorHandler | `fiber.ErrorHandler` | ErrorHandler is executed when an error is returned from fiber.Handler. | DefaultErrorHandler | | Extractor | `func(fiber.Ctx) (string, error)` | Extractor returns the CSRF token. If set, this will be used in place of an Extractor based on KeyLookup. | Extractor based on KeyLookup | | SingleUseToken | `bool` | SingleUseToken indicates if the CSRF token be destroyed and a new one generated on each use. (See TokenLifecycle) | false | | Storage | `fiber.Storage` | Store is used to store the state of the middleware. | `nil` | | Session | `*session.Store` | Session is used to store the state of the middleware. Overrides Storage if set. | `nil` | -| SessionKey | `string` | SessionKey is the key used to store the token in the session. | "csrfToken" | | TrustedOrigins | `[]string` | TrustedOrigins is a list of trusted origins for unsafe requests. This supports subdomain matching, so you can use a value like "https://*.example.com" to allow any subdomain of example.com to submit requests. | `[]` | ### Default Config @@ -124,11 +123,10 @@ var ConfigDefault = Config{ KeyLookup: "header:" + HeaderName, CookieName: "csrf_", CookieSameSite: "Lax", - Expiration: 1 * time.Hour, + IdleTimeout: 30 * time.Minute, KeyGenerator: utils.UUIDv4, ErrorHandler: defaultErrorHandler, Extractor: FromHeader(HeaderName), - SessionKey: "csrfToken", } ``` @@ -144,12 +142,11 @@ var ConfigDefault = Config{ CookieSecure: true, CookieSessionOnly: true, CookieHTTPOnly: true, - Expiration: 1 * time.Hour, + IdleTimeout: 30 * time.Minute, KeyGenerator: utils.UUIDv4, ErrorHandler: defaultErrorHandler, Extractor: FromHeader(HeaderName), Session: session.Store, - SessionKey: "csrfToken", } ``` @@ -304,7 +301,7 @@ The Referer header is automatically included in requests by all modern browsers, ## Token Lifecycle -Tokens are valid until they expire or until they are deleted. By default, tokens are valid for 1 hour, and each subsequent request extends the expiration by 1 hour. The token only expires if the user doesn't make a request for the duration of the expiration time. +Tokens are valid until they expire or until they are deleted. By default, tokens are valid for 30 minutes, and each subsequent request extends the expiration by the idle timeout. The token only expires if the user doesn't make a request for the duration of the idle timeout. ### Token Reuse diff --git a/docs/core/middleware/session.md b/docs/core/middleware/session.md index 39b9ccc801e..ff73ff6094c 100644 --- a/docs/core/middleware/session.md +++ b/docs/core/middleware/session.md @@ -2,142 +2,481 @@ id: session --- -# Session +# Session Middleware for [Fiber](https://github.com/gofiber/fiber) -Session middleware for [Fiber](https://github.com/gofiber/fiber). +The `session` middleware provides 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 (see examples below). + +As of v3, we recommend using the middleware handler for session management. However, for backward compatibility, v2's session methods are still available, allowing you to continue using the session management techniques from earlier versions of Fiber. Both methods are demonstrated in the examples. + +## Table of Contents + +- [Migration Guide](#migration-guide) + - [v2 to v3](#v2-to-v3) +- [Types](#types) + - [Config](#config) + - [Middleware](#middleware) + - [Session](#session) + - [Store](#store) +- [Signatures](#signatures) + - [Session Package Functions](#session-package-functions) + - [Config Methods](#config-methods) + - [Middleware Methods](#middleware-methods) + - [Session Methods](#session-methods) + - [Store Methods](#store-methods) +- [Examples](#examples) + - [Middleware Handler (Recommended)](#middleware-handler-recommended) + - [Custom Storage Example](#custom-storage-example) + - [Session Without Middleware Handler](#session-without-middleware-handler) + - [Custom Types in Session Data](#custom-types-in-session-data) +- [Config](#config) +- [Default Config](#default-config) + +## Migration Guide + +### v2 to v3 + +- **Function Signature Change**: In v3, the `New` function now returns a middleware handler instead of a `*Store`. To access the store, use the `Store` method on `*Middleware` (obtained from `session.FromContext(c)` in a handler) or use `NewStore` or `NewWithStore`. + +- **Session Lifecycle Management**: The `*Store.Save` method no longer releases the instance automatically. You must manually call `sess.Release()` after using the session to manage its lifecycle properly. + +- **Expiration Handling**: Previously, the `Expiration` field represented the maximum session duration before expiration. However, it would extend every time the session was saved, making its behavior a mix between session duration and session idle timeout. The `Expiration` field has been removed and replaced with `IdleTimeout` and `AbsoluteTimeout` fields, which explicitly defines the session's idle and absolute timeout periods. + + - **Idle Timeout**: The new `IdleTimeout`, handles session inactivity. If the session is idle for the specified duration, it will expire. The idle timeout is updated when the session is saved. If you are using the middleware handler, the idle timeout will be updated automatically. + + - **Absolute Timeout**: The `AbsoluteTimeout` field has been added. If you need to set an absolute session timeout, you can use this field to define the duration. The session will expire after the specified duration, regardless of activity. + +For more details about Fiber v3, see [What’s New](https://github.com/gofiber/fiber/blob/main/docs/whats_new.md). + +### Migrating v2 to v3 Example (Legacy Approach) + +To convert a v2 example to use the v3 legacy approach, follow these steps: + +1. **Initialize with Store**: Use `session.NewStore()` to obtain a store. +2. **Retrieve Session**: Access the session store using the `store.Get(c)` method. +3. **Release Session**: Ensure that you call `sess.Release()` after you are done with the session to manage its lifecycle. :::note -This middleware uses our [Storage](https://github.com/gofiber/storage) package to support various databases through a single interface. The default configuration for this middleware saves data to memory, see the examples below for other databases. +When using the legacy approach, the IdleTimeout will be updated when the session is saved. ::: +#### Example Conversion + +**v2 Example:** + +```go +store := session.New() + +app.Get("/", func(c *fiber.Ctx) error { + sess, err := store.Get(c) + if err != nil { + return err + } + + key, ok := sess.Get("key").(string) + if !ok { + return c.SendStatus(fiber.StatusInternalServerError) + } + + sess.Set("key", "value") + + err = sess.Save() + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + + return nil +}) +``` + +**v3 Legacy Approach:** + +```go +store := session.NewStore() + +app.Get("/", func(c fiber.Ctx) error { + sess, err := store.Get(c) + if err != nil { + return err + } + defer sess.Release() // Important: Release the session + + key, ok := sess.Get("key").(string) + if !ok { + return c.SendStatus(fiber.StatusInternalServerError) + } + + sess.Set("key", "value") + + err = sess.Save() + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + + return nil +}) +``` + +### v3 Example (Recommended Middleware Handler) + +Do not call `sess.Release()` when using the middleware handler. `sess.Save()` is also not required, as the middleware automatically saves the session data. + +For the recommended approach, use the middleware handler. See the [Middleware Handler (Recommended)](#middleware-handler-recommended) section for details. + +## Types + +### Config + +Defines the configuration options for the session middleware. + +```go +type Config struct { + Storage fiber.Storage + Next func(fiber.Ctx) bool + Store *Store + ErrorHandler func(fiber.Ctx, error) + KeyGenerator func() string + KeyLookup string + CookieDomain string + CookiePath string + CookieSameSite string + IdleTimeout time.Duration + AbsoluteTimeout time.Duration + CookieSecure bool + CookieHTTPOnly bool + CookieSessionOnly bool +} +``` + +### Middleware + +The `Middleware` struct encapsulates the session middleware configuration and storage, created via `New` or `NewWithStore`. + +```go +type Middleware struct { + Session *Session +} +``` + +### Session + +Represents a user session, accessible through `FromContext` or `Store.Get`. + +```go +type Session struct {} +``` + +### Store + +Handles session data management and is created using `NewStore`, `NewWithStore` or by accessing the `Store` method of a middleware instance. + +```go +type Store struct { + Config +} +``` + ## Signatures +### Session Package Functions + ```go -func New(config ...Config) *Store -func (s *Store) RegisterType(i any) -func (s *Store) Get(c fiber.Ctx) (*Session, error) -func (s *Store) Delete(id string) error -func (s *Store) Reset() error +func New(config ...Config) *Middleware +func NewWithStore(config ...Config) (fiber.Handler, *Store) +func FromContext(c fiber.Ctx) *Middleware +``` + +### Config Methods + +```go +func DefaultErrorHandler(fiber.Ctx, err error) +``` + +### Middleware Methods + +```go +func (m *Middleware) Set(key string, value any) +func (m *Middleware) Get(key string) any +func (m *Middleware) Delete(key string) +func (m *Middleware) Destroy() error +func (m *Middleware) Reset() error +func (m *Middleware) Store() *Store +``` +### Session Methods + +```go +func (s *Session) Fresh() bool +func (s *Session) ID() string func (s *Session) Get(key string) any func (s *Session) Set(key string, val any) -func (s *Session) Delete(key string) func (s *Session) Destroy() error -func (s *Session) Reset() error func (s *Session) Regenerate() error +func (s *Session) Release() +func (s *Session) Reset() error func (s *Session) Save() error -func (s *Session) Fresh() bool -func (s *Session) ID() string func (s *Session) Keys() []string -func (s *Session) SetExpiry(exp time.Duration) +func (s *Session) SetIdleTimeout(idleTimeout time.Duration) +``` + +### Store Methods + +```go +func (*Store) RegisterType(i any) +func (s *Store) Get(c fiber.Ctx) (*Session, error) +func (s *Store) GetByID(id string) (*Session, error) +func (s *Store) Reset() error +func (s *Store) Delete(id string) error ``` -:::caution -Storing `any` values are limited to built-ins Go types. +:::note + +#### `GetByID` Method + +The `GetByID` method retrieves a session from storage using its session ID. Unlike `Get`, which ties the session to a `fiber.Ctx` (request-response cycle), `GetByID` operates independently of any HTTP context. This makes it ideal for scenarios such as background processing, scheduled tasks, or non-HTTP-related session management. + +##### Key Features + +- **Context Independence**: Sessions retrieved via `GetByID` are not bound to `fiber.Ctx`. This means the session can be manipulated in contexts that aren't tied to an active HTTP request-response cycle. +- **Background Task Suitability**: Use this method when you need to manage sessions outside of the standard HTTP workflow, such as in scheduled jobs, background tasks, or any non-HTTP context where session data needs to be accessed or modified. + +##### Usage Considerations + +- **Manual Persistence**: Since there is no associated `fiber.Ctx`, changes made to the session (e.g., modifying data) will **not** automatically be saved to storage. You **must** call `session.Save()` explicitly to persist any updates to storage. +- **No Automatic Cookie Handling**: Any updates made to the session will **not** affect the client-side cookies. If the session changes need to be reflected in the client (e.g., in a future HTTP response), you will need to handle this manually by setting the cookies via other methods. +- **Resource Management**: After using a session retrieved by `GetByID`, you should call `session.Release()` to properly release the session back to the pool and free up resources. + +##### Example Use Cases + +- **Scheduled Jobs**: Retrieve and update session data periodically without triggering an HTTP request. +- **Background Processing**: Manage sessions for tasks running in the background, such as user inactivity checks or batch processing. + ::: ## Examples -Import the middleware package that is part of the Fiber web framework +:::note +**Security Notice**: For robust security, especially during sensitive operations like account changes or transactions, consider using CSRF protection. Fiber provides a [CSRF Middleware](https://docs.gofiber.io/api/middleware/csrf) that can be used with sessions to prevent CSRF attacks. +::: + +:::note +**Middleware Order**: The order of middleware matters. The session middleware should come before any handler or middleware that uses the session (for example, the CSRF middleware). +::: + +### Middleware Handler (Recommended) ```go +package main + import ( "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/csrf" "github.com/gofiber/fiber/v3/middleware/session" ) + +func main() { + app := fiber.New() + + sessionMiddleware, sessionStore := session.NewWithStore() + + app.Use(sessionMiddleware) + app.Use(csrf.New(csrf.Config{ + Store: sessionStore, + })) + + app.Get("/", func(c fiber.Ctx) error { + sess := session.FromContext(c) + if sess == nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + + name, ok := sess.Get("name").(string) + if !ok { + return c.SendString("Welcome anonymous user!") + } + + return c.SendString("Welcome " + name) + }) + + app.Listen(":3000") +} ``` -After you initiate your Fiber app, you can use the following possibilities: +### Custom Storage Example ```go -// Initialize default config -// This stores all of your app's sessions -store := session.New() +package main -app.Get("/", func(c fiber.Ctx) error { - // Get session from storage - sess, err := store.Get(c) - if err != nil { - panic(err) - } +import ( + "github.com/gofiber/fiber/v3" + "github.com/gofiber/storage/sqlite3" + "github.com/gofiber/fiber/v3/middleware/csrf" + "github.com/gofiber/fiber/v3/middleware/session" +) - // Get value - name := sess.Get("name") +func main() { + app := fiber.New() - // Set key/value - sess.Set("name", "john") + storage := sqlite3.New() + sessionMiddleware, sessionStore := session.NewWithStore(session.Config{ + Storage: storage, + }) - // Get all Keys - keys := sess.Keys() + app.Use(sessionMiddleware) + app.Use(csrf.New(csrf.Config{ + Store: sessionStore, + })) - // Delete key - sess.Delete("name") + app.Listen(":3000") +} +``` - // Destroy session - if err := sess.Destroy(); err != nil { - panic(err) - } +### Session Without Middleware Handler - // Sets a specific expiration for this session - sess.SetExpiry(time.Second * 2) +```go +package main - // Save session - if err := sess.Save(); err != nil { - panic(err) - } +import ( + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/csrf" + "github.com/gofiber/fiber/v3/middleware/session" +) - return c.SendString(fmt.Sprintf("Welcome %v", name)) -}) -``` +func main() { + app := fiber.New() -## Config + sessionStore := session.NewStore() -| Property | Type | Description | Default | -|:------------------------|:----------------|:------------------------------------------------------------------------------------------------------------|:----------------------| -| Expiration | `time.Duration` | Allowed session duration. | `24 * time.Hour` | -| Storage | `fiber.Storage` | Storage interface to store the session data. | `memory.New()` | -| KeyLookup | `string` | KeyLookup is a string in the form of "`:`" that is used to extract session id from the request. | `"cookie:session_id"` | -| CookieDomain | `string` | Domain of the cookie. | `""` | -| CookiePath | `string` | Path of the cookie. | `""` | -| CookieSecure | `bool` | Indicates if cookie is secure. | `false` | -| CookieHTTPOnly | `bool` | Indicates if cookie is HTTP only. | `false` | -| CookieSameSite | `string` | Value of SameSite cookie. | `"Lax"` | -| CookieSessionOnly | `bool` | Decides whether cookie should last for only the browser session. Ignores Expiration if set to true. | `false` | -| KeyGenerator | `func() string` | KeyGenerator generates the session key. | `utils.UUIDv4` | -| CookieName (Deprecated) | `string` | Deprecated: Please use KeyLookup. The session name. | `""` | + app.Use(csrf.New(csrf.Config{ + Store: sessionStore, + })) -## Default Config + app.Get("/", func(c fiber.Ctx) error { + sess, err := sessionStore.Get(c) + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + defer sess.Release() -```go -var ConfigDefault = Config{ - Expiration: 24 * time.Hour, - KeyLookup: "cookie:session_id", - KeyGenerator: utils.UUIDv4, - source: "cookie", - sessionName: "session_id", + name, ok := sess.Get("name").(string) + if !ok { + return c.SendString("Welcome anonymous user!") + } + + return c.SendString("Welcome " + name) + }) + + app.Post("/login", func(c fiber.Ctx) error { + sess, err := sessionStore.Get(c) + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + defer sess.Release() + + if !sess.Fresh() { + if err := sess.Regenerate(); err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + } + + sess.Set("name", "John Doe") + + err = sess.Save() + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + + return c.SendString("Logged in!") + }) + + app.Listen(":3000") } ``` -## Constants +### Custom Types in Session Data + +Session data can only be of the following types by default: + +- `string` +- `int` +- `int8` +- `int16` +- `int32` +- `int64` +- `uint` +- `uint8` +- `uint16` +- `uint32` +- `uint64` +- `bool` +- `float32` +- `float64` +- `[]byte` +- `complex64` +- `complex128` +- `interface{}` + +To support other types in session data, you can register custom types. Here is an example of how to register a custom type: ```go -const ( - SourceCookie Source = "cookie" - SourceHeader Source = "header" - SourceURLQuery Source = "query" +package main + +import ( + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/session" ) -``` -### Custom Storage/Database +type User struct { + Name string + Age int +} -You can use any storage from our [storage](https://github.com/gofiber/storage/) package. +func main() { + app := fiber.New() -```go -storage := sqlite3.New() // From github.com/gofiber/storage/sqlite3 + sessionMiddleware, sessionStore := session.NewWithStore() + sessionStore.RegisterType(User{}) -store := session.New(session.Config{ - Storage: storage, -}) + app.Use(sessionMiddleware) + + app.Listen(":3000") +} ``` -To use the store, see the [Examples](#examples). +## Config + +| Property | Type | Description | Default | +|-----------------------|--------------------------------|--------------------------------------------------------------------------------------------|---------------------------| +| **Storage** | `fiber.Storage` | Defines where session data is stored. | `nil` (in-memory storage) | +| **Next** | `func(c fiber.Ctx) bool` | Function to skip this middleware under certain conditions. | `nil` | +| **ErrorHandler** | `func(c fiber.Ctx, err error)` | Custom error handler for session middleware errors. | `nil` | +| **KeyGenerator** | `func() string` | Function to generate session IDs. | `UUID()` | +| **KeyLookup** | `string` | Key used to store session ID in cookie or header. | `"cookie:session_id"` | +| **CookieDomain** | `string` | The domain scope of the session cookie. | `""` | +| **CookiePath** | `string` | The path scope of the session cookie. | `"/"` | +| **CookieSameSite** | `string` | The SameSite attribute of the session cookie. | `"Lax"` | +| **IdleTimeout** | `time.Duration` | Maximum duration of inactivity before session expires. | `30 * time.Minute` | +| **AbsoluteTimeout** | `time.Duration` | Maximum duration before session expires. | `0` (no expiration) | +| **CookieSecure** | `bool` | Ensures session cookie is only sent over HTTPS. | `false` | +| **CookieHTTPOnly** | `bool` | Ensures session cookie is not accessible to JavaScript (HTTP only). | `true` | +| **CookieSessionOnly** | `bool` | Prevents session cookie from being saved after the session ends (cookie expires on close). | `false` | + +## Default Config + +```go +session.Config{ + Storage: memory.New(), + Next: nil, + Store: nil, + ErrorHandler: nil, + KeyGenerator: utils.UUIDv4, + KeyLookup: "cookie:session_id", + CookieDomain: "", + CookiePath: "", + CookieSameSite: "Lax", + IdleTimeout: 30 * time.Minute, + AbsoluteTimeout: 0, + CookieSecure: false, + CookieHTTPOnly: false, + CookieSessionOnly: false, +} +``` diff --git a/docs/core/whats_new.md b/docs/core/whats_new.md index e040f367d5f..53c2f9d8e69 100644 --- a/docs/core/whats_new.md +++ b/docs/core/whats_new.md @@ -30,6 +30,7 @@ Here's a quick overview of the changes in Fiber `v3`: - [🧰 Generic functions](#-generic-functions) - [🧬 Middlewares](#-middlewares) - [CORS](#cors) + - [CSRF](#csrf) - [Session](#session) - [Filesystem](#filesystem) - [Monitor](#monitor) @@ -316,9 +317,19 @@ Added support for specifying Key length when using `encryptcookie.GenerateKey(le ### Session -:::caution -DRAFT section -::: +The Session middleware has undergone key changes in v3 to improve functionality and flexibility. While v2 methods remain available for backward compatibility, we now recommend using the new middleware handler for session management. + +#### Key Updates + +- **New Middleware Handler**: The `New` function now returns a middleware handler instead of a `*Store`. To access the session store, use the `Store` method on the middleware, or opt for `NewStore` or `NewWithStore` for custom store integration. + +- **Manual Session Release**: Session instances are no longer automatically released after being saved. To ensure proper lifecycle management, you must manually call `sess.Release()`. + +- **Idle Timeout**: The `Expiration` field has been replaced with `IdleTimeout`, which handles session inactivity. If the session is idle for the specified duration, it will expire. The idle timeout is updated when the session is saved. If you are using the middleware handler, the idle timeout will be updated automatically. + +- **Absolute Timeout**: The `AbsoluteTimeout` field has been added. If you need to set an absolute session timeout, you can use this field to define the duration. The session will expire after the specified duration, regardless of activity. + +For more details on these changes and migration instructions, check the [Session Middleware Migration Guide](./middleware/session.md#migration-guide). ### Filesystem @@ -521,6 +532,24 @@ app.Use(cors.New(cors.Config{ })) ``` +#### CSRF + +- **Field Renaming**: The `Expiration` field in the CSRF middleware configuration has been renamed to `IdleTimeout` to better describe its functionality. Additionally, the default value has been reduced from 1 hour to 30 minutes. Update your code as follows: + +```go +// Before +app.Use(csrf.New(csrf.Config{ + Expiration: 10 * time.Minute, +})) + +// After +app.Use(csrf.New(csrf.Config{ + IdleTimeout: 10 * time.Minute, +})) +``` + +- **Session Key Removal**: The `SessionKey` field has been removed from the CSRF middleware configuration. The session key is now an unexported constant within the middleware to avoid potential key collisions in the session store. + #### Filesystem You need to move filesystem middleware to static middleware due to it has been removed from the core.