Đã có Magic Logic Links! Giết Password được rồi!

5208

Bước xác minh, trong nhiều năm qua, đã có những bước tiến mạnh mẽ. Chúng ta đã chứng kiến sự thay đổi từ tổ hợp email-password sang xác minh mạng xã hội, và cuối cùng là xác minh lược bỏ password (mà thực ra lại giống kiểu xác minh “chỉ email” hơn). Trong trường hợp login lược bỏ password, ứng dụng sẽ giả định bạn nhận login link từ inbox nếu email được cung cấp đúng là của bạn.

Quy trình thường thấy của một hệ thống login không password diễn ra như sau:

  • Người dùng truy cập vào login page
  • Nhập địa chỉ email và xác nhận
  • Một đường link được gửi đến email
  • Khi click vào link, họ được chuyển hướng trở lại ứng dụng và đăng nhập
  • Đường link bị vô hiệu hóa

Đây là một cách xác minh tiện lợi nếu bạn không tài nào nhớ được password cho ứng dụng đó, nhưng bạn lại nhớ email khai báo lúc đăng ký. Một điểm thú vị là thận chí cả Slack cũng dùng đến kỹ thuật này.

Trong bài viết này, chúng ta sẽ tìm cách tích hợp hệ thống trên vào ứng dụng Laravel. Đoạn code hoàn chỉnh có thể được tìm thấy ở đây.

Tạo ứng dụng

Hãy bắt đầu bằng cách tạo ứng dụng Laravel mới. Tôi sẽ sử dụng Laravel 5.2 trong bài viết này:

Nếu bạn đã có sẵn một Laravel project với users và passwords, đừng lo lắng – chúng ta sẽ không động đến lớp xác minh đã có, mà tạo thêm một lớp mới chồng lên. User vẫn sẽ có tùy chọn đăng nhập thông qua password.

Database Setup

Kế tiếp, chúng ta sẽ phải set up MySQL database trước khi chạy bất cứ migration nào.

Mở file .env trong thư mục root và nhập vào hostname, username, và database name:

Nếu bạn đang dùng Homestead Improved box, tổ hợp database/username/password sẽ là homestead, homestead, secret.

Scaffolding Auth

Laravel 5.2 có một tính năng cực kỳ hay, đó là khả năng thêm lớp xác minh đã tạo sẵn với một dòng lệnh duy nhất. Hãy làm thử nhé

Lệnh này sẽ giúp ta lên khung cho mọi thứ cần có ở bước xác minh như Views, Controllers, và Routes.

Migrations

Nếu nhìn vào database/migration, ta sẽ nhận thấy rằng ứng dụng Laravel được tạo đi kèm với migration để tạo user table và password_resets table.

Chúng ta sẽ không thay đổi gì cả vì ta vẫn muốn ứng dụng có bước xác minh thông thường.

Để tạo table, chạy:

Giờ đây ta đã có thể đưa ứng dụng vào hoạt động, và có thể đăng ký/đăng nhập bằng link trong nav.

Đăng kí ngay để khai phá những công thức tối ưu hóa PHP & PHP 7
Đăng kí ngay để khai phá những công thức tối ưu hóa PHP & PHP 7

Kế tiếp, chúng ta muốn thay đổi đường link login để chuyển hướng người dùng sang custom login view, tại đây người dùng sẽ cung cấp địa chỉ email mà không phải nhập password.

Đi đến resources/views/layouts/app.blade.php. Taị đây chúng ta sẽ tìm nav partial. Thay đổi dòng có login link (ngay dưới phần điều khiện để kiểm tra xem người dùng đã log out chưa) với:

Khi người dùng cố gắng truy cập một route được bảo vệ mà chưa login, họ cần được đưa đến custom login view mới thay vì view thông thường. Hành vi này được xác định trong authenticate middleware. Nên chúng ta sẽ thay đổi chính chỗ đó:

app/Http/Middleware/Authenticate.php

Hãy để ý, trong else block chúng ta đã chuyển địa điểm chuyển hướng đến login/magiclink thay vì login thông thường.

Tạo Magic Login Controller, View, và Routes

Bước tiếp theo, chúng ta sẽ tạo MagicLoginController bên trong thư mục Auth:

Rồi sau đó thực hiện định tuyết để hiển thị custom login page của chúng ta:

app/Http/routes.php

