1
+ import json
1
2
import logging
2
3
from typing import Any , Dict , List , Optional
3
4
@@ -117,6 +118,10 @@ async def get_initiate_auth_url(
117
118
if app_redirect_uri :
118
119
request .session ["app_redirect_uri" ] = app_redirect_uri
119
120
121
+ # Store AuthContext for the callback as a JSON string
122
+ auth_context_json_str = service .auth_context .model_dump_json () # Use .model_dump_json()
123
+ request .session ["oauth_auth_context_json" ] = auth_context_json_str # Store as JSON string
124
+
120
125
logger .info ("Prepared auth URL for '%s' for user '%s'." , connector_type , service .user_identifier )
121
126
122
127
return {"authorization_url" : authorization_url }
@@ -141,7 +146,6 @@ async def connector_oauth_callback(
141
146
state : Optional [str ] = None , # State from query parameters
142
147
error : Optional [str ] = None , # Optional error from OAuth provider
143
148
error_description : Optional [str ] = None , # Optional error description
144
- service : ConnectorService = Depends (get_connector_service ),
145
149
):
146
150
"""
147
151
Handles the OAuth 2.0 callback from the authentication provider.
@@ -154,18 +158,18 @@ async def connector_oauth_callback(
154
158
155
159
if error :
156
160
logger .error (f"OAuth provider returned error for '{ connector_type } ': { error } - { error_description } " )
157
- # You might want to redirect to a frontend error page here
158
161
raise HTTPException (status_code = 400 , detail = f"OAuth provider error: { error_description or error } " )
159
162
163
+ # --- Session Data Retrieval and Validation ---
160
164
stored_state = request .session .pop ("oauth_state" , None )
161
165
stored_connector_type = request .session .pop ("connector_type_for_callback" , None )
166
+ auth_context_json_str = request .session .pop ("oauth_auth_context_json" , None )
162
167
163
168
if not stored_state or not state or stored_state != state :
164
169
logger .error (
165
170
f"OAuth state mismatch for '{ connector_type } '. Expected: '{ stored_state } ', "
166
171
f"Received: '{ state } '. IP: { request .client .host if request .client else 'unknown' } "
167
172
)
168
- # Redirect to an error page or show a generic error
169
173
raise HTTPException (status_code = 400 , detail = "Invalid OAuth state. Authentication failed." )
170
174
171
175
if not stored_connector_type or stored_connector_type != connector_type :
@@ -174,33 +178,47 @@ async def connector_oauth_callback(
174
178
)
175
179
raise HTTPException (status_code = 400 , detail = "Connector type mismatch during OAuth callback." )
176
180
181
+ if not auth_context_json_str :
182
+ logger .error (f"AuthContext not found in session during OAuth callback for '{ connector_type } '." )
183
+ raise HTTPException (status_code = 400 , detail = "Authentication context missing. Please restart the auth flow." )
184
+
185
+ # --- Service and Connector Initialization ---
186
+ try :
187
+ auth_context = AuthContext (** json .loads (auth_context_json_str ))
188
+ service = ConnectorService (auth_context = auth_context )
189
+ # connector variable will be defined by calling service.get_connector
190
+ # but we need to ensure it's done within a try block that can handle connector-specific errors.
191
+ except Exception as e : # Covers AuthContext reconstruction and ConnectorService instantiation
192
+ logger .error (f"Failed to reconstruct AuthContext or instantiate ConnectorService: { str (e )} " )
193
+ raise HTTPException (status_code = 500 , detail = "Internal server error during authentication setup." )
194
+
195
+ # --- Code Validation (can now use service.user_identifier if needed in logs) ---
177
196
if not code :
178
- logger .error (f"Authorization code not found in OAuth callback for '{ connector_type } '." )
197
+ user_id_for_log = service .user_identifier if "service" in locals () else "unknown user"
198
+ logger .error (
199
+ f"Authorization code not found in OAuth callback for '{ connector_type } ' for user '{ user_id_for_log } '."
200
+ )
179
201
raise HTTPException (status_code = 400 , detail = "Authorization code missing from provider callback." )
180
202
181
203
# Reconstruct the full authorization response URL that the provider redirected to.
182
- # The `google-auth-oauthlib` flow.fetch_token expects the full URL.
183
204
authorization_response_url = str (request .url )
184
205
logger .debug (f"Full authorization_response_url for '{ connector_type } ': { authorization_response_url } " )
185
206
186
207
try :
208
+ # Now get the connector, as service is initialized
187
209
connector = await service .get_connector (connector_type )
188
210
189
- # The `finalize_auth` method expects a dictionary with the full response URL and the validated state.
190
211
auth_data = {
191
212
"authorization_response_url" : authorization_response_url ,
192
- "state" : state , # Pass the received (and validated) state to the connector
213
+ "state" : state ,
193
214
}
194
215
195
- success = await connector .finalize_auth (auth_data )
216
+ success = await connector .finalize_auth (auth_data ) # Correctly call on connector
196
217
197
218
if success :
198
219
logger .info (
199
220
f"Successfully finalized authentication for '{ connector_type } ' for user '{ service .user_identifier } '."
200
221
)
201
- # In a real app, redirect to a frontend page indicating success
202
- # For example: return RedirectResponse(url="/profile?auth_success="+connector_type)
203
- # Redirect to the frontend connections page with a success indicator
204
222
app_redirect_uri = request .session .pop ("app_redirect_uri" , None )
205
223
if app_redirect_uri :
206
224
logger .info (f"Redirecting to frontend app_redirect_uri: { app_redirect_uri } " )
@@ -219,18 +237,18 @@ async def connector_oauth_callback(
219
237
f"Failed to finalize auth for '{ connector_type } ' with user '{ service .user_identifier } ' "
220
238
f"(connector returned False)."
221
239
)
222
- # Redirect to a frontend error page
223
240
raise HTTPException (status_code = 500 , detail = "Failed to finalize authentication with the provider." )
224
241
225
- except ValueError as ve : # Raised by get_connector or if connector has issues not caught by its finalize_auth
242
+ except ValueError as ve :
226
243
logger .error (f"Error during OAuth callback for '{ connector_type } ' for user '{ service .user_identifier } ': { ve } " )
227
244
raise HTTPException (status_code = 400 , detail = str (ve ))
228
245
except NotImplementedError :
229
246
logger .error (f"Connector '{ connector_type } ' is not fully implemented for auth finalization." )
230
247
raise HTTPException (status_code = 501 , detail = f"Connector '{ connector_type } ' not fully implemented." )
231
248
except Exception as e :
249
+ user_id_for_log = service .user_identifier if "service" in locals () else "unknown user"
232
250
logger .exception (
233
- f"Unexpected error during OAuth callback for '{ connector_type } ' for user '{ service . user_identifier } ': { e } "
251
+ f"Unexpected error during OAuth callback for '{ connector_type } ' for user '{ user_id_for_log } ': { e } "
234
252
)
235
253
raise HTTPException (status_code = 500 , detail = "Internal server error during authentication callback." )
236
254
0 commit comments