generateNewSet(); session()->put(self::SETUP_SECRET_SESSION_KEY, encrypt($codes)); $downloadUrl = 'data:application/octet-stream;base64,' . base64_encode(implode("\n\n", $codes)); return view('mfa.backup-codes-generate', [ 'codes' => $codes, 'downloadUrl' => $downloadUrl, ]); } /** * Confirm the setup of backup codes, storing them against the user. * @throws Exception */ public function confirm() { if (!session()->has(self::SETUP_SECRET_SESSION_KEY)) { return response('No generated codes found in the session', 500); } $codes = decrypt(session()->pull(self::SETUP_SECRET_SESSION_KEY)); MfaValue::upsertWithValue($this->currentOrLastAttemptedUser(), MfaValue::METHOD_BACKUP_CODES, json_encode($codes)); $this->logActivity(ActivityType::MFA_SETUP_METHOD, 'backup-codes'); return redirect('/mfa/setup'); } /** * Verify the MFA method submission on check. * @throws NotFoundException * @throws ValidationException */ public function verify(Request $request, BackupCodeService $codeService, MfaSession $mfaSession, LoginService $loginService) { $user = $this->currentOrLastAttemptedUser(); $codes = MfaValue::getValueForUser($user, MfaValue::METHOD_BACKUP_CODES) ?? '[]'; $this->validate($request, [ 'code' => [ 'required', 'max:12', 'min:8', function ($attribute, $value, $fail) use ($codeService, $codes) { if (!$codeService->inputCodeExistsInSet($value, $codes)) { $fail(trans('validation.backup_codes')); } } ] ]); $updatedCodes = $codeService->removeInputCodeFromSet($request->get('code'), $codes); MfaValue::upsertWithValue($user, MfaValue::METHOD_BACKUP_CODES, $updatedCodes); $mfaSession->markVerifiedForUser($user); $loginService->reattemptLoginFor($user, 'mfa-backup_codes'); if ($codeService->countCodesInSet($updatedCodes) < 5) { $this->showWarningNotification('You have less than 5 backup codes remaining, Please generate and store a new set before you run out of codes to prevent being locked out of your account.'); } return redirect()->intended(); } }