はじめに
昨今、様々なログイン時に使用されている2段階認証の実装備忘録です。
PHPでの実装にあたり、PHPGangsta/GoogleAuthenticatorを使用している方を見つけたので、参考に組んでみました。
認証の流れ
フロントでAPIを呼び出し、
返却されたQRコードを画面に表示する手法で実装しました。
大まかな流れは以下になります。
- ユーザー登録
同時に秘密鍵をDBに登録 - ログイン後、認証ページに移動
- QRコードを表示
- Google Authenticatorで読み取る
- 表示された6桁のコードを入力して認証
実装の手順
➀Composerインストール
composer show --available phpgangsta/googleauthenticator
composer require --prefer-dist phpgangsta/googleauthenticator:dev-master
➁秘密鍵のDB登録
まずは秘密鍵を生成してDBに登録します。
今回はユーザーの登録時に秘密鍵を生成します。
RecipientController.php
$GangstaModel = new GoogleAuthenticatorFileTransferModel();
$secret = $GangstaModel->createSecret();
// フロントで入力されたユーザー情報と作成した秘密鍵をDBへ保存
$RecipientModel->createRecipient(
$id
, $name
, $tel
, $secret
);
➂QRコード生成
登録済ユーザーがログインを行った際に、
②で保存した秘密鍵を使用してQRコードを生成します。
QRコードを生成できたらフロントにURLを返却します。
RecipientController.php
$GangstaModel = new GoogleAuthenticatorFileTransferModel();
// 秘密鍵取得
$secret = $RecipientModel->getSecret($id);
// QRコード生成
$qrCode = $GangstaModel->createQrCode($secret);
return response()->json(['qrCode' => $qrCode]);
➃QRコード表示
バックエンドからQRコードのURLを取得できたら、
<img>
のsrc
にセットします。
ShowQrCode.vue
<template>
.
.
<img :src="qrCode">
.
.
</template>
<script>
.
.
.
const qrCode = ref(); //QRコードのURL
const showQrCode = async() => {
const url = 'api/showQrCode';
const req = {
token: token.value //ユーザー毎に一意の値
};
const response = await axios.post(url, req);
if(response) {
qrCode.value = response.data.qrCode;
}
}
return {
qrCode
}
.
.
.
</script>
処理が完了するとQRコードが画面に表示されるので、
Google Authenticatorで読み取ってコードを表示してください。
⑤認証処理
ログイン後、認証コード入力画面が表示されます。
Google Authenticatorに表示された6桁のコードを入力し、Laravelに送信します。
Authenticate.vue
const authenticate = async() => {
const url = "api/authenticate";
const req = {
userId = userId.value,
code = inputCode.value // 認証コード
}
const result = await axios.get(url, {params: req});
if(result) {
//2段階認証成功の場合、成功画面に遷移
router.push('/success');
}
}
RecipientController.php
public function checkOnetimeCode(Request $req) {
$GangstaModel = new GoogleAuthenticatorFileTransferModel();
$RecipientModel = new RecipientModel();
$userId = $req->get('userId');
$code = $req->get('code');
// ユーザー作成時DBに保存した秘密鍵を取得
$userSecret = RecipientModel->getSecret($userId);
// 認証コードと秘密鍵を用いて整合性をチェック
$result = $GangstaModel->checkOnetimeCode($code, $secret);
// チェックが成功の場合、trueを返却
if($result) {
return true;
}
}
おわりに
難しそうな印象を持っていましたが、実装は思っていたよりも単純でした。
今回はサンプルなのでエラー処理等を記述していません。
実際に導入する際は、きちんとハンドリングを実装する必要がありますね。
それでは。
参考サイト