Skip to content

Commit d83a409

Browse files
committed
feat: integrate Redis for session management
- Added Redis service to docker-compose for session storage. - Implemented Redis connection in config.py and updated session management functions. - Refactored dependencies to utilize Redis for session retrieval and deletion. - Updated requirements.txt to include Redis library.
1 parent b323607 commit d83a409

File tree

5 files changed

+63
-9
lines changed

5 files changed

+63
-9
lines changed

docker-compose.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ services:
1212
- postgres_data:/var/lib/postgresql/data
1313
restart: unless-stopped
1414
network_mode: host
15+
16+
redis:
17+
image: redis:alpine
18+
container_name: redis
19+
ports:
20+
- "6379:6379"
21+
volumes:
22+
- redis_data:/data
23+
restart: unless-stopped
24+
command: redis-server --save 60 1 --loglevel warning
25+
network_mode: host
1526

1627
keycloak:
1728
image: quay.io/keycloak/keycloak:25.0
@@ -73,6 +84,8 @@ services:
7384
- POSTGRES_DB=${POSTGRES_DB}
7485
- POSTGRES_HOST=localhost
7586
- POSTGRES_PORT=${POSTGRES_PORT}
87+
- REDIS_HOST=localhost
88+
- REDIS_PORT=6379
7689
- CODER_API_KEY=${CODER_API_KEY}
7790
- CODER_URL=http://localhost:${CODER_PORT}
7891
- CODER_TEMPLATE_ID=${CODER_TEMPLATE_ID}
@@ -81,3 +94,4 @@ services:
8194

8295
volumes:
8396
postgres_data:
97+
redis_data:

src/backend/config.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import os
2+
import json
3+
import redis
4+
from typing import Optional, Dict, Any
25
from dotenv import load_dotenv
36

47
load_dotenv()
@@ -14,7 +17,34 @@
1417
'redirect_uri': os.getenv('REDIRECT_URI')
1518
}
1619

17-
sessions = {}
20+
# Redis connection
21+
redis_client = redis.Redis(
22+
host=os.getenv('REDIS_HOST', 'localhost'),
23+
port=int(os.getenv('REDIS_PORT', 6379)),
24+
db=0,
25+
decode_responses=True
26+
)
27+
28+
# Session management functions
29+
def get_session(session_id: str) -> Optional[Dict[str, Any]]:
30+
"""Get session data from Redis"""
31+
session_data = redis_client.get(f"session:{session_id}")
32+
if session_data:
33+
return json.loads(session_data)
34+
return None
35+
36+
def set_session(session_id: str, data: Dict[str, Any], expiry: int = 86400) -> None:
37+
"""Store session data in Redis with expiry in seconds (default 24 hours)"""
38+
redis_client.setex(
39+
f"session:{session_id}",
40+
expiry,
41+
json.dumps(data)
42+
)
43+
44+
def delete_session(session_id: str) -> None:
45+
"""Delete session data from Redis"""
46+
redis_client.delete(f"session:{session_id}")
47+
1848
provisioning_times = {}
1949

2050
def get_auth_url() -> str:

src/backend/dependencies.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from typing import Optional
22
from fastapi import Request, HTTPException, Depends
33

4-
from config import sessions
4+
from config import get_session
55

66
class SessionData:
77
def __init__(self, access_token: str, token_data: dict):
@@ -15,7 +15,7 @@ def __init__(self, auto_error: bool = True):
1515
async def __call__(self, request: Request) -> Optional[SessionData]:
1616
session_id = request.cookies.get('session_id')
1717

18-
if not session_id or session_id not in sessions:
18+
if not session_id:
1919
if self.auto_error:
2020
raise HTTPException(
2121
status_code=401,
@@ -24,7 +24,15 @@ async def __call__(self, request: Request) -> Optional[SessionData]:
2424
)
2525
return None
2626

27-
session = sessions[session_id]
27+
session = get_session(session_id)
28+
if not session:
29+
if self.auto_error:
30+
raise HTTPException(
31+
status_code=401,
32+
detail="Not authenticated",
33+
headers={"WWW-Authenticate": "Bearer"},
34+
)
35+
return None
2836
return SessionData(
2937
access_token=session.get('access_token'),
3038
token_data=session

src/backend/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ PyJWT
88
requests
99
sqlalchemy
1010
posthog
11+
redis

src/backend/routers/auth.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from fastapi.responses import RedirectResponse, FileResponse
66
import os
77

8-
from config import get_auth_url, get_token_url, OIDC_CONFIG, sessions, STATIC_DIR
8+
from config import get_auth_url, get_token_url, OIDC_CONFIG, set_session, delete_session, STATIC_DIR
99
from dependencies import SessionData, require_auth
1010
from coder import CoderAPI
1111

@@ -48,8 +48,9 @@ async def callback(request: Request, code: str, state: str = "default"):
4848
if token_response.status_code != 200:
4949
raise HTTPException(status_code=400, detail="Auth failed")
5050

51-
sessions[session_id] = token_response.json()
52-
access_token = token_response.json()['access_token']
51+
token_data = token_response.json()
52+
set_session(session_id, token_data)
53+
access_token = token_data['access_token']
5354
user_info = jwt.decode(access_token, options={"verify_signature": False})
5455

5556
try:
@@ -69,8 +70,8 @@ async def callback(request: Request, code: str, state: str = "default"):
6970
@auth_router.get("/logout")
7071
async def logout(request: Request):
7172
session_id = request.cookies.get('session_id')
72-
if session_id in sessions:
73-
del sessions[session_id]
73+
if session_id:
74+
delete_session(session_id)
7475

7576
# Create a response that doesn't redirect but still clears the cookie
7677
from fastapi.responses import JSONResponse

0 commit comments

Comments
 (0)