-
Notifications
You must be signed in to change notification settings - Fork 1
Per 9929 show password strength #494
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
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #494 +/- ##
==========================================
+ Coverage 43.28% 43.35% +0.06%
==========================================
Files 363 364 +1
Lines 11123 11153 +30
Branches 1818 1820 +2
==========================================
+ Hits 4815 4835 +20
- Misses 6148 6156 +8
- Partials 160 162 +2 ☔ View full report in Codecov by Sentry. |
| waiting || | ||
| signupForm.invalid || | ||
| !agreedTerms || | ||
| passwordStrengthMessage !== 'Strong' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we want to block people from signing up just based on password strength. The intention is to show an indicator so that people can see their password strength and make their own decision on if they want to pick a stronger password.
| color: red; | ||
| } | ||
|
|
||
| .strength-weak { | ||
| color: orange; | ||
| } | ||
|
|
||
| .strength-medium { | ||
| color: blue; | ||
| } | ||
|
|
||
| .strength-strong { | ||
| color: green; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not an request for changes immediately but we probably want some design/accessibility review here for color choices that match Permanent's theme and also have proper contrast against the background.
| } | ||
|
|
||
| ngOnInit(): void { | ||
| this.signupForm.controls['password'].valueChanges.subscribe((password) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this subscription get automatically cleaned up? It might, and I'd love confirmation that it does, but I feel like subscriptions have occasionally caused errors in the application once the things they refer to have been destroyed.
| passwordStrengthMessage: string = ''; | ||
| passwordStrengthClass: string = ''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In theory we can make this typing even more specific with a type union. That way we know that Message can only ever be "Strong", "Weak", etc.
In this case it might be a bit redundant but it's nice to keep that in mind, especially when testing for string equality like we do in this component.
|
@meisekimiu thanks! I will do the changes! @k8lyn6 can you please suggest some color values for the messages? |
|
@crisnicandrei we can look at it tomorrow during our accessibility review. cc @yeslikesolo |
meisekimiu
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've decided this probably needs more design work before it's ready to be released. We probably don't want to erase the work already done though, so can you hide this behind a feature flag, using the FeatureFlagService to test for its presence?
|
@meisekimiu for sure! |
|
@meisekimiu let me know if my use of the feature flag was correct, as i have not used it before |
| this.passwordSubscription = this.signupForm.controls[ | ||
| 'password' | ||
| ].valueChanges.subscribe((password) => { | ||
| if (this.featureFlagService.isEnabled('passwordStrngth')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if (this.featureFlagService.isEnabled('passwordStrngth')) { | |
| if (this.featureFlagService.isEnabled('password-strength')) { |
There's a typo in this flag name. We might also want to use "kebab-case" for feature flags. I think it might be the most accessible to people on the experience team, though we don't have a defined standard yet so it might be fine to leave it like this.
As a small note, I like naming a component's instance of the FeatureFlagService just feature so that the code can read nicely: this.feature.isEnabled('feature-name'), but it doesn't really matter what it's named and it's easy to refactor later.
|
@meisekimiu can you tag me in a comment when this is ready for QA? |
meisekimiu
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@k8lyn6 this is ready for QA now. In theory the only step should be to verify it is controlled by the feature flag and doesn't appear by default.
k8lyn6
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the confusion with this ticket Andrei! I shouldn't have put it in the front-end-backlog without a design first. The work you did is great and as soon as Tibi has a design for us, we can pick it back up.
The feature flag seems to be working as designed. When this branch is deployed, I no longer see the password strength information.
|
@k8lyn6 I have pushed the changes with tibi's new design. I also have found a bug while working on this so I will create a ticket for that! Thanks! |
3d68c84 to
af29c65
Compare
|
@meisekimiu before I do a review, was this branch already merged? Should there be a separate PR for additional work? Just wanted to check before proceeding. |
|
@k8lyn6 it was not merged |
meisekimiu
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have approved this already, with the exception of the linting and prettier changes which don't really need to be checked again.
meisekimiu
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't realize that this incorporates new design elements and therefore needs a more thorough review. The idea behind hiding the previous changes behind a feature flag is that it allowed us to merge those changes to main and address the new design elements in their own ticket. In general it's preferable to do that so that tickets can associated with their own pull requests whenever possible, changes are visible to other developers sooner, there are less merge conflicts when integrating more frequently, and it just feels good to be able to close pull requests faster (as it stands right now, we now have to do review for a pull request opened in November, which isn't great).
It feels weird at first, but the whole point of feature flags is that they allow us to merge code for a feature that is incomplete without users seeing it. This lets us make smaller pull requests that can be integrated more frequently, since the feature doesn't have to be 100% done to warrant merging it. See https://martinfowler.com/articles/feature-toggles.html for more details. I encourage us all to take advantage of feature flags to merge things earlier and split things into multiple small tickets.
| .strength-too-weak { | ||
| color: red; | ||
| } | ||
|
|
||
| .strength-weak { | ||
| color: orange; | ||
| } | ||
|
|
||
| .strength-medium { | ||
| color: blue; | ||
| } | ||
|
|
||
| .strength-strong { | ||
| color: green; | ||
| } | ||
|
|
||
| .password-strength-meter { | ||
| margin-top: 5px; | ||
| font-size: 0.9em; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like all of this CSS is now unused since the styles for the meter are handled by the strength-meter component, right?
| // if (this.feature.isEnabled('password-strength')) { | ||
| this.updatePasswordStrength(password); | ||
| // } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these supposed to be commented out? We don't want to be committing commented out code, but we did want a feature flag on this. As it stands, this enables the feature globally regardless of feature flag. (Of course, now that it's been redesigned at least a little bit we don't need the feature flag as much, but Kaitlyn says we can still use it)
| updatePasswordStrength(password: string): void { | ||
| const { message, class: strengthClass } = | ||
| this.getPasswordStrengthDetails(password); | ||
| this.passwordStrengthMessage = message as PasswordType; | ||
| this.passwordStrengthClass = strengthClass; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know that this comes from having to bind to an input value and also deal with form values and validation, but if we want to separate the password strength meter into its own component (which we do want to do! We want to be able to reuse this functionality!), then I think all of its logic should live in the component. In other words, the SignupComponent shouldn't really know anything about password strength since that should all be happening in the password strength meter component instead.
Think about it this way: if we want to reuse this password strength meter on the "Change Password" part of the application, we'll have to duplicate all of this code into it, right?
This gets tricky, but I think with the correct combination of inputs/outputs we can properly bind the password input, its valueChanges observable, and the validity of the whole form to an external password strength meter component that can be reused elsewhere in the UI.
| templateUrl: './strength-meter.component.html', | ||
| styleUrls: ['./strength-meter.component.scss'], | ||
| }) | ||
| export class StrengthMeterComponent implements OnChanges { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if this is actually a better name, but I think maybe naming it specifically PasswordStrengthMeterComponent (or even just PasswordStrengthComponent in case we get rid of the meter one day?) could be better. This component is specifically made for measuring password strength, and not any other form of strength.
| @@ -0,0 +1,50 @@ | |||
| import { Component, Input, OnChanges } from '@angular/core'; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not entirely sure where we should put this, but I want to discourage adding new things to the SharedModule. It is the easiest place to put shared components, but we want to break up that module into more specific pieces (#20) and the first step to get there is to not add any more pieces to it.
I'd say we should put this in the AuthModule as a declared, but not exported, component for the time being, since this is only used on this screen. If we add it to the "Change Password" screen later, it might make sense to just add this to our component library module.
Oh also, we should add the Prettier format pragma to this file.
| styleUrls: ['./strength-meter.component.scss'], | ||
| }) | ||
| export class StrengthMeterComponent implements OnChanges { | ||
| progressBars: string[] = ['', '', '']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not a blocker so feel free not to address it.
I think this implementation is a bit loose, while also being a bit too coupled to the output HTML I guess? If we break down this component it really has 4 possible states: null, weak, medium, and strong. But the way the state is represented internally in the component allows for at least 12* states to be represented in the progress bars alone. While something like ['weak', '', ''] is a normal state, there's nothing in the code that prevents us from having a weird nonsensical state like ['','strong','half']. It also means that in tests we have to express our assertions with these arrays, even though we're logically only testing for those 4 possible states. These tests have to know about the internal details of our component instead of testing more abstract state behavior.
*Technically since progressBars is only of type string[], it's possibility of states is near infinite!
I think ideally the internal state of the application should try to be consistent with how we'd map it out logically. It helps prevent unintended errors or states from appearing and also just makes these components easier to work with. Instead of having to know what specific array values we need to set, we can just set this.strength = PasswordStrength.strong or something like that (it doesn't need to be an enum, but I used an enum since that's typically the tool used to convey a single state of many that an object can be in).
|
@meisekimiu great points! And thank you for the article! I will address this! |
|
@meisekimiu pushed the changes! Thanks again for your input! |
meisekimiu
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for addressing the feedback. Left some additional comments. Can you rename the folder path to the Password Strength component to match its new name? And also consider moving that component into the AuthModule?
| } from '@models'; | ||
| import { DeviceService } from '@shared/services/device/device.service'; | ||
| import { GoogleAnalyticsService } from '@shared/services/google-analytics/google-analytics.service'; | ||
| import { passwordStrength } from 'check-password-strength'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| import { passwordStrength } from 'check-password-strength'; |
We can get rid of this import.
| type PasswordType = '' | 'Too Weak' | 'Weak' | 'Medium' | 'Strong'; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| type PasswordType = '' | 'Too Weak' | 'Weak' | 'Medium' | 'Strong'; |
This can probably go too, right?
| progressBars: string[] = ['', '', '']; | ||
|
|
||
| public strength: PasswordStrength = PasswordStrength.Null; | ||
| public message: string = ''; | ||
| public passwordClass: string = ''; | ||
| @Input() password: string = ''; | ||
| public enabledPasswordCheckStrength: boolean; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a bit of style inconsistency here. Generally we like to visually separate the Input() fields from standard class properties. And some of these have access modifiers (something I generally like to see) while some don't.
| progressBars: string[] = ['', '', '']; | |
| public strength: PasswordStrength = PasswordStrength.Null; | |
| public message: string = ''; | |
| public passwordClass: string = ''; | |
| @Input() password: string = ''; | |
| public enabledPasswordCheckStrength: boolean; | |
| @Input() public password: string = ''; | |
| public progressBars: [string, string, string] = ['', '', '']; | |
| public strength: PasswordStrength = PasswordStrength.Null; | |
| public message: string = ''; | |
| public passwordClass: string = ''; | |
| public enabledPasswordCheckStrength: boolean; |
| this.strength = PasswordStrength.Weak; | ||
| this.message = 'weak'; | ||
| this.passwordClass = 'too-weak'; | ||
| this.progressBars = ['weak', '', '']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just a general piece of advice. This switch statement is rather verbose, isn't it? This is at least partly due to the fact that while we're now using an enum to declare password strength, we still have to set all of those other values alongside it. We could clear up this code quite a bit if we could derive those values from our this.strength value with something like a function call in the typescript layer or a pipe in the template layer.
|
@meisekimiu did the changes |
k8lyn6
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @crisnicandrei I'm not seeing any password strength indications at all when I deploy this branch. Is there something else I need to do? @meisekimiu do I need to activate the feature flag?
|
@k8lyn6 yes, the feature flag must be active |
|
@crisnicandrei what is the name of the feature flag? I don't see it in the retool app (I switched the environment to dev) |
|
@k8lyn6 it is 'password-strength', but I did not set it in retool, do I have to do that @meisekimiu ? This is the first time using the feature flag so I am unsure |
k8lyn6
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, nice! This looks really great. I don't think we did either an accessibility check or a copy check yet, so we might need to release this under the feature flag and then adjust for accessibility (specifically, the yellow might not pass accessibility guidelines for color contrast).
For the copy, can we do the following:
- Password strength: weak
- Password strength: medium
- Password strength: strong
|
@k8lyn6 what do you mean about a copy check? I have looked it up online but found nothing so I am a bit confused |
|
My apologies for the confusion @crisnicandrei ! By copy, I mean the words and language we're using in the design (such as "Your password is strong!"). Normally, in the design process, the last thing I do with Tibi is a review of the copy so that I don't have to ask you to re-code any of the language in the design after the fact. I wasn't able to do an accessibility or copy review before the implementation, which is why we're changing up the language on the fly. If we could swap out the text that's currently implemented with what I wrote in my review, that would be great. I think that's the only change I have for now, but Han, Natalie, and I will need to do an accessibility review of the color contrast. Because this is behind a feature flag, you should be able to merge the changes after we change the text and it won't go live on production yet. We can make additional tickets for any changes after the accessibility review. |
|
@k8lyn6 I have updated the text |
k8lyn6
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the update @crisnicandrei ! I think we're good to merge this PR, but not turn the feature on on prod yet (since it's still hidden by a feature flag). We'll do an accessibility review and add any additional tickets that are necessary afterwards.
cc @meisekimiu
This pr displays the strength of the password when the user registers a new account.
Move the logic from the signup component into the password-strength component. This way the new component will be the only one handling the password strength.
6f2e10d to
121ca22
Compare
This pull request displays the strength of a user's password when registering a new account.
Steps to test: