diff --git a/surfsense_backend/app/routes/airtable_add_connector_route.py b/surfsense_backend/app/routes/airtable_add_connector_route.py index 3bcbe4dc0..f1d3a884d 100644 --- a/surfsense_backend/app/routes/airtable_add_connector_route.py +++ b/surfsense_backend/app/routes/airtable_add_connector_route.py @@ -134,8 +134,9 @@ async def connect_airtable(space_id: int, user: User = Depends(current_active_us @router.get("/auth/airtable/connector/callback") async def airtable_callback( request: Request, - code: str, - state: str, + code: str | None = None, + error: str | None = None, + state: str | None = None, session: AsyncSession = Depends(get_async_session), ): """ @@ -143,7 +144,8 @@ async def airtable_callback( Args: request: FastAPI request object - code: Authorization code from Airtable + code: Authorization code from Airtable (if user granted access) + error: Error code from Airtable (if user denied access or error occurred) state: State parameter containing user/space info session: Database session @@ -151,6 +153,39 @@ async def airtable_callback( Redirect response to frontend """ try: + # Handle OAuth errors (e.g., user denied access) + if error: + logger.warning(f"Airtable OAuth error: {error}") + # Try to decode state to get space_id for redirect, but don't fail if it's invalid + space_id = None + if state: + try: + decoded_state = base64.urlsafe_b64decode(state.encode()).decode() + data = json.loads(decoded_state) + space_id = data.get("space_id") + except Exception: + pass # If state is invalid, we'll redirect without space_id + + # Redirect to frontend with error parameter + if space_id: + return RedirectResponse( + url=f"{config.NEXT_FRONTEND_URL}/dashboard/{space_id}/new-chat?modal=connectors&tab=all&error=airtable_oauth_denied" + ) + else: + return RedirectResponse( + url=f"{config.NEXT_FRONTEND_URL}/dashboard?error=airtable_oauth_denied" + ) + + # Validate required parameters for successful flow + if not code: + raise HTTPException( + status_code=400, detail="Missing authorization code" + ) + if not state: + raise HTTPException( + status_code=400, detail="Missing state parameter" + ) + # Decode and parse the state try: decoded_state = base64.urlsafe_b64decode(state.encode()).decode() diff --git a/surfsense_backend/app/routes/linear_add_connector_route.py b/surfsense_backend/app/routes/linear_add_connector_route.py index 2aacd5b3a..88935ef3e 100644 --- a/surfsense_backend/app/routes/linear_add_connector_route.py +++ b/surfsense_backend/app/routes/linear_add_connector_route.py @@ -104,8 +104,9 @@ async def connect_linear(space_id: int, user: User = Depends(current_active_user @router.get("/auth/linear/connector/callback") async def linear_callback( request: Request, - code: str, - state: str, + code: str | None = None, + error: str | None = None, + state: str | None = None, session: AsyncSession = Depends(get_async_session), ): """ @@ -113,7 +114,8 @@ async def linear_callback( Args: request: FastAPI request object - code: Authorization code from Linear + code: Authorization code from Linear (if user granted access) + error: Error code from Linear (if user denied access or error occurred) state: State parameter containing user/space info session: Database session @@ -121,6 +123,39 @@ async def linear_callback( Redirect response to frontend """ try: + # Handle OAuth errors (e.g., user denied access) + if error: + logger.warning(f"Linear OAuth error: {error}") + # Try to decode state to get space_id for redirect, but don't fail if it's invalid + space_id = None + if state: + try: + decoded_state = base64.urlsafe_b64decode(state.encode()).decode() + data = json.loads(decoded_state) + space_id = data.get("space_id") + except Exception: + pass # If state is invalid, we'll redirect without space_id + + # Redirect to frontend with error parameter + if space_id: + return RedirectResponse( + url=f"{config.NEXT_FRONTEND_URL}/dashboard/{space_id}/new-chat?modal=connectors&tab=all&error=linear_oauth_denied" + ) + else: + return RedirectResponse( + url=f"{config.NEXT_FRONTEND_URL}/dashboard?error=linear_oauth_denied" + ) + + # Validate required parameters for successful flow + if not code: + raise HTTPException( + status_code=400, detail="Missing authorization code" + ) + if not state: + raise HTTPException( + status_code=400, detail="Missing state parameter" + ) + # Decode and parse the state try: decoded_state = base64.urlsafe_b64decode(state.encode()).decode() diff --git a/surfsense_backend/app/routes/notion_add_connector_route.py b/surfsense_backend/app/routes/notion_add_connector_route.py index 38c435ff1..c3c81c434 100644 --- a/surfsense_backend/app/routes/notion_add_connector_route.py +++ b/surfsense_backend/app/routes/notion_add_connector_route.py @@ -100,8 +100,9 @@ async def connect_notion(space_id: int, user: User = Depends(current_active_user @router.get("/auth/notion/connector/callback") async def notion_callback( request: Request, - code: str, - state: str, + code: str | None = None, + error: str | None = None, + state: str | None = None, session: AsyncSession = Depends(get_async_session), ): """ @@ -109,7 +110,8 @@ async def notion_callback( Args: request: FastAPI request object - code: Authorization code from Notion + code: Authorization code from Notion (if user granted access) + error: Error code from Notion (if user denied access or error occurred) state: State parameter containing user/space info session: Database session @@ -117,6 +119,39 @@ async def notion_callback( Redirect response to frontend """ try: + # Handle OAuth errors (e.g., user denied access) + if error: + logger.warning(f"Notion OAuth error: {error}") + # Try to decode state to get space_id for redirect, but don't fail if it's invalid + space_id = None + if state: + try: + decoded_state = base64.urlsafe_b64decode(state.encode()).decode() + data = json.loads(decoded_state) + space_id = data.get("space_id") + except Exception: + pass # If state is invalid, we'll redirect without space_id + + # Redirect to frontend with error parameter + if space_id: + return RedirectResponse( + url=f"{config.NEXT_FRONTEND_URL}/dashboard/{space_id}/new-chat?modal=connectors&tab=all&error=notion_oauth_denied" + ) + else: + return RedirectResponse( + url=f"{config.NEXT_FRONTEND_URL}/dashboard?error=notion_oauth_denied" + ) + + # Validate required parameters for successful flow + if not code: + raise HTTPException( + status_code=400, detail="Missing authorization code" + ) + if not state: + raise HTTPException( + status_code=400, detail="Missing state parameter" + ) + # Decode and parse the state try: decoded_state = base64.urlsafe_b64decode(state.encode()).decode()