Skip to content

Commit ddb6f8c

Browse files
committed
feat: enhance authentication flow and session management
- Added frontend URL to OIDC configuration for improved redirect handling. - Updated session management to pass token expiration to the set_session function. - Modified logout endpoint to return a logout URL for Keycloak, enhancing user experience. - Adjusted CORS settings to restrict allowed origins for better security.
1 parent be1aa11 commit ddb6f8c

File tree

4 files changed

+34
-24
lines changed

4 files changed

+34
-24
lines changed

src/backend/config.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
'client_secret': os.getenv('OIDC_CLIENT_SECRET'),
1818
'server_url': os.getenv('OIDC_SERVER_URL'),
1919
'realm': os.getenv('OIDC_REALM'),
20-
'redirect_uri': os.getenv('REDIRECT_URI')
20+
'redirect_uri': os.getenv('REDIRECT_URI'),
21+
'frontend_url': os.getenv('FRONTEND_URL')
2122
}
2223

2324
# Redis connection
@@ -36,8 +37,8 @@ def get_session(session_id: str) -> Optional[Dict[str, Any]]:
3637
return json.loads(session_data)
3738
return None
3839

39-
def set_session(session_id: str, data: Dict[str, Any], expiry: int = 86400) -> None:
40-
"""Store session data in Redis with expiry in seconds (default 24 hours)"""
40+
def set_session(session_id: str, data: Dict[str, Any], expiry: int) -> None:
41+
"""Store session data in Redis with expiry in seconds"""
4142
redis_client.setex(
4243
f"session:{session_id}",
4344
expiry,
@@ -56,7 +57,8 @@ def get_auth_url() -> str:
5657
params = {
5758
'client_id': OIDC_CONFIG['client_id'],
5859
'response_type': 'code',
59-
'redirect_uri': OIDC_CONFIG['redirect_uri']
60+
'redirect_uri': OIDC_CONFIG['redirect_uri'],
61+
'scope': 'openid profile email'
6062
}
6163
return f"{auth_url}?{'&'.join(f'{k}={v}' for k,v in params.items())}"
6264

@@ -126,7 +128,8 @@ async def refresh_token(session_id: str, token_data: Dict[str, Any]) -> Tuple[bo
126128
new_token_data = refresh_response.json()
127129

128130
# Update session with new tokens
129-
set_session(session_id, new_token_data)
131+
expiry = new_token_data['expires_in']
132+
set_session(session_id, new_token_data, expiry)
130133

131134
return True, new_token_data
132135
except Exception as e:

src/backend/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ async def lifespan(_: FastAPI):
3737
# CORS middleware setup
3838
app.add_middleware(
3939
CORSMiddleware,
40-
allow_origins=["*"],
40+
allow_origins=["https://kc.pad.ws", "https://alex.pad.ws"],
4141
allow_credentials=True,
4242
allow_methods=["*"],
4343
allow_headers=["*"],

src/backend/routers/auth.py

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
import jwt
33
import httpx
44
from fastapi import APIRouter, Request, HTTPException, Depends
5-
from fastapi.responses import RedirectResponse, FileResponse
5+
from fastapi.responses import RedirectResponse, FileResponse, JSONResponse
66
import os
77

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

@@ -72,24 +72,26 @@ async def callback(request: Request, code: str, state: str = "default"):
7272
return FileResponse(os.path.join(STATIC_DIR, "auth/popup-close.html"))
7373
else:
7474
return RedirectResponse('/')
75-
75+
7676
@auth_router.get("/logout")
7777
async def logout(request: Request):
7878
session_id = request.cookies.get('session_id')
79-
if session_id:
80-
delete_session(session_id)
8179

82-
# Create a response that doesn't redirect but still clears the cookie
83-
from fastapi.responses import JSONResponse
84-
response = JSONResponse({"status": "success", "message": "Logged out successfully"})
80+
session_data = get_session(session_id)
81+
if not session_data:
82+
return RedirectResponse('/')
83+
84+
id_token = session_data.get('id_token', '')
85+
86+
# Delete the session from Redis
87+
delete_session(session_id)
88+
89+
# Create the Keycloak logout URL with redirect back to our app
90+
logout_url = f"{OIDC_CONFIG['server_url']}/realms/{OIDC_CONFIG['realm']}/protocol/openid-connect/logout"
91+
redirect_uri = OIDC_CONFIG['frontend_url'] # Match the frontend redirect URI
92+
full_logout_url = f"{logout_url}?id_token_hint={id_token}&post_logout_redirect_uri={redirect_uri}"
8593

86-
# Clear the session_id cookie with all necessary parameters
87-
response.delete_cookie(
88-
key="session_id",
89-
path="/",
90-
domain=None, # Use None to match the current domain
91-
secure=request.url.scheme == "https",
92-
httponly=True
93-
)
94+
# Create a redirect response to Keycloak's logout endpoint
95+
response = JSONResponse({"status": "success", "logout_url": full_logout_url})
9496

9597
return response

src/frontend/src/ui/MainMenu.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,11 +286,13 @@ export const MainMenuConfig: React.FC<MainMenuConfigProps> = ({
286286
capture('logout_clicked');
287287

288288
try {
289-
// Call the logout endpoint but don't follow the redirect
290-
await fetch('/auth/logout', {
289+
// Call the logout endpoint and get the session_id
290+
const response = await fetch('/auth/logout', {
291291
method: 'GET',
292292
credentials: 'include'
293293
});
294+
const data = await response.json();
295+
const logoutUrl = data.logout_url;
294296

295297
// Clear the session_id cookie client-side
296298
document.cookie = "session_id=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
@@ -299,6 +301,9 @@ export const MainMenuConfig: React.FC<MainMenuConfigProps> = ({
299301
queryClient.invalidateQueries({ queryKey: ['auth'] });
300302
queryClient.invalidateQueries({ queryKey: ['userProfile'] });
301303

304+
// Redirect to the logout URL
305+
window.location.replace(logoutUrl);
306+
302307
console.log("Logged out successfully");
303308
} catch (error) {
304309
console.error("Logout failed:", error);

0 commit comments

Comments
 (0)