Passport.js RESTh auth


155

Làm thế nào để một người xử lý xác thực (ví dụ như cục bộ và Facebook) bằng cách sử dụng Passport.js, thông qua API RESTful thay vì qua giao diện web?

Các mối quan tâm cụ thể đang xử lý việc chuyển dữ liệu từ các cuộc gọi lại sang phản hồi RESTful (JSON) so với sử dụng một res.send điển hình ({data: req.data}), thiết lập điểm cuối đăng nhập / đăng nhập chuyển hướng đến Facebook (/ đăng nhập không thể được truy cập qua AJAX, vì đó không phải là phản hồi JSON - nó là một chuyển hướng đến Facebook với một cuộc gọi lại).

Tôi đã tìm thấy https://github.com/halrobertson/test-restify-passport-facebook , nhưng tôi gặp khó khăn khi hiểu nó.

Hơn nữa, làm thế nào để.j.j lưu trữ thông tin xác thực auth? Máy chủ (hoặc là dịch vụ?) Được hỗ trợ bởi MongoDB và tôi mong muốn thông tin đăng nhập (đăng nhập và băm muối của pw) sẽ được lưu trữ ở đó, nhưng tôi không biết liệu Passport.js có loại khả năng này không.


Vì bạn chưa quen với Node, hãy bắt đầu dễ dàng và kiểm tra ứng dụng ví dụ cho passport-facebook. Sau khi bạn làm việc đó, bước tiếp theo là bắt đầu hiểu cách thức hoạt động của Hộ chiếu và cách lưu trữ thông tin đăng nhập. Kết nối nó để Restify ( xem ở đây để biết phiên bản cập nhật của phiên bản bạn đề cập) sẽ là một trong những bước cuối cùng (hoặc bạn có thể triển khai giao diện REST trong Express).
robertklep

Câu trả lời:


312

Có rất nhiều câu hỏi được đặt ra ở đây, và dường như mặc dù các câu hỏi được đặt ra trong bối cảnh của Node và Passport.js, các câu hỏi thực sự liên quan đến quy trình làm việc hơn là cách thực hiện điều này với một công nghệ cụ thể.

Hãy sử dụng thiết lập ví dụ @Keith, sửa đổi một chút để tăng cường bảo mật:

  • Máy chủ web https://example.comphục vụ một ứng dụng khách Javascript duy nhất
  • Dịch vụ web RESTful tại https://example.com/apicung cấp hỗ trợ máy chủ cho ứng dụng khách phong phú
  • Máy chủ được triển khai trong Node và Passport.js.
  • Máy chủ có cơ sở dữ liệu (bất kỳ loại nào) với bảng "người dùng".
  • Tên người dùng / mật khẩu và Facebook Connect được cung cấp dưới dạng tùy chọn xác thực
  • Máy khách phong phú biến các yêu cầu REST thành https://example.com/api
  • Có thể có các khách hàng khác (ví dụ: ứng dụng điện thoại) sử dụng dịch vụ web tại https://example.com/apinhưng không biết về máy chủ web tại https://example.com.

Lưu ý rằng tôi đang sử dụng HTTP an toàn. Theo ý kiến ​​của tôi, điều này là bắt buộc đối với bất kỳ dịch vụ nào có sẵn ở dạng mở, vì thông tin nhạy cảm như mật khẩu và mã thông báo ủy quyền đang truyền giữa máy khách và máy chủ.

Xác thực tên người dùng / mật khẩu

