Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions blacksheep/docs/css/extra.css
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ span.task-list-indicator {
--nt-color-7: #108d10;
}

:root>*
{
--md-mermaid-sequence-number-bg-color: #00363e;
}

.version-warning {
margin-top: 5px !important;
}
Expand Down
159 changes: 145 additions & 14 deletions blacksheep/docs/sessions.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,87 @@
# Sessions

This page describes features to support sessions, handled with digitally signed
cookies.
Sessions are a mechanism used in web applications to persist user-specific data
across multiple requests. Since HTTP is a stateless protocol, each request from
a client is independent and does not retain information about previous
interactions. Sessions solve this by allowing the server to associate requests
with a particular user, typically by issuing a unique session identifier (often
stored in a cookie). This enables features like user authentication, shopping
carts, and personalized experiences.

/// admonition | Sessions are stored in cookies.
type: danger
This page describes built-in features to support sessions.

The built-in sessions store data in cookies on the client side. Therefore, web
applications using these features must implement [Anti-Forgery
validation](anti-request-forgery.md) to prevent Cross-Site Request Forgery
(XSRF/CSRF).
---

/// admonition | Sessions have been improved in `2.4.0`.
type: info

The built-in sessions have been improved in version `2.4.0` to support any kind
of store. Older versions of BlackSheep included built-in support for sessions
but offered only built-in support for storing sessions information in cookies.
Since version `2.4.0`, any kind of store is supported, through the
`SessionStore` interface described below in this page.

///

## Enabling sessions
## Sessions flows

The diagram below illustrates an high-level overview of how session works:

```mermaid
sequenceDiagram
autonumber
participant Client
participant Server

Client->>Server: HTTP Request (no session)
Server->>Server: Create session data
Server-->>Client: HTTP Response (include session information)
Client->>Server: HTTP Request (include session information)
Server->>Server: Obtain the whole session data using session information
Server-->>Client: HTTP Response (can update session information)
Note over Client,Server: Session information is exchanged via cookies<br/> or custom request headers.<br/>Session information can consist of the Session ID alone,<br/>or include a signed token of the whole session data.
```

The session information exchanged with the client can either be a _session ID_,
—in this case the actual session data is stored server-side— or the entire
session data contained within a digitally signed token.

### Example: session data stored in dedicated service

