Skip to content

Commit eed42eb

Browse files
debug logs for adfs
1 parent 3e2a737 commit eed42eb

2 files changed

Lines changed: 132 additions & 3 deletions

File tree

wavefront/server/modules/user_management_module/user_management_module/controllers/auth_plugin_controller.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,13 +258,18 @@ async def init_oauth_flow(
258258
"""Initialize OAuth flow and return authorization URL."""
259259

260260
try:
261+
logger.debug('OAuth init requested for auth_id=%s', oauth_request.auth_id)
261262
# Get authenticator instance by ID
262263
auth_id = UUID(oauth_request.auth_id)
263264
authenticator = await get_authenticator_instance(
264265
auth_id, authenticator_repository
265266
)
266267

267268
if not authenticator:
269+
logger.debug(
270+
'OAuth init: no enabled authenticator for auth_id=%s',
271+
oauth_request.auth_id,
272+
)
268273
return JSONResponse(
269274
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
270275
content=response_formatter.buildErrorResponse(
@@ -275,6 +280,13 @@ async def init_oauth_flow(
275280
# Mint opaque CSRF state + OIDC nonce, persist server-side, and pass
276281
# both into the provider so they end up in the authorize URL.
277282
state, nonce = _store_oauth_flow(cache_manager, oauth_request.auth_id)
283+
logger.debug(
284+
'OAuth flow stored: auth_id=%s state=%s nonce=%s ttl=%ss',
285+
oauth_request.auth_id,
286+
state,
287+
nonce,
288+
OAUTH_FLOW_TTL_SECONDS,
289+
)
278290
auth_url = authenticator.get_authorization_url(state, nonce=nonce)
279291

280292
if not auth_url:
@@ -325,6 +337,13 @@ async def google_oauth_callback(
325337
token_service: TokenService = Depends(Provide[AuthContainer.token_service]),
326338
):
327339
"""Handle Google OAuth callback."""
340+
logger.debug(
341+
'Google OAuth callback: has_code=%s has_state=%s has_error=%s state=%s',
342+
bool(code),
343+
bool(state),
344+
bool(error),
345+
state,
346+
)
328347
flow = _consume_oauth_flow(cache_manager, state)
329348
if flow is None:
330349
logger.warning('Google OAuth callback received unknown/expired state')
@@ -371,6 +390,13 @@ async def microsoft_oauth_callback(
371390
token_service: TokenService = Depends(Provide[AuthContainer.token_service]),
372391
):
373392
"""Handle Microsoft OAuth callback."""
393+
logger.debug(
394+
'Microsoft OAuth callback: has_code=%s has_state=%s has_error=%s state=%s',
395+
bool(code),
396+
bool(state),
397+
bool(error),
398+
state,
399+
)
374400
flow = _consume_oauth_flow(cache_manager, state)
375401
if flow is None:
376402
logger.warning('Microsoft OAuth callback received unknown/expired state')
@@ -415,6 +441,13 @@ async def microsoft_adfs_oauth_callback(
415441
token_service: TokenService = Depends(Provide[AuthContainer.token_service]),
416442
):
417443
"""Handle Microsoft ADFS OAuth callback."""
444+
logger.debug(
445+
'Microsoft ADFS callback: has_code=%s has_state=%s has_error=%s state=%s',
446+
bool(code),
447+
bool(state),
448+
bool(error),
449+
state,
450+
)
418451
flow = _consume_oauth_flow(cache_manager, state)
419452
if flow is None:
420453
logger.warning('Microsoft ADFS callback received unknown/expired state')
@@ -451,6 +484,14 @@ async def _handle_oauth_callback(
451484
"""Common OAuth callback handler."""
452485

453486
try:
487+
logger.debug(
488+
'_handle_oauth_callback: auth_id=%s has_code=%s has_error=%s '
489+
'expected_nonce_set=%s',
490+
auth_id,
491+
bool(callback_data.get('authorization_code')),
492+
bool(callback_data.get('error')),
493+
expected_nonce is not None,
494+
)
454495
# Get authenticator instance and config
455496
auth_uuid = UUID(auth_id)
456497
authenticator, config_data = await get_authenticator_with_config(
@@ -481,6 +522,12 @@ def get_failure_redirect(error_msg: str) -> RedirectResponse:
481522
provider = config_data.get('auth_type')
482523
success_url = config_data.get('config', {}).get('client_redirect_success_url')
483524
failure_url = config_data.get('config', {}).get('client_redirect_failure_url')
525+
logger.debug(
526+
'_handle_oauth_callback: provider=%s success_url=%s failure_url=%s',
527+
provider,
528+
success_url,
529+
failure_url,
530+
)
484531

485532
# Handle OAuth error from provider
486533
if callback_data.get('error'):
@@ -498,6 +545,13 @@ def get_failure_redirect(error_msg: str) -> RedirectResponse:
498545
auth_result = authenticator.handle_callback(
499546
callback_data, expected_nonce=expected_nonce
500547
)
548+
logger.debug(
549+
'_handle_oauth_callback: provider auth_result success=%s error_code=%s '
550+
'email=%s',
551+
auth_result.success,
552+
auth_result.error_code,
553+
auth_result.user_info.email if auth_result.user_info else None,
554+
)
501555

502556
if not auth_result.success:
503557
if failure_url:
@@ -512,6 +566,12 @@ def get_failure_redirect(error_msg: str) -> RedirectResponse:
512566

513567
# Create session from auth result
514568
user = await user_repository.find_one(email=auth_result.user_info.email)
569+
logger.debug(
570+
'_handle_oauth_callback: user lookup by email=%s found=%s deleted=%s',
571+
auth_result.user_info.email,
572+
user is not None,
573+
getattr(user, 'deleted', None),
574+
)
515575
if user is None:
516576
if failure_url:
517577
params = urlencode(
@@ -552,6 +612,11 @@ def get_failure_redirect(error_msg: str) -> RedirectResponse:
552612
role_id = await user_service.get_user_role_for_scope(
553613
user_id=str(user.id), scope=ResourceScope.CONSOLE
554614
)
615+
logger.debug(
616+
'_handle_oauth_callback: console role lookup user_id=%s role_id=%s',
617+
str(user.id),
618+
role_id,
619+
)
555620

556621
if not role_id:
557622
if failure_url:
@@ -572,12 +637,24 @@ def get_failure_redirect(error_msg: str) -> RedirectResponse:
572637

573638
# Success: redirect to success URL with access token
574639
if success_url:
640+
logger.debug(
641+
'_handle_oauth_callback: success redirect provider=%s user_id=%s '
642+
'session_id=%s -> %s',
643+
provider,
644+
str(user.id),
645+
str(session.id),
646+
success_url,
647+
)
575648
params = urlencode({'provider': provider, 'access_token': token})
576649
return RedirectResponse(url=f'{success_url}?{params}')
577650

651+
logger.debug(
652+
'_handle_oauth_callback: no success_url configured, redirecting to about:blank'
653+
)
578654
return RedirectResponse(url='about:blank')
579655

580656
except Exception as e:
657+
logger.debug('_handle_oauth_callback raised: %s', e)
581658
# Try to get config for failure URL
582659
try:
583660
auth_uuid = UUID(auth_id)

wavefront/server/plugins/authenticator/authenticator/microsoft_adfs/authenticator.py

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,26 @@ def get_authorization_url(
174174
if nonce:
175175
params['nonce'] = nonce
176176

177-
return f'{self.auth_url}?{urlencode(params)}'
177+
url = f'{self.auth_url}?{urlencode(params)}'
178+
logger.debug(
179+
'ADFS authorize URL built (state_set=%s nonce_set=%s): %s',
180+
bool(state),
181+
bool(nonce),
182+
url,
183+
)
184+
return url
178185

179186
def handle_callback(
180187
self, callback_data: Dict[str, Any], expected_nonce: Optional[str] = None
181188
) -> AuthResult:
189+
logger.debug(
190+
'ADFS handle_callback: has_code=%s has_state=%s has_error=%s '
191+
'expected_nonce_set=%s',
192+
bool(callback_data.get('authorization_code')),
193+
bool(callback_data.get('state')),
194+
bool(callback_data.get('error')),
195+
expected_nonce is not None,
196+
)
182197
return self.authenticate(callback_data, expected_nonce=expected_nonce)
183198

184199
def refresh_token(self, refresh_token: str) -> TokenResult:
@@ -268,6 +283,8 @@ def _exchange_code_for_token(
268283
'scope': ' '.join(self.config.scopes),
269284
}
270285

286+
logger.debug('ADFS token exchange: POST %s', self.token_url)
287+
271288
try:
272289
response = requests.post(
273290
self.token_url,
@@ -278,22 +295,36 @@ def _exchange_code_for_token(
278295
response.raise_for_status()
279296
token_data = response.json()
280297

298+
id_token = token_data.get('id_token')
299+
logger.debug(
300+
'ADFS token exchange response: status=%d has_access_token=%s '
301+
'has_id_token=%s has_refresh_token=%s expires_in=%s',
302+
response.status_code,
303+
bool(token_data.get('access_token')),
304+
bool(id_token),
305+
bool(token_data.get('refresh_token')),
306+
token_data.get('expires_in'),
307+
)
308+
logger.debug('ADFS id_token=%s', id_token)
309+
281310
return (
282311
TokenResult(
283312
success=True,
284313
access_token=token_data.get('access_token'),
285314
refresh_token=token_data.get('refresh_token'),
286315
expires_in=token_data.get('expires_in'),
287316
),
288-
token_data.get('id_token'),
317+
id_token,
289318
)
290319

291320
except requests.exceptions.RequestException as e:
321+
logger.debug('ADFS token exchange request failed: %s', e)
292322
return (
293323
TokenResult(success=False, error=f'Token exchange failed: {str(e)}'),
294324
None,
295325
)
296-
except json.JSONDecodeError:
326+
except json.JSONDecodeError as e:
327+
logger.debug('ADFS token endpoint returned non-JSON: %s', e)
297328
return (
298329
TokenResult(
299330
success=False, error='Invalid response from ADFS token endpoint'
@@ -306,16 +337,30 @@ def _get_user_info_from_id_token(
306337
) -> Optional[UserInfo]:
307338
claims = self._decode_id_token_claims(id_token, expected_nonce=expected_nonce)
308339
if not claims:
340+
logger.debug('ADFS user_info: no claims (decode/validate failed)')
309341
return None
310342

311343
email = claims.get('email') or claims.get('upn') or claims.get('unique_name')
312344
if not email:
345+
logger.debug(
346+
'ADFS user_info: no email/upn/unique_name claim present in id_token'
347+
)
313348
return None
314349

315350
first_name = claims.get('given_name')
316351
if not first_name and '@' in email:
317352
first_name = email.split('@')[0]
318353

354+
logger.debug(
355+
'ADFS user_info resolved: email=%s (source=%s) given_name=%s family_name=%s',
356+
email,
357+
'email'
358+
if claims.get('email')
359+
else ('upn' if claims.get('upn') else 'unique_name'),
360+
first_name,
361+
claims.get('family_name'),
362+
)
363+
319364
return UserInfo(
320365
email=email,
321366
first_name=first_name,
@@ -340,6 +385,10 @@ def _decode_id_token_claims(
340385
# that legitimately differs from the configured `authority`.
341386
try:
342387
signing_key = self._jwks_client.get_signing_key_from_jwt(id_token)
388+
logger.debug(
389+
'ADFS JWKS signing key obtained (kid=%s)',
390+
getattr(signing_key, 'key_id', None),
391+
)
343392
decode_kwargs: Dict[str, Any] = {
344393
'audience': self.config.client_id,
345394
'leeway': self.config.clock_skew_seconds,
@@ -356,10 +405,13 @@ def _decode_id_token_claims(
356405
decode_kwargs['issuer'] = self.config.expected_issuer
357406

358407
claims = jwt.decode(id_token, signing_key.key, **decode_kwargs)
408+
logger.debug('ADFS id_token claims decoded: %s', claims)
359409

360410
if expected_nonce is not None and claims.get('nonce') != expected_nonce:
361411
logger.warning('ADFS id_token nonce mismatch')
362412
return None
413+
if expected_nonce is not None:
414+
logger.debug('ADFS id_token nonce matched expected value')
363415

364416
return claims
365417
except jwt.PyJWTError as e:

0 commit comments

Comments
 (0)