Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix loss of cipher data when using pass generator #1935

Merged
merged 1 commit into from
Jul 7, 2021

Conversation

eliykat
Copy link
Member

@eliykat eliykat commented Jul 6, 2021

Objective

Note: this may be a candidate for rc

Fix #1927. The latest update broke the password generator when accessed via the add-edit component. Maybe 80%, when returning from the password generator to the add-edit component, it would blank all the fields - so you would lose anything you entered, including the password you just generated. The other 20% of the time it would work as expected.

This mainly affected Firefox for me, I don't think I was able to reproduce it on Chrome.

Cause

There are 3 (relevant) trigger events for the Angular router to start navigation:

  • imperative - e.g. calling router.navigate
  • popstate - usually triggered by going forward or back through the browser history, e.g. location.back()
  • hashchange - triggered when the hash component of the URL changes (only when using the hash navigation strategy, which we do)

There appears to be a bug where:

  • we call location.back() from the password generator
  • that emits a popstate event which navigates back to the add-edit page
  • the add-edit page uses stateService to retrieve the cipher information (so far so good)
  • the popstate event changes the URL hash, which triggers hashchange
  • the router navigates to the add-edit page again - but because we're already there, it just re-initializes the component
  • by now, stateService has been cleared, so it's treated like a brand new cipher and we lose our data

However, this is a race condition, which is why it doesn't trigger every time. Sometimes, the hashchange event will fire quick enough that it interrupts and aborts the popstate navigation. I can't see any clear code changes that introduced this bug, so I'm thinking it's a combination of Angular 11 upgrade + other changes that have thrown out the race condition.

This has ostensibly already been reported and fixed here. However, that fix relies on the id of the two navigation events being identical, whereas in this case it's incremented by 1. This means the logic of that fix doesn't catch it. If others agree with this assessment, then I'm happy to try to do a minimal reproduction and open an issue.

Code changes

As it is, we have to work around it for now. This PR adds DebounceNavigationService, which detects the duplicate navigation and will cancel the second hashchange navigation if (but only if) the first popstate navigation has successfully completed.

It can be used on any route where this behaviour is causing an issue. It also requires the runGuardsAndResolvers: 'always' option so that it will be run even though the route is already active.

This has the advantage of being entirely local to browser and (hopefully) minimal risk of other regressions.

This double navigation occurs wherever location.back() is used. However, it doesn't cause any other issues that I can see, so I've left the other components alone even if they are technically affected.

Other options

Don't like this? We could also:

  • delete onSameUrlNavigation: 'reload' in app-routing.module.ts. That would revert to the default behaviour where you can't navigate to the same route twice. However, it might cause other regressions - I assume it's there for a reason.
  • just put await this.stateService.remove('addEditCipherInfo') on a 500ms timeout in the cipher add-edit component. That way, the state information would still be there for the duplicate navigation. However, this isn't really fixing the race condition, it's just trying to make it more predictable. This didn't feel like the right solution. However, if this is preferred, I did a quick mock-up and pushed it to remote branches called fix-cipher-state on browser and jslib.

@eliykat eliykat requested a review from a team July 6, 2021 09:47
Copy link
Contributor

@cscharf cscharf left a comment

Choose a reason for hiding this comment

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

Looks good, queued to @bitwarden/dept-qa for testing before merging.

@cscharf cscharf added the hold do not merge, do not approve yet label Jul 6, 2021
@kendratodd kendratodd assigned eliykat and unassigned kendratodd Jul 6, 2021
@kendratodd
Copy link

@eliykat, I've logged my test findings in our internal ticket, this one looks good - reassigning so you can merge to rc 👍

@eliykat eliykat removed the hold do not merge, do not approve yet label Jul 7, 2021
@eliykat eliykat merged commit 72f315e into master Jul 7, 2021
@eliykat eliykat deleted the debounce-navigation branch July 7, 2021 01:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants