LoginSignup
2
3

Laravel ログイン認証に使用する項目を変更する

Posted at

Laravelを使ってログイン認証を作成した場合、初期状態ではemailとpassowordのセットをPOST送信することでログインできる形にLoginControllerが作成されますが、別の項目を使ってログインするようにしたい場合、ちょっとした工夫が必要となります。

経緯

メールによる認証トークンを使った二段階認証の実装時、最終的なログインの値をemail・passwordではなく二段階認証トークンにする必要がありました。
ログインに使用するパスワード項目を変更するのにEloquentUserProviderをオーバーライドする方法しか見当たらなかったため、備忘録として記載しておきます。

二段階認証の具体的な実装方法については省きますが、以下のようなフローに組み替えることで最小限のオーバーライドで対応することができました。

  • 1ページ目でID・パスワードを送信してHash::check()によるパスワードの整合性チェック (ここは自前で実装)
  • 2ページ目で2段階認証トークンを入力させてで本来のログイン機構(attemptLogin)を使用して権限を付与する

email以外の値を認証に使用する

nameやlogin_idなど、メールアドレス以外の項目でユーザーを特定する場合です。
この項目でアカウントを特定できる必要があるため、テーブルの中でユニークな項目である必要があります。
LaravelUIのログイン画面はIlluminate\Foundation\Auth\AuthenticatesUsersトレイトでデフォルトのログイン処理が構成されていて、細かい挙動はこのトレイトで付加された関数をオーバーライドする形調整することができます。
モデル・データベース側にも追加したい項目を用意した上でLoginControllerに以下のような関数を追加すればメールアドレス以外の項目でログインするようになります。

LoginController.php
/**
 * Get the login username to be used by the controller.
 *
 * @return string
 */
public function username()
{
    // 認証に利用したいフィールド名に変更
    return 'name';
}

password以外の値を認証に使用する

password 側を変更するのはすこし難儀します。
なぜなら入力値はSessionGuardを通して、EloquentUserProviderで判定されるのですが、認証に仕様する値のキーがpasswordで固定されているためです。
https://github.com/laravel/framework/blob/10.x/src/Illuminate/Auth/EloquentUserProvider.php#L151
(config/auth.phpで設定が変更されている場合は別のクラスとなります、念のため)

そのため、EloquentUserProviderを自前でオーバーライドしたくない場合は、SessionGuard::attemptに渡す暗号化された値のキーは必ずpasswordである必要があります。
ということで、シンプルにやるならフォームからトークンを送信する際にpasswordとして送ってあげればOKです。
<input type="text" name="password">

今回は二段階認証で別にパスワードを持っているため、混同しないようコントローラ側で対応しました。

<input type="text" name="tfa_token">

LoginController.php
/**
 * Attempt to log the user into the application.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return bool
 */
protected function credentials(Request $request)
{
    return $request->only('id') + ['password' => $request->tfa_token];
}

そのままではモデル側の($user->password)と比較されてしまうので、Authenticatableを継承したモデル側にもgetAuthPasswordメソッドを追加する必要があります。

User.php
/**
 * Get the password for the user.
 *
 * @return string
 */
public function getAuthPassword()
{
    return $this->tfa_token;
}

注意点として、この値はハッシュとして対応をチェックされるためレコードの登録時にpasswordと同様暗号化されている必要があります。setterメソッドでbcryptメソッドを通すなどして、データベースに保存する前に暗号化するようにしておきましょう。
これで$user->tfa_tokenとフォームから送信した$request->tfa_tokenの値がハッシュとして対応した値となります。

ここまでやればAuthenticatableトレイトがうまくやってくれるはずです。

内訳の解説

ログインの判定ですが大まかに以下のようなかんじです。(今回直接関係しない部分は省いています)

  • Illuminate\Foundation\Auth\AuthenticatesUsersが組み込まれたLoginControllerにはloginメソッドが追加されるので、ルーティングからloginメソッドが呼ばれる。
  • loginメソッドから同じトレイトのattemptLoginが呼ばれてSessionGuard::attemptにログインに必要なフォーム入力値が渡される。
    AuthenticatesUsers.php
    protected function attemptLogin(Request $request)
    {
        return $this->guard()->attempt(
            $this->credentials($request), $request->filled('remember')
        );
    }
    
  • EloquentUserProvider::retrieveByCredentials password以外の項目を材料に該当するユーザーのレコードを検索。
    ここでレコードが見つからなければ失敗。
  • EloquentUserProvider::validateCredentialsでpassword項目とモデルのgetAuthPassword()がハッシュとして対応していればログイン成功。
    (ハッシュとして対応をチェックするので、モデル側の値はハッシュ化されている必要あり)
    LoginController::attemptLoginでtrueが返る。
  • LoginController::sendLoginResponseでログインセッションの書き込みとリダイレクトが実行される。

認証に使用するモデルはIlluminate\Foundation\Auth\User(Authenticatable)を継承しているので、その中で継承しているgetAuthPasswordをオーバーライド
https://laravel.com/api/10.x/Illuminate/Auth/Authenticatable.html#method_getAuthPassword

また、EloquentUserProviderをオーバーライドでも対応可能です。
暗号化された値を用いない場合などはそのほうがよさそうですね。
https://qiita.com/hkusaba/items/c5a7f43a6312a259f200
こちらの記事が参考になります。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3