passport.js RESTful auth


155

W jaki sposób można obsługiwać uwierzytelnianie (na przykład lokalne i Facebook) za pomocą passport.js, za pośrednictwem interfejsu API RESTful zamiast interfejsu internetowego?

Konkretne obawy dotyczą obsługi przekazywania danych z wywołań zwrotnych do odpowiedzi RESTful (JSON) w porównaniu z użyciem typowego res.send ({data: req.data}), konfigurowania punktu końcowego początkowego / logowania, który przekierowuje do Facebooka (/ login nie może być dostępne przez AJAX, ponieważ nie jest to odpowiedź JSON - jest to przekierowanie do Facebooka z wywołaniem zwrotnym).

Znalazłem https://github.com/halrobertson/test-restify-passport-facebook , ale nie rozumiem tego.

Co więcej, w jaki sposób passport.js przechowuje dane uwierzytelniające? Serwer (czy jest to usługa?) Jest obsługiwany przez MongoDB i spodziewałbym się, że będą tam przechowywane poświadczenia (login i zasolony skrót pw), ale nie wiem, czy passport.js ma tego typu możliwości.


Ponieważ jesteś nowy w Node, zacznij od łatwego i sprawdź przykładową aplikację dla passport-facebook. Gdy już to zrobisz, następnym krokiem jest zrozumienie, jak działa usługa Passport i jak przechowuje poświadczenia. Podłączenie go do Restify ( zobacz tutaj zaktualizowaną wersję tej, o której wspomniałeś) byłoby jednym z ostatnich kroków (lub możesz zaimplementować interfejs REST w Express).
robertklep

Odpowiedzi:


312

Zadawanych jest tutaj wiele pytań i wydaje się, że chociaż pytania są zadawane w kontekście Node i passport.js, prawdziwe pytania dotyczą bardziej przepływu pracy niż tego, jak to zrobić za pomocą określonej technologii.

Użyjmy przykładowej konfiguracji @Keith, nieco zmodyfikowanej w celu zwiększenia bezpieczeństwa:

  • Serwer sieci Web pod adresem https://example.comobsługuje pojedynczą stronę aplikacji klienckiej Javascript
  • Usługa internetowa RESTful pod adresem https://example.com/apizapewnia obsługę serwera dla bogatej aplikacji klienckiej
  • Serwer zaimplementowany w Node i passport.js.
  • Serwer posiada bazę danych (dowolnego rodzaju) z tabelą "użytkowników".
  • Nazwa użytkownika / hasło i Facebook Connect są oferowane jako opcje uwierzytelniania
  • Bogaty klient wysyła żądania REST do https://example.com/api
  • Mogą istnieć inni klienci (na przykład aplikacje na telefon), którzy korzystają z usługi internetowej pod adresem, https://example.com/apiale nie wiedzą o serwerze WWW pod adresem https://example.com.

Pamiętaj, że używam bezpiecznego protokołu HTTP. Moim zdaniem jest to obowiązkowe dla każdej usługi, która jest dostępna na wolnym powietrzu, ponieważ poufne informacje, takie jak hasła i tokeny autoryzacyjne, są przesyłane między klientem a serwerem.

Uwierzytelnianie nazwy użytkownika / hasła