Trước tiên hãy xem cách xác thực cũ đơn giản hoạt động.

  • Người dùng kết nối với https://example.com
  • Máy chủ phục vụ một ứng dụng Javascript phong phú hiển thị trang ban đầu. Ở đâu đó trong trang có một hình thức đăng nhập.
  • Nhiều phần của ứng dụng một trang này chưa được điền dữ liệu do người dùng không đăng nhập. Tất cả các phần này có trình nghe sự kiện trong sự kiện "đăng nhập". Tất cả điều này là công cụ phía khách hàng, máy chủ không biết về những sự kiện này.
  • Người dùng nhập thông tin đăng nhập và mật khẩu của mình và nhấn nút gửi, điều này kích hoạt trình xử lý Javascript để ghi lại tên người dùng và mật khẩu trong các biến phía máy khách. Sau đó, trình xử lý này kích hoạt sự kiện "đăng nhập". Một lần nữa, đây là tất cả hành động phía khách hàng, thông tin đăng nhập chưa được gửi đến máy chủ .
  • Người nghe của sự kiện "đăng nhập" được gọi. Mỗi trong số này hiện cần gửi một hoặc nhiều yêu cầu tới API RESTful tại https://example.com/apiđể lấy dữ liệu cụ thể của người dùng để hiển thị trên trang. Mỗi yêu cầu họ gửi đến dịch vụ web sẽ bao gồm tên người dùng và mật khẩu, có thể dưới dạng xác thực HTTP Basic , vì dịch vụ là RESTful không được phép duy trì trạng thái máy khách từ yêu cầu này sang yêu cầu tiếp theo. Vì dịch vụ web trên HTTP an toàn, mật khẩu được mã hóa an toàn trong quá trình vận chuyển.
  • Dịch vụ web https://example.com/apinhận được một loạt các yêu cầu riêng lẻ, mỗi yêu cầu có thông tin xác thực. Tên người dùng và mật khẩu trong mỗi yêu cầu được kiểm tra đối với cơ sở dữ liệu người dùng và nếu tìm thấy chính xác, chức năng được yêu cầu sẽ thực thi và dữ liệu được trả về máy khách ở định dạng JSON. Nếu tên người dùng và mật khẩu không khớp, một lỗi sẽ được gửi đến máy khách dưới dạng mã lỗi 401 HTTP.
  • Thay vì buộc khách hàng gửi tên người dùng và mật khẩu với mỗi yêu cầu, bạn có thể có chức năng "get_access_token" trong dịch vụ RESTful của mình lấy tên người dùng và mật khẩu và trả lời bằng mã thông báo, đó là một loại băm mật mã duy nhất và đã hết hạn ngày gắn liền với nó. Các mã thông báo này được lưu trữ trong cơ sở dữ liệu với mỗi người dùng. Sau đó, khách hàng gửi mã thông báo truy cập trong các yêu cầu tiếp theo. Mã thông báo truy cập sau đó sẽ được xác thực dựa trên cơ sở dữ liệu thay vì tên người dùng và mật khẩu.
  • Các ứng dụng khách không có trình duyệt như ứng dụng điện thoại làm tương tự như trên, họ yêu cầu người dùng nhập thông tin đăng nhập của họ, sau đó gửi cho họ (hoặc mã thông báo truy cập được tạo từ họ) với mọi yêu cầu tới dịch vụ web.

Điểm quan trọng lấy đi từ ví dụ này là các dịch vụ web RESTful yêu cầu xác thực với mọi yêu cầu .

Một lớp bảo mật bổ sung trong kịch bản này sẽ thêm ủy quyền ứng dụng khách bên cạnh xác thực người dùng. Ví dụ: nếu bạn có ứng dụng web, ứng dụng iOS và Android đều sử dụng dịch vụ web, bạn có thể muốn máy chủ biết ai trong số ba ứng dụng khách của một yêu cầu cụ thể, bất kể người dùng được xác thực là ai. Điều này có thể cho phép dịch vụ web của bạn hạn chế các chức năng nhất định đối với các khách hàng cụ thể. Đối với điều này, bạn có thể sử dụng các khóa và bí mật API, xem câu trả lời này để biết một số ý tưởng về điều đó.

Xác thực Facebook

Quy trình làm việc ở trên không hoạt động để kết nối Facebook vì đăng nhập qua Facebook có bên thứ ba, chính Facebook. Quy trình đăng nhập yêu cầu người dùng được chuyển hướng đến trang web của Facebook nơi thông tin đăng nhập được đưa ra ngoài tầm kiểm soát của chúng tôi.

