Skip to content

fix(auth): emit notifyAttachedServices for passwordless and linked-accounts#20358

Merged
vbudhram merged 1 commit intomainfrom
investigate-basket
Apr 13, 2026
Merged

fix(auth): emit notifyAttachedServices for passwordless and linked-accounts#20358
vbudhram merged 1 commit intomainfrom
investigate-basket

Conversation

@vbudhram
Copy link
Copy Markdown
Contributor

@vbudhram vbudhram commented Apr 10, 2026

Because

  • Basket / Braze stopped receiving user records for accounts.firefox.com signups once passwordless OTP was enabled in production for the Settings client
  • Passwordless (/account/passwordless/confirm_code) and Google/Apple third-party auth (/linked_account/login) both created new FxA accounts via db.createAccount without calling log.notifyAttachedServices, so the SNS topic that Basket subscribes to never saw a verified event for these signups

This pull request

  • Adds notifyAttachedServicesForAccountSession helper to routes/utils/account.ts
  • Updates passwordless.ts confirmCode to call the helper, passing deviceCount: 1 for new signups and db.sessions(uid).length for existing-account logins
  • Updates linked-accounts.ts loginOrCreateAccount to track which of its three paths was taken via a linkedAccountFlow flag, then calls the helper with the matching isNewAccount + profileChanged flags (new-account, new-link-on-existing-account, or existing-linked-account re-login)

Issue that this pull request solves

Closes: https://mozilla-hub.atlassian.net/browse/FXA-13416

Checklist

  • My commit is GPG signed.
  • If applicable, I have modified or added tests which pass locally.
  • I have added necessary documentation (if appropriate).
  • I have verified that my changes render correctly in RTL (if appropriate).
  • I have manually reviewed all AI generated code.

Other information

This will be a bit hard to verify until it gets to stage/production

@vbudhram vbudhram requested a review from a team as a code owner April 10, 2026 17:00
Copilot AI review requested due to automatic review settings April 10, 2026 17:00
@vbudhram vbudhram self-assigned this Apr 10, 2026
@vbudhram vbudhram changed the title fix(auth): emit notifyAttachedServices for passwordless and linked-ac… fix(auth): emit notifyAttachedServices for passwordless and linked-accounts Apr 10, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR restores downstream SNS notifications (e.g., Basket/Braze) for account creation/login flows that bypass AccountHandler.createAccount, specifically passwordless OTP and Google/Apple linked-account sign-ins, so new signups again produce the expected verified/login/profileDataChange events.

Changes:

  • Added notifyAttachedServicesForAccountSession helper to centralize emitting verified, login, and profileDataChange SNS events.
  • Updated passwordless OTP confirm flow to emit attached-services notifications and added assertions in Jest coverage.
  • Updated linked-account login/create flow to emit attached-services notifications based on which of the three linked-account paths occurred, with added Jest assertions.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/fxa-auth-server/lib/routes/utils/account.ts Adds a helper to emit attached-services SNS notifications for create/login/profile-change scenarios.
packages/fxa-auth-server/lib/routes/passwordless.ts Calls the new helper during passwordless OTP confirm to emit downstream events.
packages/fxa-auth-server/lib/routes/passwordless.spec.ts Updates module mocking and adds assertions for SNS event emission in passwordless confirm tests.
packages/fxa-auth-server/lib/routes/linked-accounts.ts Tracks linked-account flow path and calls the new helper to emit SNS notifications.
packages/fxa-auth-server/lib/routes/linked-accounts.spec.ts Adds assertions validating SNS event emission for linked-account create/link/re-login cases.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +122 to +134
if (isNewAccount) {
notifications.push(
log.notifyAttachedServices('verified', request, {
country,
countryCode,
email: account.email,
locale: account.locale,
service,
uid: account.uid,
userAgent,
})
);
}
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

notifyAttachedServicesForAccountSession claims to mirror AccountHandler.createAccount, but it emits the verified notification based solely on isNewAccount. In AccountHandler.createAccount, the verified event is gated by account.emailVerified (so new-but-unverified accounts do not emit verified). Consider adding an explicit emailVerified/shouldEmitVerified option (or passing it via account) and using that to decide whether to emit verified, to keep behavior aligned and prevent future misuse of this helper in unverified-signup flows.

Copilot uses AI. Check for mistakes.
const isNewAccount = linkedAccountFlow === 'new-account';
const deviceCount = isNewAccount
? 1
: (await this.db.sessions(accountRecord.uid)).length;
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For existing-account third-party logins, deviceCount is computed via await this.db.sessions(accountRecord.uid) before the new session token is created later in this handler. This will typically undercount by 1 compared to other login flows (e.g. sendSigninNotifications in routes/utils/signin.js, which counts sessions after creating the session). Consider moving the notifyAttachedServicesForAccountSession call to after createSessionToken, or adjusting deviceCount to include the imminent session so downstream device/session tracking isn't skewed.

Suggested change
: (await this.db.sessions(accountRecord.uid)).length;
: (await this.db.sessions(accountRecord.uid)).length + 1;

Copilot uses AI. Check for mistakes.
@vbudhram vbudhram force-pushed the investigate-basket branch from d94ec18 to 9e6e01b Compare April 10, 2026 17:46
// accounts for the session token created below, matching the post-
// createSessionToken count in signin.js sendSigninNotifications.
const isNewAccount = linkedAccountFlow === 'new-account';
const deviceCount = isNewAccount
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would moving the notification after the session token creation avoid the need for this calculation?


// Should emit SNS verified + login + profileDataChange events so
// Basket/Braze learn about the new passwordless account. Missing
// these calls was the root cause of the basket regression.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Link ticket number about regression?

});

// Mirror the SNS notifications that AccountHandler.createAccount
// fires, since passwordless bypasses that path. The +1 on existing
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't the session count be accurate already (+1 unnecessary), since the token was created above?

…count flows

Because:
- Basket/Braze stopped receiving user records for accounts.firefox.com
  signups once passwordless OTP was enabled in production
- Passwordless and Google/Apple third-party auth both created new FxA
  accounts via db.createAccount without calling log.notifyAttachedServices,
  so the SNS topic that Basket subscribes to never saw a verified event

This commit:
- Adds notifyAttachedServicesForAccountSession helper to routes/utils/account.ts
- Calls the helper from passwordless confirmCode and linked-accounts
  loginOrCreateAccount so downstream subscribers receive verified, login,
  and profileDataChange events
- Places notification after createSessionToken so db.sessions reflects the
  actual session count without needing a +1 adjustment
- Adds CORS credentials support for passwordless endpoints
- Adds isResend/isNewAccount tags to passwordless statsd metrics

Fixes FXA-13416
@vbudhram vbudhram force-pushed the investigate-basket branch from 9e6e01b to 8571684 Compare April 13, 2026 17:48
@vbudhram vbudhram merged commit 2901752 into main Apr 13, 2026
20 checks passed
@vbudhram vbudhram deleted the investigate-basket branch April 13, 2026 18:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants