MFA (Take Two)

It’s been a while since I’ve written a post. I’ve been heads down working on the platform thinking more about use cases, revising code and introducing a basic React client. If I haven’t mentioned already, I love IntelliJ. It’s my tool of choice and have been using it for years. Every time they make a release they introduce something cool that is helpful.

Ok, back to the platform. After I wrote the first post on multi-factor authentication, I began filling out more of a full user registration flow where a user comes to the site, creates an account and then verifies it via an e-mail link. I really REALLY wanted to have people opted in by default to use MFA. I set the code up to do this so that when a user verified their registration I e-mailed them their secret QR code to scan into Google Authenticator or Authy that they could easily scan in. As I looked at this beautiful e-mail (yes, beauty is in the eye of the beholder) I thought back to a business I worked for where we asked people to use MFA and paused…

Whisked away into thoughts about the not so distant past I know MFA is still hard for many people. Many smart people who care about security don’t even turn it on. As much as I want this platform I’m writing to be secure I want to protect my users even more and I’m also cognizant that I can’t force people to do it. What I can do is provide users options and incentives to become more secure. Give them small steps to accomplish to help them protect themselves and have a platform that is flexible enough to support these use cases.

So… I revised my login code to have a normal username and password login flow and then added support to allow a user to opt into multi-factor authentication. After looking at some other solutions build out there by bigger people (I’m looking at you Google) I came upon some basic rules for updating security settings.

  1. Regardless of whether you are logged in or not, you MUST re-authenticate with a password before toggling ANY security setting.
  2. Opting into multi-factor authentication is a multi-step process and SHOULD be time bound.
  3. Opting out of multi-factor authentication is a single step process.

Here’s the code that follows the rules set above and allow for a user to enable or disable their multi-factor authentication setting.

@PostMapping(value = "/user/multifactorauth")
public ResponseEntity<?> updateMultiFactorAuth(@Valid @RequestBody MultiFactorPreferenceRequest request) {
    // Get the logged in user.
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    String username = authentication.getName();
    CustomUserDetails userDetails = (CustomUserDetails) userDetailsManager.loadUserByUsername(username);
    User user = userDetails.getUser();

    TOTPAuthenticationToken authToken = new TOTPAuthenticationToken(username, request.getPassword(),

    // Password must be valid to update any security setting.
    if (!authenticationProvider.authenticatePassword(authToken)) {
        return ResponseEntity.badRequest().build();

    // User is in the process of setting up multi-factor auth. Validate their verification code and enable
    // 2-factor auth or return a bad request response.
    if (request.isEnable2FA() && !userDetails.isUsing2FA() && userDetails.getSecret2FA() != null &&
            authToken.getOneTimePassword() != null) {
        // Get the verification code they sent and verify it.
        if (!googleAuthenticator.authorize(userDetails.getSecret2FA(), authToken.getOneTimePassword())) {
            return ResponseEntity.badRequest().build();

        // Go ahead and update multi-factor setting based on request.

        Map<String, String> map = new HashMap<>();
        map.put("firstName", user.getFirstName());
            "", "99 Miles to Empty Multi-factor Enabled", map, MULTI_FACTOR_ENABLED);

        return ResponseEntity.ok().build();

        // TODO: Send success email for setting up multi-factor auth.

    // If the request is to enable multi-factor, verify that the user doesn't already have it enabled,
    // generate a new secret token and return it. A subsequent call to this endpoint will be made to verify
    // that the user can generate a verification code from that secret and then we will enable MFA.
    if (request.isEnable2FA() && !user.isUsing2FA()) {
        if (user.getSecret2FA() == null) {
            // User does not have multi-factor auth and is requesting to enable it.

            // Create a TOTP secret that can be used for two-factor authentication.
            GoogleAuthenticatorKey googleAuthenticatorKey = googleAuthenticator.createCredentials();
            // Save the changes

            Map<String, String> map = new HashMap<>();
            map.put("firstName", user.getFirstName());
            map.put("secret", user.getSecret2FA());

            // TODO: Send email to let user know that a request has been made to enable multi factor auth for their account.

            // Return the secret. Client application can show user a QR code to scan in Authy or Google Authenticator.
            return ResponseEntity.ok(map);
    } else if (!request.isEnable2FA()) {
        // Disable multi-factor auth.

    return ResponseEntity.badRequest().build();

This code has not yet been integrated into the example project at

Also, if you are looking for hosting please consider Dreamhost. If you’d like to sign up with them and feel inclined to throw me some credit, please use my referral link

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.