This repository was archived by the owner on Jun 3, 2026. It is now read-only.
File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -217,8 +217,9 @@ async def start_connector_oauth(
217217@router .get ("/{connector_id}/oauth/callback" )
218218async def connector_oauth_callback (
219219 connector_id : str ,
220- code : str = Query (..., min_length = 1 ),
221220 state : str = Query (..., min_length = 1 ),
221+ code : Optional [str ] = Query (None , min_length = 1 ),
222+ error : Optional [str ] = Query (None , min_length = 1 ),
222223) -> dict :
223224 connector = _get_connector (connector_id )
224225 now = _now ()
@@ -229,6 +230,11 @@ async def connector_oauth_callback(
229230 status_code = status .HTTP_400_BAD_REQUEST ,
230231 detail = "Invalid or expired connector authorization state" ,
231232 )
233+ if error or not code :
234+ raise HTTPException (
235+ status_code = status .HTTP_400_BAD_REQUEST ,
236+ detail = f"Authorization denied: { error or 'no authorization code received' } " ,
237+ )
232238
233239 # Token exchange, encrypted credential storage, and source ingestion are intentionally
234240 # separate follow-up steps. Do not mark the connector as connected until those exist.
Original file line number Diff line number Diff line change 77from src .api .routes import connectors
88
99
10+ def setup_function () -> None :
11+ connectors ._pending_states .clear ()
12+
13+
1014def _user () -> dict :
1115 return {"id" : "user-1" , "email" : "user@example.com" , "username" : "user" }
1216
@@ -74,3 +78,18 @@ def test_callback_validates_state_without_marking_connected(monkeypatch) -> None
7478 disconnected = client .post ("/api/connectors/notion/disconnect" )
7579 assert disconnected .status_code == 200
7680 assert disconnected .json () == {"connector_id" : "notion" , "disconnected" : False }
81+
82+
83+ def test_callback_handles_provider_denial_and_consumes_state (monkeypatch ) -> None :
84+ monkeypatch .setenv ("NOTION_CLIENT_ID" , "notion-client" )
85+ client = _client ()
86+
87+ started = client .post ("/api/connectors/notion/oauth/start" )
88+ state = started .json ()["state" ]
89+
90+ callback = client .get (f"/api/connectors/notion/oauth/callback?error=access_denied&state={ state } " )
91+
92+ assert callback .status_code == 400
93+ assert "access_denied" in callback .json ()["detail" ]
94+ retry = client .get (f"/api/connectors/notion/oauth/callback?code=abc&state={ state } " )
95+ assert retry .status_code == 400
You can’t perform that action at this time.
0 commit comments