🔐 Session Management
Lila provides a simple yet powerful session management system built on top of
itsdangerous
for cryptographically signed session data. This ensures that session data cannot be
tampered with on the client side.
The
Session
class provides a set of static methods to handle session data.
📝 Setting Session Data
The
setSession
method is used to store session data in a cookie. It can handle strings, dictionaries,
and lists, which are automatically serialized to JSON.
from lila.core.session import Session
from lila.core.responses import JSONResponse
@router.route("/set-session", methods=["GET"])
async def set_session_example(request: Request):
response = JSONResponse({"message": "Session has been set"})
session_data = {"user_id": 123, "username": "lila_user"}
Session.setSession(new_val=session_data, response=response, name_cookie="user_session")
return response
The
setSession
method accepts several parameters to customize the cookie, such as
secure,
samesite,
max_age,
etc.
🔍 Getting Session Data
The
getSession
method retrieves the raw, signed session data from a cookie. To get the unsigned and
validated data, use
getSessionValue
or
unsign.
✅ Unsigning and Validating Session Data
The
unsign
and
getSessionValue
methods get the session data in its original form after verifying the signature.
getSessionValue
is recommended because it also gracefully handles expired signatures.
from lila.core.session import Session
@router.route("/get-session", methods=["GET"])
async def get_session_example(request: Request):
session_data = Session.getSessionValue(request=request, key="user_session")
if session_data:
return JSONResponse({"session_data": session_data})
else:
return JSONResponse({"message": "No session found or session is invalid/expired."}, status_code=401)
🗑️ Deleting a Session
The
deleteSession
method is used to delete a session cookie from the client's browser.
from lila.core.session import Session
from lila.core.responses import JSONResponse
@router.route("/logout", methods=["GET"])
async def logout(request: Request):
response = JSONResponse({"message": "Logged out successfully"})
Session.deleteSession(response=response, name_cookie="user_session")
return response
🚀 Simplified Async Helpers
For convenience, Lila provides async static methods in the
Session
class that simplify common operations.
from lila.core.session import Session
# Get session data
data = await Session.get(request, key="auth")
# Set session data (requires response)
await Session.set(request, response, data={"id": 1}, key="auth")
# Delete session data
await Session.delete(response, key="auth")
📖 Full Session Class Reference
Here is the full code for the
Session
class for your reference.
import orjson
from app.config import SECRET_KEY
from itsdangerous import BadSignature, URLSafeTimedSerializer, SignatureExpired
from core.request import Request
from typing import Union, Optional, Dict, List
from core.logger import Logger
serializer = URLSafeTimedSerializer(SECRET_KEY)
class Session:
@staticmethod
def setSession(
new_val: str | dict | list,
response,
name_cookie: str = "session",
secure: bool = True,
samesite: str = "strict",
max_age: int = 3600,
domain: Optional[str] = None,
http_only: bool = True,
path: str = "/",
) -> bool:
try:
if isinstance(new_val, (dict, list)):
new_val = orjson.dumps(new_val).decode()
else:
new_val = str(new_val)
signed_session = serializer.dumps(new_val)
response.set_cookie(
key=name_cookie,
value=signed_session,
max_age=max_age,
expires=max_age,
secure=secure,
httponly=http_only,
samesite=samesite,
domain=domain,
path=path,
)
return True
except (TypeError, ValueError, Exception) as e:
Logger.error(f"Error setting session: {str(e)}")
return False
@staticmethod
def getSession(key: str, request: Request) -> Optional[str]:
return request.cookies.get(key)
@staticmethod
def unsign(
key: str, request: Request, max_age: Optional[int] = None
) -> Union[Dict, List, str, None]:
"""Unsign and return the session data for the given cookie key."""
session_data = request.cookies.get(key)
if not session_data:
return None
try:
unsigned_data = serializer.loads(session_data, max_age=max_age)
try:
return orjson.loads(unsigned_data)
except orjson.JSONDecodeError:
return unsigned_data
except SignatureExpired:
Logger.warning(f"Session expired for cookie: {key}")
return None
except BadSignature:
Logger.warning(f"Invalid session signature for cookie: {key}")
return None
except Exception as e:
Logger.error(f"Error unsigning session: {str(e)}")
return None
@staticmethod
def deleteSession(response, name_cookie: str) -> bool:
try:
response.delete_cookie(name_cookie)
return True
except Exception as e:
Logger.error(f"Error deleting session: {str(e)}")
return False
@staticmethod
def getSessionValue(
request: Request, key: str = "auth", max_age: Optional[int] = 3600
) -> Union[Dict, List, str, None]:
"""Convenience wrapper around unsign with a default max_age."""
return Session.unsign(key=key, request=request, max_age=max_age)
@staticmethod
async def get(request: Request, key: str = "auth") -> Union[Dict, List, str, None]:
"""Async helper to get session data."""
return Session.unsign(key=key, request=request)
@staticmethod
async def set(
request: Request, response, data: dict, key: str = "auth", max_age: int = 3600
) -> bool:
"""Async helper to set session data."""
return Session.setSession(new_val=data, response=response, name_cookie=key, max_age=max_age)
@staticmethod
async def delete(response, key: str = "auth") -> bool:
"""Async helper to delete session data."""
return Session.deleteSession(response=response, name_cookie=key)