The diagram below illustrates a more specific scenario, in which the server
exchanges with the client only a _session ID_, and the whole session data is
stored in a third-party service such as [PostgreSQL](https://www.postgresql.org/)
server, or [Valkey](https://github.com/valkey-io/valkey) server.

```mermaid
sequenceDiagram
autonumber
participant Client
participant Server
participant Store as Session Store<br/>(e.g. PostgreSQL, Valkey)

Client->>Server: HTTP Request (no session)
Server->>Store: Create and store session data
Store-->>Server: Session ID
Server-->>Client: HTTP Response (Set-Cookie: session ID)
Client->>Server: HTTP Request (Cookie: session ID)
Server->>Store: Retrieve session data using session ID
Store-->>Server: Session data
Server-->>Client: HTTP Response (may update Set-Cookie)
```

In this scenario, Session ID is exchanged between client and server via
cookies. Full session data is stored in a third-party store. The server uses
the session ID from the cookie to restore session data for each request.

In alternative to cookies, it is possible to use other ways to exchange
information between the client and the server, such as custom response and
request headers.

## Enabling sessions, with cookie store

To enable sessions, use the `app.use_sessions` method as in the example below:
To enable sessions and have sessions stored in cookies, use the
`app.use_sessions` method as in the example below:

```python
from blacksheep import Application, Request, get, text
Expand All @@ -35,7 +101,10 @@ def home(request: Request):
return text(session["example"])
```

The `use_sessions` method accepts the following parameters:
The `<SIGNING_KEY>` is a key used to digitally sign and protect from tampering,
this key must be protected and not shared with clients.

For this scenario, the `use_sessions` method accepts the following parameters:

| Name | Description | Defaults to |
| --------------- | -------------------------------------------------------------------------------------- | ------------------------------------- |
Expand All @@ -48,7 +117,7 @@ The `use_sessions` method accepts the following parameters:
```python
def use_sessions(
self,
secret_key: str,
store: str,
*,
session_cookie: str = "session",
serializer: Optional[SessionSerializer] = None,
Expand All @@ -64,13 +133,75 @@ encrypt, and verify session cookies. Refer to [data
protection](dataprotection.md) for more information on how tokens are signed
and encrypted.

Before version `2.4.0`, BlackSheep offered built-in support only for sessions
stored entirely on the client side, in cookies. Using sessions with a different
store required custom code.

## Enabling sessions, with custom store

Starting from `2.4.0`, the built-in classes for sessions include support for
custom stores. To enable sessions and have sessions stored in the desired way,
implement a type of `SessionStore` like in the following example:

```python
from blacksheep.sessions.abc import Session, SessionStore


class MySessionStore(SessionStore):

async def load(self, request: Request) -> Session:
"""Load the session for the given request."""
...

async def save(
self, request: Request, response: Response, session: Session
) -> None:
"""Save the session related to the given request-response cycle."""
...
```

And then call the `app.use_sessions` passing as first argument an instance of
your custom store type.

```python
from blacksheep import Application, Request, get, text

from myapp.sessions import MySessionStore

app = Application()


app.use_sessions(MySessionStore(...))
```

When this option is used, all other parameters to the `use_sessions` method are
ignored.

### InMemorySessionStore

Since version `2.4.0`, an `InMemorySessionStore` is included in the
`blacksheep.sessions.memory` namespace.

```python
from blacksheep.sessions.memory import InMemorySessionStore
```

This kind of store is an in-memory implementation of `SessionStore` for
managing user sessions. The session ID is transmitted in cookies, to restore
the same session information across request-response cycles.

This session store keeps session data in a Python dictionary, mapping session
IDs to session data. It is suitable for development and testing environments,
but not recommended for production use as session data will be lost when the
application restarts and is not shared across multiple processes or servers.

## Using sessions

When sessions are enabled, they are always populated for the `request` object,
and can be accessed through the `request.session` property.

The sessions middleware takes care of setting a response cookie whenever the
session is modified, session cookies are signed and encrypted by default.
The sessions middleware takes care of saving session information whenever the
session is modified.

```python
@get("/")
Expand Down
53 changes: 45 additions & 8 deletions blacksheep/docs/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,21 @@ This page describes:

## Environmental variables

| Name | Category | Description |
| ------------------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| APP_ENV | Settings | This environment variable is read to determine the environment of the application. For more information, refer to [_Defining application environment_](/blacksheep/settings/#defining-application-environment). |
| APP_SHOW_ERROR_DETAILS | Settings | If "1" or "true", configures the application to display web pages with error details in case of HTTP 500 Internal Server Error. |
| APP_MOUNT_AUTO_EVENTS | Settings | If "1" or "true", automatically binds lifecycle events of mounted apps between children and parents BlackSheep applications. |
| APP_ROUTE_PREFIX | Settings | Allows configuring a global prefix for all routes handled by the application. For more information, refer to: [Behind proxies](/blacksheep/behind-proxies/). |
| APP_SECRET_<i>i</i> | Secrets | Allows configuring the secrets used by the application to protect data. |
| BLACKSHEEP_SECRET_PREFIX | Secrets | Allows specifying the prefix of environment variables used to configure application secrets, defaults to "APP_SECRET" if not specified. |
| Name | Category | Description |
| ------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| APP_ENV | Settings | This environment variable is read to determine the environment of the application. For more information, refer to [_Defining application environment_](/blacksheep/settings/#defining-application-environment). |
| APP_DEFAULT_ROUTER | Settings | Allows disabling the use of the default router exposed by BlackSheep. To disable the default router: `APP_DEFAULT_ROUTER=0`. |
| APP_SIGNAL_HANDLER | Settings | Allows enabling a callback to the `{signal.SIGINT, signal.SIGTERM}` signals to detect when the application process is stopping. Information is used in `blacksheep.server.process.is_stopping()` and is useful when handling long-lasting connections. See _[Server-Sent Events](./server-sent-events.md)_. |
| APP_JINJA_EXTENSION | Settings | Allows configuring the file extension used to work with Jinja templates. By default it is `.jinja`. |
| APP_JINJA_PACKAGE_NAME | Settings | Allows configuring the package name used by the default Jinja templates loader. By default it is `app` (as by default views are loaded from `app/views`. |
| APP_JINJA_PACKAGE_PATH | Settings | Allows configuring the path used by the default Jinja templates loader. By default it is `views` (as by default views are loaded from `app/views`. |
| APP_JINJA_DEBUG | Settings | Allows enabling Jinja debug mode, setting this env variable to a truthy value. |
| APP_JINJA_ENABLE_ASYNC | Settings | Allows enabling Jinja async mode, setting this env variable to a truthy value. |
| APP_SHOW_ERROR_DETAILS | Settings | If "1" or "true", configures the application to display web pages with error details in case of HTTP 500 Internal Server Error. |
| APP_MOUNT_AUTO_EVENTS | Settings | If "1" or "true", automatically binds lifecycle events of mounted apps between children and parents BlackSheep applications. |
| APP_ROUTE_PREFIX | Settings | Allows configuring a global prefix for all routes handled by the application. For more information, refer to: [Behind proxies](/blacksheep/behind-proxies/). |
| APP_SECRET_<i>i</i> | Secrets | Allows configuring the secrets used by the application to protect data. |
| BLACKSHEEP_SECRET_PREFIX | Secrets | Allows specifying the prefix of environment variables used to configure application secrets, defaults to "APP_SECRET" if not specified. |

## Defining application environment

Expand Down Expand Up @@ -148,6 +155,7 @@ def my_json(data: Any, status: int = 200) -> Response:
```

### Example: applying transformations during JSON operations

The example below illustrates how to apply transformations to objects while
they are serialized and deserialized. Beware that the example only illustrates
this possibility, it doesn't handle objects inside lists, `@dataclass`, or
Expand Down Expand Up @@ -194,3 +202,32 @@ json_settings.use(
dumps=custom_dumps,
)
```

## Encodings settings

Starting from version `2.4.0`, the framework provides settings to control how
`UnicodeDecodeError` exceptions are handled when parsing request bodies.

By default the framework raises an exception when the client sends a payload
specifying the wrong encoding in the `Content-Type` request header, or when
the client sends a payload that is not `UTF-8` encoded and without specifying
the charset encoding.

To customize the handling of `UnicodeDecodeError` and implement possible
fallbacks, import the `Decoder` class from the `blacksheep.settings.encodings`
namespace and create your own implementation. Subclasses of this class define a
strategy for decoding bytes into strings when a `UnicodeDecodeError` occurs
during standard decoding. You must implement the `decode` method, which
receives the bytes to decode and the original `UnicodeDecodeError`.

```python
from blacksheep.settings.encodings import Decoder, encodings_settings


class MyDecoder(Decoder):
"""Implements the Decoder interface."""
def decode(self, value: bytes, decode_error: UnicodeDecodeError) -> str: ...


encodings_settings.use(MyDecoder())
```