Hãy cập nhật MagicLoginController để kèm theo show action:

app/Http/Controllers/Auth/MagicLoginController.php

Để có login view, chúng ta sẽ mượn tạm login view thường và bỏ trường password đi. Chúng ta cũng sẽ thay đổi post URL của form đến \login\magiclink.

Hãy tạo một thư mục magic trong views/auth để giữ view mới này:

Và cập nhật view vừa tạo thành:

resources/views/auth/magic/login.blade.php

Chúng ta sẽ vẫn để lại tùy chọn login với password vì người dùng vẫn có thể thích dùng password login hơn. Vậy nếu người dùng click login từ nav, họ sẽ được đưa đến login view trông như sau:

Tạo Tokens và liên kết chúng với

Bước tiếp theo của chúng ta là tạo token và liên kết chúng với người dùng. Điều này xảy ra khi ai đó nhập email để đăng nhập.

Hãy bắt đầu với bước tạo route để xử lý posting action của form đăng nhập.

app/Http/routes.php

 

Đăng kí ngay để khai phá những công thức tối ưu hóa PHP & PHP 7
Đăng kí ngay để khai phá những công thức tối ưu hóa PHP & PHP 7

Sau đó, chúng ta sẽ thêm controller method tên sendToken trong MagicLoginController. Method này sẽ chứng thực địa chỉ email, liên kết token với user, gửi đi email đăng nhập và thông báo người dùng check email của mình:

app/Http/Controllers/Auth/MagicLoginController.php

Sau khi đã có địa chỉ email đạt chuẩn, chúng ta có thể gửi email đăng nhập đến người dùng. Nhưng trước khi email được gửi đi, chúng ta phải tạo trước token cho người dùng đang tìm cách đăng nhập này. Tôi không muốn phải để tất cả method trong MagicLoginController nên ta sẽ tạo model user-token dể xử lý chỉ một phần các method này.

Lệnh này sẽ giúp chúng ta có cả model và migration. Chúng ta vẫn phải tinh chỉnh migration một chút và thêm user_id và cột token. Mở file migration vừa tạo và thay đổi method up thành thế này:

database/migrations/{timestamp}_create_user_tokens_table.php

Sau đó chạy lệnh migrate Artisan:

Trong model UserToken, chúng ta cần thêm user_id và token dưới dạng thông số gán được (assignable attributes). Chúng ta cũng nên xác định mối quan hệ giữa model này với model User và ngược lại:

App/UserToken.php

Sau đó trong App/User.php xác định rằng một User chỉ có thể có một token liên kết với chúng:

App/User.php

Đến đây, hãy tạo token. Trước hết, chúng ta cần phải lấy user object thông qua email trước khi tạo token. Tạo method trong User model với tên getUserByEmail để xử lý tính năng này:

App/User.php

Chúng ta phải thêm namespace cho các class User và UserToken vào MagicLoginController của chúng ta để có thể call các method trong những class này từ controller.

app/Http/Controllers/Auth/MagicLoginController.php

Trong khối code trên, chúng ta đang truy xuất user object dựa trên email được cung cấp. Trước khi đến bước này, các bạn cần nhớ là ta phải chứng thực sự tồn tại của địa chỉ email được cung cấp trong user table. Nhưng nếu có ai đó vượt qua được bước chứng thực và cung cấp email chưa có trong kho lưu trữ của chúng ta, ta sẽ báo họ phải đăng ký trước.

Khi đã có user object, ta sẽ tạo token cho họ.

Đăng kí ngay để khai phá những công thức tối ưu hóa PHP & PHP 7
Đăng kí ngay để khai phá những công thức tối ưu hóa PHP & PHP 7

Gửi Token qua email

Chúng ta đến đây đã có thể email Token vừa tạo đến người dùng dưới dạng URL. Trước hết, chúng ta sẽ phải yêu cầu Facade Mail trong model để hỗ trợ chức năng gửi email.

Tuy nhiên, trong phạm vi bài viết, chúng ta sẽ không gửi bất cứ email thật nào cả, mà chỉ để xác nhận là ứng dụng có thể gửi được email trong logs thôi. Để thực hiện, hãy tìm đến file .env của bạn, dưới phần mail chỉnh thành MAIL_DRIVER=log. Hơn nữa, chúng ta sẽ không tạo email view; chỉ tạo một email thuần từ class UserToken.

