Ngay từ lúc đầu làm blog, tôi đã nghĩ đến trường hợp người đọc có thể để lại bình luận trong bài viết. Việc phát triển một module đăng ký/đăng nhập không nằm ngoài dự tính. Nhưng thay vì làm thêm màn đăng ký, tôi dành ít thời gian để suy nghĩ về việc làm cách nào để rút ngắn thời gian đăng nhập xuống. Vì giả dụ người dùng chưa có tài khoản, họ muốn thảo luận ngay lập tức mà lại bắt phải nhập nhiều thông tin trong bước đăng ký thì chắc chắn ít nhiều, họ sẽ nao núng và thôi không nói nữa. Cho nên, thời gian là cực kỳ quan trọng trong tình huống này, càng phức tạp, càng tốn thời gian, bạn đọc của tôi sẽ chẳng buồn làm gì thêm nữa. Đối tượng tôi nhắm đến là "dân dev", mà đã là dev thì chắc hẳn phải có tài khoản Google hoặc Github.
Cả hai đều cung cấp cơ chế Oauth, nghĩa là cho phép chúng ta tạo các ứng dụng trên nền tảng và thông qua nó, xác thực & ủy quyền người dùng. Nếu làm theo cách này, đồng nghĩa với việc người dùng chỉ cần thao tác tối thiểu 3 cú bấm chuột là đã có thể đăng nhập được vào trang web. Đó là một sự tiện lợi cho cả hai, khi tôi không cần phải thêm bước đăng ký còn người dùng chỉ cần có tài khoản Google hay Github là đã có thể đăng nhập.
Cứ ngỡ sau một lần viết, code chạy mãi mãi, chức năng này hoạt động trong một khoảng thời gian cho đến khi tôi phát hiện ra điều bất thường. Cụ thể là trường hợp một người dùng lại bình luận được dưới một danh nghĩa của một người dùng khác!?. Họ đang đăng nhập bằng tài khoản Github A nhưng bình luận lại hiện ra thông tin của họ dưới tài khoản B. Ngay lập tức, tôi tạm thời tắt tính năng đăng nhập bằng Github và điều tra nguyên nhân. Sau khoảng vài ngày, tôi dường như phát hiện ra một sai lầm trong mã của mình và những gì sau đó là sắp xếp thời gian để sửa lỗi.
Cùng lúc đó, Google có gửi nhiều email thông báo đến các tài khoản đang sử dụng dịch vụ xác thực là ngày 31/03/2023, họ sẽ chính thức ngừng cung cấp API đăng nhập bằng tài khoản Google theo cách cũ. Buộc quản trị trang web phải chuyển sang một API mới để tiếp tục sử dụng. Thời đến cản không kịp thế là tôi ngồi lại sửa lại cả hai cùng một lúc.
Trong quá trình sửa lỗi, tôi có rút ra được một vài bài học và cảm thấy có thể chia sẻ được cho bạn đọc, để nếu như bạn có đang xử lý giống tôi thì hãy chú ý, hoặc cho những bạn nào chuẩn bị làm tính năng này thì cũng có thêm kinh nghiệm xử lý.
Đầu tiên, tôi xin nhắc lại một chút về cơ chế Oauth xem nó là cái gì. Hẳn là bạn đọc đã nghe hoặc quá quen thuộc với khái niệm của Oauth, hoặc chỉ cần hiểu đơn giản bất kỳ ứng dụng phần mềm nào mà có mục "đăng nhập bằng tài khoản Google/Github"… là đã thấy sự hiện diện của Oauth trong đó. Nếu bấm vào nút, một cửa sổ trình duyệt hiện ra hỏi bạn có đồng ý cho phép ứng dụng ABC truy cập vào tài khoản hay không. Nếu có, bạn được đăng nhập, còn nếu không thì chẳng có gì xảy ra cả.
Cơ chế của Oauth có thể phức tạp, trong bài viết ngắn này tôi không tiện nêu ra, thay vào đó bạn đọc có thể tìm đọc các bài mô tả về luồng, cách hoạt động của Oauth trên mạng. Nhưng để nói vắn tắt, quá trình Oauth sẽ bao gồm 2 bước là xác thực & ủy quyền.
Nếu để ý, mỗi khi bấm vào nút Đăng nhập bằng Google, sẽ có một màn hình hiện ra bảo rằng "Ứng dụng ABC đang yêu cầu quyền truy cập vào Email/Drive/Docs…" và hỏi bạn có cho phép không? Nếu chọn "có" thì tức là bạn đã ủy quyền cho ứng dụng ABC có những quyền hạn như đọc/ghi vào các dịch vụ mà nó vừa hỏi. Hay nói sâu hơn, lúc đó lập trình viên của ứng dụng ABC sẽ lấy được Access Token tài khoản của bạn để qua đó, gọi các API được cung cấp để truy cập vào chúng.
Còn quá trình trước khi vào được đến màn hình ủy quyền là quá trình xác thực. Hiểu đơn giản, quá trình đăng nhập vào tài khoản là quá trình xác thực. Xác thực bạn là chủ tài khoản trước bằng cách đăng nhập rồi ủy quyền sau bằng cách cấp quyền.
Lỗi tôi gặp phải là đã quá vội vàng nên lấy nhầm hoặc không lấy được địa chỉ email của tài khoản. Cụ thể là đối với Github, sau khi tôi lấy được Access Token của người dùng, tôi sẽ dùng nó để gọi đến một API là /user
để lấy thông tin người dùng. Trong dữ liệu trả về, ngoài một vài thông tin như tên, avatar… thì còn có cả email, không chần chừ gì tôi lấy ngay email này và coi nó như là email đăng nhập của người dùng. Đó là gốc rễ của vấn đề.
Bởi vì email trả về trong API này nó mang tính thông tin là chủ yếu, nói cách khác, đó không phải là email thật của người dùng. Muốn lấy được email thật, buộc bạn phải xin quyền đọc được địa chỉ email ở bước ủy quyền. Sau đó, tiếp tục gọi đến một API khác để lấy danh sách địa chỉ email, trong đó bao gồm email chính (primary) và nhiều email phụ nếu có, bao gồm cả các email mà họ chưa xác thực (confirm).
Có thể dạo gần đây, khi mà vấn đề rò rỉ thông tin người dùng ngày càng được quan tâm thì các dịch vụ cung cấp xác thực & ủy quyền họ càng làm chặt hơn về đòi hỏi quyền đọc được địa chỉ email của người dùng.
Tương tự đối với Google, sau khi nhận được mã ID Google Token. Tôi sẽ xác thực đoạn mã đó và giải mã bên trong nó chứa địa chỉ email của người dùng, bao gồm một trường đánh dấu là email đó đã được xác thực hay chưa (email_verified). Thực tế, tôi không hiểu rõ việc đánh dấu tài khoản là đã được xác thực lắm, nhưng nếu việc Google hoặc Github đánh dấu một tài khoản là "chưa xác thực" thì rõ ràng có một vấn đề gì đó mà tôi không nên "tin" vào những tài khoản như vậy. Vì thế tôi nghĩ cách tốt nhất là từ chối đăng nhập với những trường hợp như vậy.
Sau khi biết được nguồn cơn của vấn đề, chúng ta sẽ tìm cách khắc phục. Như tôi đã trình bày như trên, đó là kiểm tra lại thật kỹ tài liệu để biết làm cách nào để lấy được email thật của người dùng chứ không phải là email mang tính chất thông tin hiển thị. Bên cạnh đó, tôi cũng nhấn mạnh hai lưu ý về việc xác thực & ủy quyền.
Đầu tiên, cũng là bước quan trọng nhất, chúng ta phải xin được đúng quyền (scope), trong đó bao gồm quyền đọc địa chỉ email. Ví dụ như trong Github, việc xin quyền email thể hiện ngay trong url chuyển hướng đăng nhập. Bạn đọc xem thêm ở Request a user's GitHub identity.
Thứ hai, xác định được vị trí để lấy ra được email chính. Ví dụ như trong Github, cần gọi một API lấy danh sách địa chỉ email và kiểm tra xem đâu là email chính, đã xác thực hay là chưa. Bạn đọc xem thêm ở List email addresses for the authenticated user.
Còn đối với Google, họ đang khuyến kích chúng ta chuyển sang Sign in with Google for Web, bởi vì cách cũ Google Sign-In for Web sẽ bị ngừng vào ngày 31/03 tới đây.
Oauth là một cách nhanh chóng để người dùng có thể đăng nhập vào trang web của bạn bằng tài khoản Google, Github… mà không cần phải qua nhiều bước đăng ký rườm rà. Tuy nhiên, khi sử dụng Oauth cần chú ý đến việc xác thực được địa chỉ email của người dùng để tránh những lỗi lầm tai hại như trong bài viết nhé.
Xin chào, tôi tên là Hoài - một anh Dev kể chuyện bằng cách viết ✍️ và làm sản phẩm 🚀. Với nhiều năm kinh nghiệm lập trình, tôi đã đóng góp một phần công sức cho nhiều sản phẩm mang lại giá trị cho người dùng tại nơi đang làm việc, cũng như cho chính bản thân. Sở thích của tôi là đọc, viết, nghiên cứu... Tôi tạo ra trang Blog này với sứ mệnh mang đến những bài viết chất lượng cho độc giả của 2coffee.dev.Hãy theo dõi tôi qua các kênh LinkedIn, Facebook, Instagram, Telegram.
Bình luận (1)