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

feat: add support for TOTP multi-factor authentication #220

Merged
merged 20 commits into from
Nov 28, 2023

Conversation

AlexCuse
Copy link
Contributor

No description provided.

Copy link
Contributor Author

@AlexCuse AlexCuse left a 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.

server/handlers/get_totp.go Outdated Show resolved Hide resolved
return nil, errors.Wrap(err, "TOTPDecrypt")
}
if !totp.Validate(totpCode, secret) {
return nil, FieldErrors{{"totp", ErrInvalidOrExpired}}
Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor Author

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

@AlexCuse
Copy link
Contributor Author

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 ExpirePassword functionality but open to arguments for a dedicated endpoint.

Copy link
Member

@cainlevy cainlevy left a 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:

  1. remember this device for 30 days - extra cookie
  2. account recovery codes - generate and handle nonces for bypassing 2fa
  3. session security level - indicate if session was secured by 2fa so main app can implement related policies

app/data/encrypted_blob_store.go Show resolved Hide resolved
return nil, errors.Wrap(err, "TOTPDecrypt")
}
if !totp.Validate(totpCode, secret) {
return nil, FieldErrors{{"totp", ErrInvalidOrExpired}}
Copy link
Member

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?

app/services/passwordless_token_verifier.go Outdated Show resolved Hide resolved
app/services/totp_creator.go Outdated Show resolved Hide resolved
app/services/totp_deleter.go Outdated Show resolved Hide resolved
app/services/totp_setter.go Show resolved Hide resolved
app/data/totp_cache.go Outdated Show resolved Hide resolved
app/services/totp_creator.go Show resolved Hide resolved
app/services/totp_setter.go Show resolved Hide resolved
server/handlers/post_session_token.go Outdated Show resolved Hide resolved
@AlexCuse
Copy link
Contributor Author

AlexCuse commented Nov 7, 2023

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 ExpirePassword admin action. We can write up issues for that and the 3 improvements you identified and tackle in bite size chunks from here I think. Happy to let this sit in main awhile before releasing to give time for those + finish building up the new demo I'm working on.

Copy link
Member

@cainlevy cainlevy left a 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.

@AlexCuse AlexCuse linked an issue Nov 9, 2023 that may be closed by this pull request
@AlexCuse AlexCuse merged commit bbc6311 into keratin:main Nov 28, 2023
2 checks passed
@AlexCuse AlexCuse deleted the jlomfa branch November 28, 2023 01:59
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.

Add support for Multifactor Authentication (MFA / TFA)
4 participants