Przyjrzyjmy się najpierw, jak działa zwykłe stare uwierzytelnianie.

  • Użytkownik łączy się z https://example.com
  • Serwer obsługuje bogatą aplikację JavaScript, która renderuje stronę początkową. Gdzieś na stronie znajduje się formularz logowania.
  • Wiele sekcji tej pojedynczej strony aplikacji nie zostało wypełnionych danymi, ponieważ użytkownik nie jest zalogowany. Wszystkie te sekcje mają detektor zdarzeń na zdarzeniu „logowania”. To wszystko po stronie klienta, serwer nie wie o tych zdarzeniach.
  • Użytkownik wprowadza swój login i hasło i naciska przycisk przesyłania, co powoduje, że moduł obsługi Javascript rejestruje nazwę użytkownika i hasło w zmiennych po stronie klienta. Następnie ten program obsługi wyzwala zdarzenie „login”. Ponownie, jest to akcja po stronie klienta, poświadczenia nie zostały jeszcze wysłane na serwer .
  • Wywoływane są detektory zdarzenia „login”. Każdy z nich musi teraz wysłać jedno lub więcej żądań do RESTful API pod adresem, https://example.com/apiaby uzyskać dane użytkownika do wyświetlenia na stronie. Każde żądanie wysłane do usługi sieciowej będzie zawierało nazwę użytkownika i hasło, prawdopodobnie w formie podstawowego uwierzytelniania HTTP , ponieważ usługa RESTful nie może utrzymywać stanu klienta od jednego żądania do następnego. Ponieważ usługa sieciowa korzysta z bezpiecznego protokołu HTTP, hasło jest bezpiecznie szyfrowane podczas przesyłania.
  • Usługa internetowa pod adresem https://example.com/apiodbiera zestaw indywidualnych żądań, z których każde zawiera informacje uwierzytelniające. Nazwa użytkownika i hasło w każdym żądaniu są sprawdzane w bazie danych użytkowników i jeśli okaże się, że są prawidłowe, wykonywana jest żądana funkcja, a dane są zwracane do klienta w formacie JSON. Jeśli nazwa użytkownika i hasło nie są zgodne, do klienta wysyłany jest błąd w postaci kodu błędu 401 HTTP.
  • Zamiast zmuszać klientów do wysyłania nazwy użytkownika i hasła przy każdym żądaniu, możesz mieć funkcję „get_access_token” w swojej usłudze RESTful, która pobiera nazwę użytkownika i hasło i odpowiada tokenem, który jest rodzajem kryptograficznego skrótu, który jest unikalny i ma pewną datę ważności data z tym związana. Te tokeny są przechowywane w bazie danych z każdym użytkownikiem. Następnie klient wysyła token dostępu w kolejnych żądaniach. Token dostępu zostanie następnie zweryfikowany w bazie danych zamiast nazwy użytkownika i hasła.
  • Aplikacje klienckie inne niż przeglądarki, takie jak aplikacje na telefon, robią to samo, co powyżej, proszą użytkownika o wprowadzenie swoich poświadczeń, a następnie wysyłają je (lub wygenerowany z nich token dostępu) przy każdym żądaniu do usługi internetowej.

Ważnym punktem wyjścia z tego przykładu jest to, że usługi sieciowe obsługujące REST wymagają uwierzytelniania przy każdym żądaniu .

Dodatkowa warstwa zabezpieczeń w tym scenariuszu dodałaby autoryzację aplikacji klienckiej oprócz uwierzytelniania użytkownika. Na przykład, jeśli masz klienta internetowego, aplikacje iOS i Android korzystające z usługi internetowej, możesz chcieć, aby serwer wiedział, który z trzech klientów danego żądania jest, niezależnie od tego, kto jest uwierzytelnionym użytkownikiem. Może to umożliwić usłudze internetowej ograniczenie niektórych funkcji do określonych klientów. W tym celu możesz użyć kluczy i kluczy API, zobacz tę odpowiedź, aby uzyskać kilka pomysłów na ten temat.

Uwierzytelnianie na Facebooku

Powyższy przepływ pracy nie działa w przypadku połączenia z Facebookiem, ponieważ logowanie za pośrednictwem Facebooka ma stronę trzecią, sam Facebook. Procedura logowania wymaga przekierowania użytkownika do witryny Facebooka, gdzie dane uwierzytelniające są wprowadzane poza naszą kontrolą.

