oogle2FA for Laravel
Installing
composer require pragmarx/google2fa-laravel
Publish the config file
php artisan vendor:publish --provider="PragmaRX\Google2FALaravel\ServiceProvider"
User table modify
google2fa_secret
VARCHAR(255) NULL DEFAULT NULL COLLATE 필드를 User 테이블에 추가
처리하기
google2fa 가 세팅되었다면 로그인시 정상적인 ID/pwd 로 접근하였을때 바로 세션을 생성하지 않고 2google2fa를 실행시킨 후 이 것까지 성공하면 세션을 활성화 시킨다.
코드는 아래와 같다.
routes > web.php
// 2fa
Route::group(['namespace' => 'App\Http\Controllers', 'middleware' => 'web'], function () {
Route::get('admin/2fa/setting', 'Google2FAController@setting')->name('admin.2fa.setting');
Route::get('admin/2fa/enable', 'Google2FAController@enableTwoFactor')->name('admin.2fa.enable');
Route::get('admin/2fa/disable', 'Google2FAController@disableTwoFactor')->name('admin.2fa.disable');
Route::get('/2fa/validate', 'Auth\LoginController@getValidateToken');
Route::post('/2fa/validate', 'Auth\LoginController@postValidateToken');
});
로그인
LoginController.php
use Cache;
use App\Http\Requests\ValidateSecretRequest; // 이부분은 아래에 다시 설명
public function login(Request $request){
..........
// 로그인 유효성 처리 완료후
if ($user->google2fa_secret) { // google2fa_secret 가 세팅된 상태라면
Auth::logout(); // 로그아웃 시킨다.
$request->session()->put('2fa:user:id', $user->id); // 현재값은 세션에 넣어 둔다.
return redirect('2fa/validate'); // 2fa를 입력하는 창으로 이동 시킨다. (아래 getValidateToken() 호출)
} else {
// 성공시 이동경로
}
}
// 2fa 페이지 호출
public function getValidateToken()
{
if (session('2fa:user:id')) {
return view('auth/2fa-validate');
}
return redirect('login');
}
// auth/2fa-validate 에서 submit시 이곳을 본다.
public function postValidateToken(ValidateSecretRequest $request)
{
$userId = $request->session()->pull('2fa:user:id'); // 기존 세션값에서 userId 가져옮
$key = $userId . ':' . $request->totp; // userId와 입력값(totp: google 2fa 값)을 이용하여 키를 만들어 준다.
//use cache to store token to blacklist
Cache::add($key, true, 4); // 키값을 추가한다.
//login and redirect user
$user = Auth::loginUsingId($userId); // 로그인 처리
return redirect()->intended('/');
}
Requests
Http 하위에 Requests 디렉토리를 생성후 아래와 같이 각각 Request.php 및 ValidateSecretRequest.php를 저장한다.
- Request.php
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
abstract class Request extends FormRequest
{
//
}
- ValidateSecretRequest.php
<?php
namespace App\Http\Requests;
use Cache;
use Crypt;
use Google2FA;
use App\Models\Auth\User\User;
use App\Http\Requests\Request;
use Illuminate\Validation\Factory as ValidatonFactory;
class ValidateSecretRequest extends Request
{
/**
*
* @var \App\User
*/
private $user;
/**
* Create a new FormRequest instance.
*
* @param \Illuminate\Validation\Factory $factory
* @return void
*/
public function __construct(ValidatonFactory $factory)
{
$factory->extend(
'valid_token',
function ($attribute, $value, $parameters, $validator) {
$secret = Crypt::decrypt($this->user->google2fa_secret);
return Google2FA::verifyKey($secret, $value);
},
'Not a valid token '
);
$factory->extend(
'used_token',
function ($attribute, $value, $parameters, $validator) {
$key = $this->user->id . ':' . $value;
return !Cache::has($key);
},
'Cannot reuse token'
);
}
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
try {
$this->user = User::findOrFail(
session('2fa:user:id')
);
} catch (Exception $exc) {
return false;
}
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'totp' => 'bail|required|digits:6|valid_token|used_token',
];
}
}