Skip to content

fix(auth): Use verifiedSessionToken on sign out - #20974

Open
vbudhram wants to merge 1 commit into
mainfrom
fxa-14033
Open

fix(auth): Use verifiedSessionToken on sign out #20974
vbudhram wants to merge 1 commit into
mainfrom
fxa-14033

Conversation

@vbudhram

@vbudhram vbudhram commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Because

  • Make POST /account/device/destroy require a verifiedSessionToken

This pull request

  • Gates the route in devices-and-sessions.js on verifiedSessionTokenBearer and verifiedSessionToken, matching the sibling /account/attached_client/destroy, and keeps refreshToken so the AAL checks never apply to mobile.

Issue that this pull request solves

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

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

@vbudhram
vbudhram requested a review from a team as a code owner August 6, 2026 11:48
@vbudhram vbudhram self-assigned this Aug 6, 2026
Because:

* `POST /account/device/destroy` accepted a bare `sessionToken`, so a
  password-only attacker holding an unverified AAL1 session could
  enumerate deviceIds via `GET /account/devices` and disconnect the
  victim's AAL2 devices, cascading to their sessionTokens and
  device-bound OAuth refresh tokens, without proving the second factor.
* No first-party client presents a sessionToken to this route. Firefox
  Desktop never calls it, signing out via `/session/destroy` and
  `/oauth/destroy` instead; mobile reaches it with a refreshToken from
  application-services; `fxa-settings` uses
  `/account/attached_client/destroy`.

This commit:

* Gates the route on `verifiedSessionTokenBearer` and
  `verifiedSessionToken`, matching the sibling
  `/account/attached_client/destroy`, and keeps `refreshToken` so the
  AAL checks never apply to mobile.
* Pins the strategy chain's fall-through contract. This is the first
  route to put `verifiedSessionToken` ahead of `refreshToken`, and Hapi
  only advances to the next strategy when the error carries `isMissing`.
  Mobile sends a plain `Bearer <hex>`, which the Hawk-backed strategy
  rejects via `Boom.unauthorized(null, 'Hawk')`; losing that flag would
  401 every mobile disconnect.
* Asserts the strategy list on the route, following the existing
  `/account/attached_client/destroy` test.
* Drops the incidental `destroyDevice` teardown from two remote device
  registration tests and adds one asserting that an unconfirmed account
  is now rejected with errno 104. Inheriting the strategy's
  email-verified check is wider than the finding requires, but it
  matches the already-shipped sibling route and no first-party client
  reaches this path with a sessionToken.

Leaves `GET /account/devices` unchanged: Sync clients need the device
list on AAL1 sessions, and it is same-account read-only disclosure to
someone who already has the password.

@LZoog LZoog left a comment

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.

LGTM, but I'd double check that clients uses the refresh token for this and not session token. Since Desktop drops it, is there a scenario where a user signs into Firefox on Desktop device A, then on another device B sets up TOTP, and then on device A could there be an issue when trying to sign out / destroy that session/device?

expect(devices[0].pushEndpointExpired).toBe(false);
});

it('device disconnect fails on an unconfirmed account', async () => {

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.

Should we have a test for an account with TOTP and only entering the password?

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.

2 participants