laravel / fortify

Backend controllers and scaffolding for Laravel authentication.
https://laravel.com/docs/fortify
MIT License
1.61k stars 295 forks source link

Create & Send Recovery Codes #148

Closed alanondra closed 3 years ago

alanondra commented 3 years ago

Looking over the login mechanism, it looks like the expectation is that a user already has a recovery code at registration?

I think it would make more sense for it to generate a recovery code with a configurable lifetime, then send it to a user's e-mail or mobile number, or some other mechanism like a 2FA app like Duo.

taylorotwell commented 3 years ago

Probably won't be taking this on in the near future. Feel free to build into your own application. The more opinions we take on the more maintenance burden and complaints I have to deal with 😄

alanondra commented 3 years ago

It's been nearly a month since I've touched it but the last thing I attempted was using a modified version of the RedirectIfTwoFactorAuthenticatable action (and modifying the logic chain up to the point of using it) to send an e-mail with a newly generated code, but the modification is never used and I don't have time to invest into why.

I've simply disabled it. It seems now I'm using a login method that is a step back from previous versions of the framework in that it doesn't even support throttling out-of-the-box without adding more stuff I don't want/need.

sburkett commented 3 years ago

Just to comment further on this. We have built our enterprise SaaS app with Laravel. We are in the healthcare IT space, and the service is not public, where people can come register on their own, etc. The challenge we have now is that due to HIPAA concerns and other things, we are in a mode where we need to force 2FA for all users, existing and new.

What I am envisioning right now is that when new or existing users login, if they are not configured for 2FA, we send them into a special funnel which forces them to set it up. After that, they will be logged out and pushed through the normal 2FA login process.

sburkett commented 3 years ago

Just a bit of a follow-up for anyone needing some help with this down the road. To generate or re-generate the 2FA secret, a fresh set of recovery codes, and the QR code, for a given user, you can take this little test code and pretty much go from there:

<?php

namespace App\Http\Controllers;

use App\Models\User;

use App\Http\Controllers\Controller;

use Laravel\Fortify\RecoveryCode;
use Illuminate\Support\Collection;

class FooController extends Controller
{
  public function foo()
  {
    $user = User::find(1); // Or whatever user ID you want

    // Generate (or re-generate) 2FA secret and recovery codes. Also saves to the User model.
    app(\Laravel\Fortify\Actions\EnableTwoFactorAuthentication::class)($user);

    // Dump out the recovery codes
    $recoveryCodes = json_decode(decrypt($user->two_factor_recovery_codes));

    echo "<h3>Recovery Codes</h3>";
    dump($recoveryCodes);

    // Dump out the QR code
    echo $user->twoFactorQrCodeSvg();
  }
}

You can throw that into a test controller, add a route for it, and see the output to verify.

You can do this sort of thing in a loop for all users, and then send them an email, etc. with all of the pertinent information so they can log in using 2FA.

$users = User::all();

foreach($users as $user)
{
  // Generate (or re-generate) 2FA secret and recovery codes. Also saves to the User model.
  app(\Laravel\Fortify\Actions\EnableTwoFactorAuthentication::class)($user);

  // Decrypt printable recovery codes
  $recoveryCodes = json_decode(decrypt($user->two_factor_recovery_codes));

  // Get the QR code for the current user
  $qrCode = $user->twoFactorQrCodeSvg();

  // Pass the recovery codes and QR code to a background job which sends emails, or just send the email here, whatever
  ...
}