@@ -403,11 +403,10 @@ def _parse_auth_timestamp(value: _typing.Any) -> _dt.datetime | None:
403403 except (OverflowError , OSError , ValueError ):
404404 return None
405405 if isinstance (value , str ) and value :
406- for parser in (_util .timestamp_conversion , _dt .datetime .fromisoformat ):
407- try :
408- return _ensure_utc (parser (value ))
409- except ValueError :
410- continue
406+ try :
407+ return _ensure_utc (_util .timestamp_conversion (value ))
408+ except ValueError :
409+ return None
411410 return None
412411
413412
@@ -442,10 +441,24 @@ def _auth_user_metadata_from_ce_data(meta_data: dict[str, _typing.Any] | None):
442441
443442 # Production Eventarc sends 'createTime'; Auth Emulator sends 'creationTime'.
444443 raw_creation = meta_data .get ("createTime" ) or meta_data .get ("creationTime" )
444+ if raw_creation not in (None , "" ):
445+ creation_time = _parse_auth_timestamp (raw_creation )
446+ if creation_time is None :
447+ raise ValueError (f"Invalid Auth metadata creationTime: { raw_creation !r} " )
448+ else :
449+ creation_time = _EPOCH_START_UTC
450+
445451 raw_last_sign_in = meta_data .get ("lastSignInTime" )
452+ if raw_last_sign_in not in (None , "" ):
453+ last_sign_in_time = _parse_auth_timestamp (raw_last_sign_in )
454+ if last_sign_in_time is None :
455+ raise ValueError (f"Invalid Auth metadata lastSignInTime: { raw_last_sign_in !r} " )
456+ else :
457+ last_sign_in_time = None
458+
446459 return AuthUserMetadata (
447- creation_time = _parse_auth_timestamp ( raw_creation ) or _EPOCH_START_UTC ,
448- last_sign_in_time = _parse_auth_timestamp ( raw_last_sign_in ) ,
460+ creation_time = creation_time ,
461+ last_sign_in_time = last_sign_in_time ,
449462 )
450463
451464
@@ -457,13 +470,11 @@ def _unwrap_auth_event_data(raw_data: _typing.Any) -> dict[str, _typing.Any]:
457470 if not raw_data or not isinstance (raw_data , dict ):
458471 raise ValueError ("Auth event data payload must be a non-empty dictionary." )
459472 # 'on_user_created' wraps the user in 'value'; 'on_user_deleted' wraps it in 'oldValue'.
460- # Check isinstance(..., dict) so {'value': None, 'oldValue': {...}} does not shadow 'oldValue'.
461- for envelope_key in ("value" , "oldValue" ):
462- if isinstance (raw_data .get (envelope_key ), dict ) and raw_data [envelope_key ]:
463- return raw_data [envelope_key ]
464- # If either envelope key is present with a non-dict (e.g. None) or empty dict, reject it.
465- if any (k in raw_data for k in ("value" , "oldValue" )):
466- raise ValueError ("Auth event data envelope contains an empty or null user record." )
473+ if "value" in raw_data or "oldValue" in raw_data :
474+ user_data = raw_data .get ("value" ) or raw_data .get ("oldValue" )
475+ if not isinstance (user_data , dict ) or not user_data :
476+ raise ValueError ("Auth event data envelope contains an empty or null user record." )
477+ return user_data
467478 # Otherwise, assume raw_data is already the top-level user dict (e.g. synthetic tests).
468479 return raw_data
469480
@@ -529,12 +540,9 @@ def _auth_event_from_ce(raw: _ce.CloudEvent):
529540
530541 # Extract project ID from the CloudEvent source attribute
531542 # (e.g. "//identitytoolkit.googleapis.com/projects/my-project").
532- source = event_attributes .get ("source" , "" )
533- project : str | None = None
534- if source :
535- match = _PROJECT_ID_REGEX .search (source )
536- if match :
537- project = match .group (1 )
543+ source : str = event_attributes ["source" ]
544+ match = _PROJECT_ID_REGEX .search (source )
545+ project : str | None = match .group (1 ) if match else None
538546
539547 raw_time = event_attributes .get ("time" )
540548 event_time = _parse_auth_timestamp (raw_time )
0 commit comments