|
2 | 2 | import jwt |
3 | 3 | import httpx |
4 | 4 | from fastapi import APIRouter, Request, HTTPException, Depends |
5 | | -from fastapi.responses import RedirectResponse |
| 5 | +from fastapi.responses import RedirectResponse, FileResponse |
| 6 | +import os |
6 | 7 |
|
7 | | -from config import get_auth_url, get_token_url, OIDC_CONFIG, sessions |
| 8 | +from config import get_auth_url, get_token_url, OIDC_CONFIG, sessions, STATIC_DIR |
8 | 9 | from dependencies import SessionData, require_auth |
9 | 10 | from coder import CoderAPI |
10 | 11 |
|
11 | 12 | auth_router = APIRouter() |
12 | 13 | coder_api = CoderAPI() |
13 | 14 |
|
14 | 15 | @auth_router.get("/login") |
15 | | -async def login(request: Request, kc_idp_hint: str = None): |
| 16 | +async def login(request: Request, kc_idp_hint: str = None, popup: str = None): |
16 | 17 | session_id = secrets.token_urlsafe(32) |
17 | 18 | auth_url = get_auth_url() |
| 19 | + state = "popup" if popup == "1" else "default" |
18 | 20 | if kc_idp_hint: |
19 | 21 | auth_url = f"{auth_url}&kc_idp_hint={kc_idp_hint}" |
| 22 | + # Add state param to OIDC URL |
| 23 | + auth_url = f"{auth_url}&state={state}" |
20 | 24 | response = RedirectResponse(auth_url) |
21 | 25 | response.set_cookie('session_id', session_id) |
22 | 26 |
|
23 | 27 | return response |
24 | 28 |
|
25 | 29 | @auth_router.get("/callback") |
26 | | -async def callback(request: Request, code: str): |
| 30 | +async def callback(request: Request, code: str, state: str = "default"): |
27 | 31 | session_id = request.cookies.get('session_id') |
28 | 32 | if not session_id: |
29 | 33 | raise HTTPException(status_code=400, detail="No session") |
@@ -53,19 +57,32 @@ async def callback(request: Request, code: str): |
53 | 57 | user_info |
54 | 58 | ) |
55 | 59 | coder_api.ensure_workspace_exists(user_data['username']) |
56 | | - |
57 | | - |
58 | 60 | except Exception as e: |
59 | 61 | print(f"Error in user/workspace setup: {str(e)}") |
60 | 62 | # Continue with login even if Coder API fails |
61 | | - |
62 | | - return RedirectResponse('/') |
| 63 | + |
| 64 | + if state == "popup": |
| 65 | + return FileResponse(os.path.join(STATIC_DIR, "auth/popup-close.html")) |
| 66 | + else: |
| 67 | + return RedirectResponse('/') |
63 | 68 |
|
64 | 69 | @auth_router.get("/logout") |
65 | 70 | async def logout(request: Request): |
66 | 71 | session_id = request.cookies.get('session_id') |
67 | 72 | if session_id in sessions: |
68 | 73 | del sessions[session_id] |
69 | | - response = RedirectResponse('/') |
70 | | - response.delete_cookie('session_id') |
| 74 | + |
| 75 | + # Create a response that doesn't redirect but still clears the cookie |
| 76 | + from fastapi.responses import JSONResponse |
| 77 | + response = JSONResponse({"status": "success", "message": "Logged out successfully"}) |
| 78 | + |
| 79 | + # Clear the session_id cookie with all necessary parameters |
| 80 | + response.delete_cookie( |
| 81 | + key="session_id", |
| 82 | + path="/", |
| 83 | + domain=None, # Use None to match the current domain |
| 84 | + secure=request.url.scheme == "https", |
| 85 | + httponly=True |
| 86 | + ) |
| 87 | + |
71 | 88 | return response |
0 commit comments