Zobaczmy więc, jak to się zmienia:

  • Użytkownik łączy się z https://example.com
  • Serwer obsługuje bogatą aplikację JavaScript, która renderuje stronę początkową. W niektórych miejscach na stronie znajduje się formularz logowania, który zawiera przycisk „Zaloguj się przez Facebook”.
  • Użytkownik klika przycisk „Zaloguj się przez Facebooka”, który jest po prostu linkiem przekierowującym (na przykład) https://example.com/auth/facebook.
  • https://example.com/auth/facebookTrasy obsługiwane przez passport.js (patrz dokumentacja )
  • Jedyne, co użytkownik widzi, to zmiana strony i teraz znajduje się na stronie hostowanej na Facebooku, gdzie musi się zalogować i autoryzować naszą aplikację internetową. To jest całkowicie poza naszą kontrolą.
  • Użytkownik loguje się na Facebooku i udziela pozwolenia naszej aplikacji, więc Facebook przekierowuje teraz z powrotem do adresu URL wywołania zwrotnego, który skonfigurowaliśmy w konfiguracji passport.js, co zgodnie z przykładem w dokumentacji tohttps://example.com/auth/facebook/callback
  • Program obsługi passport.js dla https://example.com/auth/facebook/callbacktrasy wywoła funkcję zwrotną, która odbiera token dostępu Facebooka i niektóre informacje o użytkowniku z Facebooka, w tym adres e-mail użytkownika.
  • Za pomocą e-maila możemy zlokalizować użytkownika w naszej bazie danych i przechowywać wraz z nim token dostępu do Facebooka.
  • Ostatnią rzeczą, jaką robisz w wywołaniu zwrotnym Facebooka, jest przekierowanie z powrotem do bogatej aplikacji klienta, ale tym razem musimy przekazać klientowi nazwę użytkownika i token dostępu, aby mógł z nich korzystać. Można to zrobić na wiele sposobów. Na przykład zmienne JavaScript można dodać do strony za pośrednictwem mechanizmu szablonów po stronie serwera, albo też można zwrócić plik cookie z tymi informacjami. (podziękowania dla @RyanKimber za wskazanie problemów bezpieczeństwa podczas przekazywania tych danych w adresie URL, jak początkowo sugerowałem).
  • Więc teraz ponownie uruchamiamy aplikację z jedną stroną, ale klient ma nazwę użytkownika i token dostępu.
  • Aplikacja kliencka może natychmiast wywołać zdarzenie „logowanie” i pozwolić różnym częściom aplikacji zażądać informacji, których potrzebują, z usługi sieciowej.
  • Wszystkie żądania wysyłane do https://example.com/apibędą zawierały token dostępu Facebook do uwierzytelniania lub token dostępu do aplikacji wygenerowany z tokena Facebooka za pośrednictwem funkcji „get_access_token” w REST API.
  • W przypadku aplikacji innych niż przeglądarki jest to nieco trudniejsze, ponieważ OAuth wymaga przeglądarki internetowej do logowania. Aby zalogować się z aplikacji na telefon lub komputer, musisz uruchomić przeglądarkę, aby wykonać przekierowanie do Facebooka, a co gorsza, Potrzebujesz sposobu, aby przeglądarka mogła przekazać token dostępu Facebooka z powrotem do aplikacji za pośrednictwem jakiegoś mechanizmu.

Mam nadzieję, że to odpowiada na większość pytań. Oczywiście możesz zastąpić Facebooka serwisem Twitter, Google lub inną usługą uwierzytelniania opartą na OAuth.

Chciałbym wiedzieć, czy ktoś ma prostszy sposób radzenia sobie z tym.


5
Dziękuję za szczegółową odpowiedź. Tylko jedno pytanie: mówisz to Every single request they send to the web service will include the username and password, a jednak mówisz you can have a "get_access_token" function in your RESTful service. Stwierdzenie, że REST musi być bezstanowy, wydaje się sprzeczne, ale przechowywanie tokenów dostępu po stronie serwera jest w porządku, ponieważ ten akt przechowywania tokenów dostępu oznacza, że ​​serwer jest teraz stanowy. Byłbym wdzięczny za wszelkie wyjaśnienia lub uzasadnienia w tej sprawie. Dzięki! :)
ryanrhee

6
Kiedy myślę o wymogu bezpaństwowości, myślę o stanie konkretnej sesji z klientem. Nie widzę przechowywania danych związanych z użytkownikiem w bazie danych, takich jak hasło lub token dostępu, jako „stan”, a przynajmniej nie jako „stan sesji”. Twój serwer musi oczywiście wiedzieć, kim są użytkownicy i ich hasła, ale są to dane aplikacji, które nie są powiązane z sesją. To powiedziawszy, wiele osób używa plików cookie w usługach rzekomo zgodnych z REST, więc to, jak rygorystycznie chcesz przestrzegać specyfikacji REST, zależy tak naprawdę od każdego implementującego.
Miguel

