Hi @Eugene.Kriuchkov
Welcome to the Auth0 Community!
You are evaluating whether to migrate from Log Streams to Event Streams for keeping MFA state synchronized with Auth0. You have observed that Event Streams surface MFA changes only as generic user.updated events, requiring you to query the Management API to determine actual MFA state, while Log Streams provide specific MFA event codes like gd_enrollment_complete and gd_unenroll.
Auth0 recommends Event Streams for user lifecycle synchronization, but for production systems that need to keep MFA state synchronized, Log Streams are better suited because they provide granular, MFA-specific event codes that directly indicate enrollment, unenrollment, and authentication changes. Event Streams are state-based and abstract away the specific trigger, which requires you to query the Management API to determine what actually changed. For MFA synchronization, Log Streams provide more actionable events without additional API calls.
Root Cause:
Event Streams and Log Streams serve different purposes:
- Event Streams — State-based notifications focused on user lifecycle (created, updated, deleted). They abstract away the specific trigger and focus on the fact that the user profile changed. This requires you to query the Management API to determine what changed.
- Log Streams — Trigger-based detailed audit logs that capture specific events, including granular MFA events like enrollment, unenrollment, and authentication success/failure. These events directly indicate what happened without requiring additional API calls.
For MFA synchronization specifically, Log Streams provide:
gd_enrollment_complete — MFA enrollment completed
gd_start_enroll — MFA enrollment started
gd_unenroll — MFA device unenrolled
gd_update_device_account — MFA device updated
gd_auth_succeed — MFA authentication success
gd_auth_failed — MFA authentication failed
Solution: Continue Using Log Streams for MFA Synchronization
Recommendation:
For a production system that needs to keep MFA state synchronized with Auth0, continue using Log Streams with your webhook. Log Streams are purpose-built for this use case because they:
- Provide specific MFA event codes — You can directly react to
gd_enrollment_complete, gd_unenroll, and other MFA-specific events without querying the Management API
- Eliminate unnecessary API calls — You know exactly what changed from the event code alone
- Provide audit trail — All MFA changes are logged with timestamps and details
- Are designed for monitoring and analytics — Which aligns with your synchronization needs
Why Event Streams Are Not Ideal for MFA Synchronization:
- Generic
user.updated events — You cannot determine what changed without querying the Management API
- Additional latency — You must make a Management API call to fetch current MFA methods after each event
- Potential race conditions — Between receiving the event and querying the API, the state may change again
- Designed for different use case — Event Streams are optimized for user lifecycle (provisioning/deprovisioning), not detailed state tracking
If You Must Use Event Streams:
If you decide to migrate to Event Streams for other reasons, the pattern would be:
- Subscribe to
user.updated events
- On each event, call the Management API to fetch the current user's authentication methods:
GET /api/v2/users/{user_id}/authentication-methods
- Compare the returned methods with your local cache to determine what changed
- Update your system based on the differences
This approach works but requires additional API calls and introduces latency compared to Log Streams.
Best Practice:
For MFA synchronization in production:
- Continue using Log Streams with your webhook
- Subscribe to these MFA-specific event codes:
gd_enrollment_complete — User enrolled in MFA
gd_start_enroll — User started MFA enrollment
gd_unenroll — User unenrolled from MFA
gd_update_device_account — User updated MFA device
gd_auth_succeed — MFA authentication succeeded
gd_auth_failed — MFA authentication failed
- React directly to these events without querying the Management API
- Use Event Streams separately if you need general user lifecycle notifications (for provisioning/deprovisioning)
Example Log Stream Webhook Handler:
app.post('/webhook', (req, res) => {
const event = req.body;
// React to specific MFA events
if (event.type === 'gd_enrollment_complete') {
// User enrolled in MFA - update your system
updateUserMfaStatus(event.user_id, 'enrolled');
} else if (event.type === 'gd_unenroll') {
// User unenrolled from MFA - update your system
updateUserMfaStatus(event.user_id, 'unenrolled');
} else if (event.type === 'gd_auth_succeed') {
// MFA authentication succeeded - log or track
logMfaSuccess(event.user_id);
}
res.status(200).send('OK');
});
Migration Path (If Needed):
If you need to migrate to Event Streams in the future:
- Keep Log Streams active during transition
- Implement Event Streams handler that queries Management API for MFA methods
- Validate that both systems produce the same results
- Once validated, deprecate Log Streams
For MFA state synchronization in production, Log Streams are the recommended approach because they provide specific, actionable events without requiring additional API calls.
Kind Regards,
Nik