Vậy hãy xem mọi thứ thay đổi như thế nào :.

  • Người dùng kết nối với https://example.com
  • Máy chủ phục vụ một ứng dụng Javascript phong phú hiển thị trang ban đầu. Ở đâu đó trong trang có một hình thức đăng nhập bao gồm nút "Đăng nhập bằng Facebook".
  • Người dùng nhấp vào nút "Đăng nhập bằng Facebook", đây chỉ là một liên kết chuyển hướng đến (ví dụ) https://example.com/auth/facebook.
  • Các https://example.com/auth/facebooktuyến đường được xử lý bởi passport.js (xem tài liệu )
  • Tất cả những gì người dùng thấy là trang thay đổi và hiện tại họ đang ở trong một trang được lưu trữ trên Facebook nơi họ cần đăng nhập và ủy quyền cho ứng dụng web của chúng tôi. Điều này là hoàn toàn ngoài tầm kiểm soát của chúng tôi.
  • Người dùng đăng nhập vào Facebook và cấp quyền cho ứng dụng của chúng tôi, do đó, Facebook hiện chuyển hướng trở lại URL gọi lại mà chúng tôi đã định cấu hình trong cài đặt Passport.js, theo ví dụ trong tài liệu này làhttps://example.com/auth/facebook/callback
  • Trình xử lý Passport.js cho https://example.com/auth/facebook/callbacktuyến sẽ gọi chức năng gọi lại nhận mã thông báo truy cập Facebook và một số thông tin người dùng từ Facebook, bao gồm cả địa chỉ email của người dùng.
  • Với email, chúng tôi có thể định vị người dùng trong cơ sở dữ liệu của mình và lưu trữ mã thông báo truy cập Facebook với nó.
  • Điều cuối cùng bạn làm trong cuộc gọi lại Facebook là chuyển hướng trở lại ứng dụng khách phong phú, nhưng lần này chúng ta cần chuyển tên người dùng và mã thông báo truy cập cho khách hàng để có thể sử dụng chúng. Điều này có thể được thực hiện theo một số cách. Ví dụ: các biến Javascript có thể được thêm vào trang thông qua công cụ mẫu phía máy chủ hoặc nếu không thì có thể trả lại cookie với thông tin này. (cảm ơn @RyanKimber đã chỉ ra các vấn đề bảo mật khi truyền dữ liệu này trong URL, như tôi đã đề xuất ban đầu).
  • Vì vậy, bây giờ chúng tôi bắt đầu ứng dụng một trang một lần nữa, nhưng khách hàng có tên người dùng và mã thông báo truy cập.
  • Ứng dụng khách có thể kích hoạt sự kiện "đăng nhập" ngay lập tức và để các phần khác nhau của ứng dụng yêu cầu thông tin mà họ cần từ dịch vụ web.
  • Tất cả các yêu cầu được gửi https://example.com/apisẽ bao gồm mã thông báo truy cập Facebook để xác thực hoặc mã thông báo truy cập của ứng dụng được tạo từ mã thông báo của Facebook thông qua chức năng "get_access_token" trong API REST.
  • Các ứng dụng không phải trình duyệt gặp khó khăn hơn một chút ở đây, vì OAuth yêu cầu trình duyệt web để đăng nhập. Để đăng nhập từ ứng dụng trên điện thoại hoặc máy tính để bàn, bạn sẽ cần khởi động trình duyệt để chuyển hướng sang Facebook, và thậm chí tệ hơn, bạn cần một cách để trình duyệt chuyển mã thông báo truy cập Facebook trở lại ứng dụng thông qua một số cơ chế.

Tôi hy vọng điều này trả lời hầu hết các câu hỏi. Tất nhiên, bạn có thể thay thế Facebook bằng Twitter, Google hoặc bất kỳ dịch vụ xác thực dựa trên OAuth nào khác.

Tôi muốn biết liệu ai đó có cách đơn giản hơn để giải quyết vấn đề này.


5
Cảm ơn bạn đã phản hồi chi tiết của bạn. Chỉ một câu hỏi: bạn nói điều đó Every single request they send to the web service will include the username and password, và bạn nói you can have a "get_access_token" function in your RESTful service. Có vẻ mâu thuẫn khi nói rằng REST cần phải không trạng thái, nhưng lưu trữ phía máy chủ mã thông báo truy cập là ổn, vì hành động lưu trữ mã thông báo truy cập có nghĩa là máy chủ hiện đang ở trạng thái. Tôi sẽ đánh giá cao bất kỳ sự làm rõ hoặc biện minh liên quan đến điều này. Cảm ơn! :)
ryanrhee

6
Khi tôi nghĩ về yêu cầu không trạng thái, tôi nghĩ về trạng thái cho một phiên cụ thể với khách hàng. Tôi không thấy việc lưu trữ dữ liệu được liên kết với người dùng trong cơ sở dữ liệu, như mật khẩu hoặc mã thông báo truy cập là "trạng thái", ít nhất không phải là "trạng thái phiên". Máy chủ của bạn rõ ràng cần biết người dùng là ai và mật khẩu của họ, nhưng đây là dữ liệu ứng dụng không liên quan đến phiên. Điều đó nói rằng, rất nhiều người sử dụng cookie trong các dịch vụ được cho là RESTful, vì vậy mức độ nghiêm ngặt mà bạn muốn tuân thủ đặc tả REST thực sự tùy thuộc vào mỗi người triển khai.
Miguel

1
@Dexter: Trong trường hợp đăng nhập truyền thống, người dùng nhập tên người dùng và mật khẩu vào một biểu mẫu và khi anh ta nhấn nút Gửi, thông tin này được đăng lên máy chủ web. Trong trường hợp này, điều này không xảy ra, người dùng sẽ điền vào biểu mẫu và khi anh ta nhấn Gửi trình xử lý Javascript (một sự kiện onClick trong nút gửi) sẽ ghi lại dữ liệu và giữ nó trong ngữ cảnh máy khách. Tôi không có ví dụ sẵn sàng để trình bày, nhưng hãy xem phần thứ hai của hướng dẫn này trên blog của tôi, nơi tôi sẽ trình bày cách thực hiện: blog.miguelgrinberg.com/post/ Kẻ
Miguel

