Skip to content

Commit

Permalink
Add domain parameter to SessionMiddleware (#2280)
Browse files Browse the repository at this point in the history
* feat: add domain argument to SessionMiddleware

* docs: keep existing formatting

* fix: add newline at end of file

* fix: comply with formatting

* fix: comply with formatting rules

* fix: set domain default value to empty string

* Make `domain` `Optional[str]`

---------

Co-authored-by: max <max@maxlabs.se>
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
  • Loading branch information
3 people authored Nov 3, 2023
1 parent e9f3f31 commit e017600
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 0 deletions.
2 changes: 2 additions & 0 deletions docs/middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ The following arguments are supported:
* `max_age` - Session expiry time in seconds. Defaults to 2 weeks. If set to `None` then the cookie will last as long as the browser session.
* `same_site` - SameSite flag prevents the browser from sending session cookie along with cross-site requests. Defaults to `'lax'`.
* `https_only` - Indicate that Secure flag should be set (can be used with HTTPS only). Defaults to `False`.
* `domain` - Domain of the cookie used to share cookie between subdomains or cross-domains. The browser defaults the domain to the same host that set the cookie, excluding subdomains [refrence](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#domain_attribute).


## HTTPSRedirectMiddleware

Expand Down
3 changes: 3 additions & 0 deletions starlette/middleware/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def __init__(
path: str = "/",
same_site: typing.Literal["lax", "strict", "none"] = "lax",
https_only: bool = False,
domain: typing.Optional[str] = None,
) -> None:
self.app = app
self.signer = itsdangerous.TimestampSigner(str(secret_key))
Expand All @@ -29,6 +30,8 @@ def __init__(
self.security_flags = "httponly; samesite=" + same_site
if https_only: # Secure flag can be used with HTTPS only
self.security_flags += "; secure"
if domain is not None:
self.security_flags += f"; domain={domain}"

async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if scope["type"] not in ("http", "websocket"): # pragma: no cover
Expand Down
24 changes: 24 additions & 0 deletions tests/middleware/test_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,27 @@ def test_session_cookie(test_client_factory):
client.cookies.delete("session")
response = client.get("/view_session")
assert response.json() == {"session": {}}


def test_domain_cookie(test_client_factory):
app = Starlette(
routes=[
Route("/view_session", endpoint=view_session),
Route("/update_session", endpoint=update_session, methods=["POST"]),
],
middleware=[
Middleware(SessionMiddleware, secret_key="example", domain=".example.com")
],
)
client: TestClient = test_client_factory(app)

response = client.post("/update_session", json={"some": "data"})
assert response.json() == {"session": {"some": "data"}}

# check cookie max-age
set_cookie = response.headers["set-cookie"]
assert "domain=.example.com" in set_cookie

client.cookies.delete("session")
response = client.get("/view_session")
assert response.json() == {"session": {}}

0 comments on commit e017600

Please sign in to comment.