1
@Dexter: W przypadku tradycyjnego logowania użytkownik wprowadza nazwę użytkownika i hasło do formularza, a po naciśnięciu przycisku Prześlij informacje te są wysyłane na serwer WWW. W tym przypadku tak się nie dzieje, użytkownik wypełnia formularz, a po kliknięciu Prześlij moduł obsługi JavaScript (zdarzenie onClick w przycisku przesyłania) przechwytuje dane i zachowuje je w kontekście klienta. Nie mam gotowego przykładu do pokazania, ale uważaj na drugą część tego samouczka na moim blogu, gdzie pokażę, jak to się robi: blog.miguelgrinberg.com/post/ ...
Miguel

2
Jest to bardzo dobrze przemyślana treść, ale zawiera jedno ważne przeoczenie lub błąd. Podczas obsługi logowania do Facebooka (lub Github, Twittera itp.) Byłoby znacznie lepiej przekazać token z powrotem do klienta w pliku cookie, a następnie usunąć plik cookie po stronie klienta po jego wykryciu. Przekazanie tokena jako części ciągu adresu URL spowoduje dodanie tego adresu URL do historii przeglądarki i (jeśli coś zostanie obsłużone nieprawidłowo) może doprowadzić do zażądania tego adresu URL przez przeglądarkę. Albo sprawia, że ​​token jest widoczny. Każdy, kto następnie skopiował adres URL, mógł sfałszować tego użytkownika w Twojej aplikacji.
Ryan Kimber

1
@Nathan: podstawowe uwierzytelnianie przez https jest bezpieczne, więc tak, to akceptowalny mechanizm.
Miguel

11

Bardzo doceniam wyjaśnienie @ Miguel dotyczące pełnego przepływu w każdym przypadku, ale chciałbym dodać trochę w części dotyczącej uwierzytelniania na Facebooku.

Facebook zapewnia Javascript SDK, którego można użyć do uzyskania tokena dostępu bezpośrednio po stronie klienta, który jest następnie przesyłany do serwera i używany do dalszego pobierania wszystkich informacji o użytkowniku z Facebooka. Więc w zasadzie nie potrzebujesz żadnych przekierowań.

Co więcej, możesz użyć tego samego punktu końcowego API również dla aplikacji mobilnych. Po prostu użyj Android / iOS SDK dla Facebooka, uzyskaj dostęp do Facebooka po stronie klienta i przekaż go do serwera.

Jeśli chodzi o charakter bezstanowy, jak wyjaśniono, gdy get_access_token jest używany do generowania tokenu i przekazywany do klienta, ten token jest również przechowywany na serwerze. Więc jest tak dobry jak token sesji i uważam, że dzięki temu jest stanowy?

Tylko moje 2 centy ...


1
Jest to bardzo ważne, ponieważ w ten sposób możesz zrobić jednostronicową autoryzację AJAX za pomocą Facebooka. Token jest równie bezpieczny jak uwierzytelnianie sesji, jedyną różnicą jest to, że token można przekazać do innej domeny, a sesja jest używana tylko w jednej określonej domenie. Używając wyrażeń i paszportu, możesz utworzyć interfejs API, który zachowuje stan i korzystać z uwierzytelniania sesji.
jperelli

Interfejs API javascript jest świetny, jeśli chcesz uwierzytelnić użytkownika w celu wykonywania działań na Facebooku, ale sam w sobie bezużyteczny, jeśli chcesz zweryfikować użytkownika na serwerze / bazie danych, o ile wiem.
James Westgate,

4
Możesz także skorzystać z metody opisanej przez Miguela powyżej, ale następnie wystawić własny token JWT jako plik cookie podczas przekierowywania klienta w wywołaniu zwrotnym uwierzytelniania. Pozwala to na to, co najlepsze z obu światów - Twoja jednostronicowa aplikacja może martwić się tylko jednym typem uwierzytelniania (JWT), zachowując ten sam poziom bezpieczeństwa i zapewniając elastyczność obsługi dowolnego logowania społecznościowego bez konieczności korzystania z określone interfejsy API JavaScript dla każdej sieci społecznościowej (Facebook, Twitter, LinkedIn, Google itp.). Pozwala także zachować obsługę w stylu AJAX dla nazwy użytkownika / hasła i dostępu REST.
Ryan Kimber

Facebook Javascript SDK obecnie nie działa z Chrome iOS. Może dla niektórych problem.
demisx

@RyanKimber, czy możesz napisać małe repozytorium git lub coś podobnego, gdzie jest to zrobione, jako przykład,
jestem

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.