2
Đây là một suy nghĩ rất tốt viết lên, nhưng có một lỗi giám sát hoặc lỗi quan trọng. Khi xử lý thông tin đăng nhập Facebook (hoặc Github, twitter, v.v.), việc chuyển mã thông báo lại cho khách hàng trong cookie và sau đó xóa cookie ở phía máy khách sẽ được ưu tiên hơn nhiều. Truyền mã thông báo dưới dạng một phần của chuỗi URL sẽ thêm URL này vào lịch sử trình duyệt và (nếu mọi thứ được xử lý không đúng cách) có thể dẫn đến URL này được trình duyệt yêu cầu. Hoặc là làm cho mã thông báo hiển thị. Bất cứ ai sau đó sao chép URL đều có thể giả mạo người dùng này trong ứng dụng của bạn.
Ryan Kimber

1
@Nathan: auth cơ bản trên https là an toàn, vì vậy, đó là một cơ chế chấp nhận được.
Miguel

11

Tôi đánh giá rất cao lời giải thích của @ Miguel với dòng chảy hoàn chỉnh trong từng trường hợp, nhưng tôi muốn thêm một số vào phần Xác thực Facebook.

Facebook cung cấp SDK Javascript mà bạn có thể sử dụng để nhận mã thông báo truy cập trực tiếp ở đầu máy khách, sau đó được chuyển đến máy chủ và được sử dụng để tiếp tục lấy tất cả thông tin người dùng từ Facebook. Vì vậy, về cơ bản, bạn không cần bất kỳ chỉ đạo lại.

Hơn nữa, bạn cũng có thể sử dụng cùng một điểm cuối API cho các ứng dụng di động. Chỉ cần sử dụng SDK Android / iOS cho Facebook, lấy access_token của Facebook ở đầu máy khách và chuyển nó đến máy chủ.

Về bản chất không trạng thái như được giải thích, khi get_access_token được sử dụng để tạo mã thông báo và được chuyển đến máy khách, mã thông báo này cũng được lưu trữ trên máy chủ. Vì vậy, nó tốt như mã thông báo phiên và tôi tin rằng điều này làm cho nó có trạng thái?

Chỉ 2 xu của tôi ..


1
Điều này rất quan trọng, bởi vì cách này bạn có thể thực hiện auth chỉ một trang bằng cách sử dụng facebook. Mã thông báo được bảo mật an toàn dưới dạng phiên xác thực, điểm khác biệt duy nhất là mã thông báo có thể được chuyển sang tên miền khác và phiên chỉ được sử dụng trong một tên miền cụ thể. Khi sử dụng expressjs và hộ chiếu, bạn có thể tạo một api lưu trạng thái và sử dụng phiên auth.
jperelli

Api javascript là tuyệt vời nếu bạn muốn xác thực người dùng để thực hiện các hành động chống lại Facebook, nhưng bản thân nó vô dụng nếu bạn muốn xác thực người dùng đối với máy chủ / cơ sở dữ liệu của bạn theo như tôi có thể nói.
James Westgate

4
Bạn cũng có thể sử dụng phương pháp được mô tả bởi Miguel ở trên, nhưng sau đó phát hành mã thông báo JWT của riêng bạn dưới dạng cookie khi chuyển hướng ứng dụng khách trong cuộc gọi lại xác thực. Điều này cho phép tốt nhất của cả hai thế giới - ứng dụng một trang của bạn chỉ có thể quan tâm đến một loại xác thực (JWT), trong khi vẫn giữ cùng mức bảo mật và cung cấp tính linh hoạt để hỗ trợ bất kỳ thông tin đăng nhập xã hội nào mà không cần phải sử dụng API JavaScript cụ thể cho từng mạng xã hội (Facebook, Twitter, LinkedIn, Google, v.v.). Nó cũng cho phép bạn duy trì hỗ trợ kiểu AJAX cho tên người dùng / mật khẩu và truy cập REST.
Ryan Kimber

Facebook Javascript SDK hiện không hoạt động với Chrome iOS. Có lẽ một vấn đề cho một số.
demisx

@RyanKimber bạn có thể viết một repo git nhỏ hoặc tương tự trong đó điều này được thực hiện như một ví dụ tôi hoàn toàn bị mắc kẹt
Simon Dragsbæk

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.