logo
Published on

Đăng xuất khỏi thiết bị khác trên Laravel

Mục lục

Vấn đề

Gần đây mình được khách hàng yêu cầu một tính năng tưởng chừng rất cũ nhưng đó giờ mình chưa làm bao giờ, đó là đăng xuất ra khỏi tất cả thiết bị khác sau khi đăng nhập. Có thể hiểu là không thể sử dụng đồng thời trên nhiều thiết bị. Mình nghĩ đây cũng là một feature quốc dân nên làm bài này để chia sẻ cũng như note lại cho mình.

Giải pháp với PHP

Đối với một dự án PHP thông thường, mình sẽ nghĩ đến giải pháp tạo một session_token và lưu nó lại nó đăng nhập, nếu đăng nhập ở một thiết bị khác, token đó sẽ được tạo lại và không còn khớp với token cũ nữa. Và mình chỉ cần kiểu tra token đó trong phương thức checkAuth của mình.

Đăng xuất khỏi tất cả thiết bị khác trên Laravel

Lúc nào cũng vậy, Laravel luôn có giải pháp cho những vấn đề thường gặp như thế này, vừa gõ từ khoá "laravel logout from another browser" thì lập tức mình nhận được câu trả lời chính là hàm Auth::logoutOtherDevices(), nếu đã có sẵn build-in như thế này thì tội tình gì phải code.

Bước 1. Bật middleware AuthenticateSession ở Http\Kernel.php

Mở file App\Http\Kernel.php lên và uncomment cái middleware nhé:

'web' => [
    // ...
    \Illuminate\Session\Middleware\AuthenticateSession::class,
    // ...
],

Bước 2. Sử dụng hàm Auth::logoutOtherDevices

Bây giờ công việc còn lại chỉ là thêm đoạn mã Auth::logoutOtherDevices($currentPassword); vào chỗ nào mà bạn muốn ứng dụng sẽ thực hiện việc log out trên tất cả trình duyệt khác.

Ở đây mình muốn logoutOtherDevices khi đăng nhập nên mình sẽ mở AuthenticatedSessionController ở action store() - action này thực hiện việc đăng nhập. Mình thêm dòng logoutOtherDevices bên dưới

use Illuminate\Support\Facades\Auth;

public function store(LoginRequest $request)
{
	  $request->authenticate();

	  Auth::logoutOtherDevices($request->password); // <= Đây nè

	// ...
}

Và nó không hoạt động ❌

Mình không biết tại sao nhưng bây giờ mình thử để nó vào bên trong authenticate()

use Illuminate\Support\Facades\Auth;

public function authenticate()
{
	  $this->ensureIsNotRateLimited();
	  $this->ensureIsNotLocked($this->username);

	if (! Auth::attempt($this->only('username', 'password'), $this->filled('remember'))) {
		RateLimiter::hit($this->throttleKey());

		throw ValidationException::withMessages([
		    'username' => __('auth.failed'),
		]);
	}
	Auth::logoutOtherDevices($this->password); // <= Đây nè

	RateLimiter::clear($this->throttleKey());
}

Nó lại work ✔️

Lời kết

Qua bài viết này mình muốn trích dẫn lại một câu của anh Phát Seth:

Các bạn nên chú ý, khi xài framework (nhất là mấy cái nổi nổi, tech-trending,…) thì bản thân các features, methods,… của nó đã được test và viết test rất là kỹ càng rồi. Hãy ưu tiên sử dụng built-in của Framework, trừ khi mà nó ko có cái bạn muốn, thì hãy tính đến invent the wheel. Cụ thể những cái Authentication và Authorization.