-
Notifications
You must be signed in to change notification settings - Fork 110
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
feat: add support for TOTP multi-factor authentication #220
Conversation
An attempt to see keratin#133 through.
The mysql:5.7 image does not yet have an Apple Silicon compatible build.
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.
@cainlevy pretty determined to get this through this time. I've been looking at the code for a while and I think it seems sound - just noting one change to the API that I think would be beneficial. I am working on adapting the demo app to work as a test harness and hoping once that is available to prove things out we should be able to get this merged.
app/services/credentials_verifier.go
Outdated
return nil, errors.Wrap(err, "TOTPDecrypt") | ||
} | ||
if !totp.Validate(totpCode, secret) { | ||
return nil, FieldErrors{{"totp", ErrInvalidOrExpired}} |
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.
Should we vary the error here if MFA code is missing? Thinking mostly in terms of how applications might try submitting without MFA then prompt user for a code based on the response. This also comes into play if we want to add HOTP support that requires out-of-band code delivery.
Note the timing above we should keep going at least through password validation when making any changes.
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 think probably not - maybe a back end api could support a status check on a user though.
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.
While considering this, I came to believe that it would be more secure to test and verify a TOTP prior to the account password. Given a choice between someone testing and confirming the account password or the TOTP, I'd choose the TOTP since it's such a temporary victory.
In that event, I don't see the harm in responding with a new error code for a missing TOTP. It seems like a simpler API than a status check. What are your thoughts?
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 like it - I presume we should go ahead with the password check in all scenarios still and NOT inform whether failure is bad password or bad OTP. Can do this + adding secret removal to the expire password handler after knocking out the easy stuff here.
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.
Hmm yes a constant time comparison that doesn't reveal whether the password or OTP was correct might be ideal. However if there's any chance something is revealed, I'd prefer to reveal the correctness of the OTP.
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 is already returning invalid or expired for the OTP. Added missing in 153d385
Final note I think that I can't really find a place for in the changeset is that there should be a private API that can be used by back office tooling to remove the MFA secret. Its common to see MFA enrollment used in account takeovers as a way to buy the attacker time. I would be happy to just include this as part of the existing |
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's looking good! while reviewing, i thought about a few related upgrades that could come later:
- remember this device for 30 days - extra cookie
- account recovery codes - generate and handle nonces for bypassing 2fa
- session security level - indicate if session was secured by 2fa so main app can implement related policies
app/services/credentials_verifier.go
Outdated
return nil, errors.Wrap(err, "TOTPDecrypt") | ||
} | ||
if !totp.Validate(totpCode, secret) { | ||
return nil, FieldErrors{{"totp", ErrInvalidOrExpired}} |
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.
While considering this, I came to believe that it would be more secure to test and verify a TOTP prior to the account password. Given a choice between someone testing and confirming the account password or the TOTP, I'd choose the TOTP since it's such a temporary victory.
In that event, I don't see the harm in responding with a new error code for a missing TOTP. It seems like a simpler API than a status check. What are your thoughts?
I think I addressed the main issues @cainlevy take a look. I am about tired of looking at this PR think it is good to go at this point. I'm happy to do a follow up to clear the TOTP secret on |
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 think it's ready! Nicely done.
No description provided.