Hãy tạo thêm một method nữa trong model UserToken mang tên sendEmail để xử lý chức năng này. URL tổ hợp của token, email address và giá trị remember me sẽ được tạo trong method này.

app/UserToken.php

Khi tạo URL, chúng ta sẽ dùng hàm http_build_query của PHP để giúp một query từ array options được pass. Trong trường hợp của chúng ta là email, và giá trị remember me.

Đã đến lúc cập nhật MagicLoginController và call method sendEmail:

app/Http/Controllers/Auth/MagicLoginController.php

 

Chúng ta cũng sẽ tích hợp một số trình tin nhắn đơn giản để gửi noti. Trong resources/views/layouts/app.blade.php, hãy thêm đoạn code này vào hay trên content của bạn vì flash message sẽ hiện trên bất kỳ nội dung nào khác:

resources/views/layouts/app.blade.php

Sau đó tạo notifications partial:

resources/views/layouts/partials/_notifications.blade.php

Sau đó tạo notifications partial:

resources/views/layouts/partials/_notifications.blade.php

Trong partial, chúng ta đã dùng helper session để giúp chúng với những màu noti khác nhay dựa trên các tình trạng session như success hoặc error.

Tại thời điểm này, chúng ta đã có thể gửi email. Bạn có thể thử trước bằng cách đăng nhập với một địa chỉ email hợp lệ, sau đó tìm đến file laravel.log. Chúng ta sẽ có thể thấy email có chứa URL ở cuối file log.

Kế tiếp, chúng ta muốn chứng thực token là đăng nhập người dùng. Chúng ta cũng không muốn những trường hợp mà token đã được gửi 3 ngày trước vẫn dùng được để đăng nhập.

Đăng kí ngay để khai phá những công thức tối ưu hóa PHP & PHP 7
Đăng kí ngay để khai phá những công thức tối ưu hóa PHP & PHP 7

Chứng thực Token Validation và Authentication

Giờ thì chúng ta đã có URL, hãy tạo route và controller action để xử lý chuỗi sự kiện xảy ra khi người dùng click vào URL từ email:

app/Http/routes.php

Hãy tạo action authenticate trong MagicLoginController. Chúng ta sẽ xác minh người dùng ngay trong method này. Chúng ta sẽ kéo token vào method authenticate thông qua Route Model Binding. Chúng ta sau đó sẽ grab người dùng từ token. Chú ý rằng chúng ta phải triệu tập Auth facade trong controller để có thể dùng method Auth:

app/Http/Controllers/Auth/MagicLoginController.php

Sau đó, trong UserToken class, đặt route key name chúng ta muốn. Trong trường hợp này, chính là token:

App/UserToken.php

Và xong rồi. Người dùng có thể đăng nhập được. Lưu ý, sau khi đăng nhập người dùng, chúng ta phải xóa token vì ta không muốn quá tải table user_tokens với các token đã qua sử dụng.

Bước tiếp theo là kiểm tra độ xác thực của token. Với ứng dụng này, chúng ta sẽ thiết đặt magic link hết hạn sau 5 phút. Ta sẽ cần đến thư viện Carbon để theo dõi thời gian giữa lúc tạo token và thời gian hiện tại.

Trong model UserToken, chúng ta sẽ tạo hai method: isExpired và belongstoEmail để kiểm tra độ xác thực của token. Chú ý, bước chứng thực belongsToEmail chỉ là bước dự phòng để đảm bảo token thực sự thuộc về địa chỉ email đó:

App/UserToken.php

 

Hãy call các method trên token instance trong method authenticate trong MagicLoginController:

app/Http/Controllers/Auth/MagicLoginController.php

 

Lời kết

Như vậy, chúng ta đã thành công với lớp đăng nhập không password bên cạnh cách xác minh truyền thống. Nhiều người cho rằng cách này sẽ mất nhiều thời gian hơn cách đăng nhập bằng password thông thường, nhưng dùng password manager liệu có nhanh hơn chăng?

Tuy vậy, hệ thống không-password không phải ở đâu cũng hoạt động được, nếu bạn có session timeout periods ngắn hoặc đòi hỏi người dùng phải đăng nhập thường xuyên, cách này tỏ ra khá dùng dằng. Cũng thật may, có rất ít trang đi theo hướng này.

Techtalk via Sitepoint

CHIA SẺ