passport.js RESTful авт


155

Як можна обробляти автентифікацію (наприклад, локальну та Facebook), використовуючи passport.js, через API RESTful замість веб-інтерфейсу?

Особливі занепокоєння стосуються передачі даних з зворотних викликів у відповідь RESTful (JSON) проти використання типового res.send ({data: req.data}), встановлення початкової кінцевої точки / входу, яка переадресовує на Facebook (/ вхід не може бути доступ через AJAX, оскільки це не відповідь JSON - це переадресація на Facebook із зворотним дзвоном).

Я знайшов https://github.com/halrobertson/test-restify-passport-facebook , але у мене виникають проблеми з його розумінням.

Крім того, як passport.js зберігає автентифікаційні дані? Сервер (чи це послуга?) Підтримується MongoDB, і я очікую, що там будуть зберігатися облікові дані (логін та солоний хеш pw), але я не знаю, чи passport.js має такий тип можливостей.


Оскільки ви новачок в вузол, почати легко і перевірити приклад програми для passport-facebook. Після того, як ви працюєте, наступним кроком є ​​розуміння того, як працює паспорт і як він зберігає облікові дані. Підключення до Restify ( див. Тут оновлену версію тієї, яку ви згадуєте) - це один з останніх кроків (або ви можете реалізувати інтерфейс REST в Express).
robertklep

Відповіді:


312

Тут задають багато запитань, і, здається, навіть незважаючи на те, що запитання задаються в контексті Node та passport.js, справжні питання стосуються більше робочого процесу, ніж того, як це зробити за допомогою певної технології.

Давайте скористаємося прикладом налаштування @Keith, трохи зміненим для додаткової безпеки:

  • Веб-сервер https://example.comобслуговує одну сторінку клієнтської програми Javascript
  • Веб-сервіс RESTful у https://example.com/apiнадає серверну підтримку багатим клієнтським додаткам
  • Сервер реалізований в Node та passport.js.
  • Сервер має базу даних (будь-якого виду) з таблицею "користувачів".
  • Ім'я користувача / пароль та Facebook Connect пропонуються як параметри аутентифікації
  • Багатий клієнт робить REST-запити https://example.com/api
  • Можуть бути й інші клієнти (наприклад, програми телефону), які використовують веб-сервіс у, https://example.com/apiале не знають про веб-сервер за адресою https://example.com.

Зауважте, що я використовую захищений HTTP. На мою думку, це є обов'язковим для будь-якої служби, яка доступна відкрито, оскільки конфіденційна інформація, як паролі та маркери авторизації, передаються між клієнтом та сервером.

Ідентифікація користувача / пароля

Давайте подивимось, як спочатку працює звичайна стара автентифікація.

  • Користувач підключається до https://example.com
  • Сервер обслуговує багату програму Javascript, яка надає початкову сторінку. Хтосьбув на сторінці є форма для входу.
  • Багато розділів цього додатка для однієї сторінки не заповнені даними через те, що користувач не входив у систему. Усі ці розділи мають слухача подій під час події "входу". Все це на стороні клієнта, сервер не знає про ці події.
  • Користувач вводить свій логін та пароль та натискає кнопку подання, яка запускає обробник Javascript для запису імені користувача та пароля у змінні на стороні клієнта. Потім цей обробник запускає подію "входу". Знову ж таки, це все дії клієнта, облікові дані ще не надіслані на сервер .
  • Слухачі події "Логін" викликаються. Кожному з них тепер потрібно надіслати один або більше запитів до API RESTful на, https://example.com/apiщоб отримати конкретні користувачеві дані для візуалізації на сторінці. Кожен окремий запит, який вони надсилають до веб-сервісу, включатиме ім’я користувача та пароль, можливо, у формі автентифікації HTTP Basic , оскільки службі RESTful не дозволяється підтримувати стан клієнта від одного запиту до іншого. Оскільки веб-служба захищена HTTP, пароль надійно шифрується під час транзиту.
  • Веб-сервіс https://example.com/apiотримує купу індивідуальних запитів, кожен з інформацією про автентифікацію. Ім'я користувача та пароль у кожному запиті перевіряються відповідно до бази даних користувачів, і якщо вони виявлені правильними, виконується запитувана функція, а дані повертаються клієнту у форматі JSON. Якщо ім’я користувача та пароль не відповідають помилці, клієнту надсилається помилка у вигляді коду помилки 401 HTTP.
  • Замість того, щоб змушувати клієнтів надсилати ім’я користувача та пароль при кожному запиті, ви можете мати функцію "get_access_token" у вашій службі RESTful, яка бере ім'я користувача та пароль та відповідає маркером, який є унікальним криптографічним хешем, який має деякий термін дії дата, пов’язана з нею. Ці маркери зберігаються в базі даних з кожним користувачем. Потім клієнт надсилає маркер доступу в наступних запитах. Токена доступу буде підтверджена відповідно до бази даних замість імені користувача та пароля.
  • Клієнтські програми, які не є браузерами, як додатки для телефонів, роблять те саме, що вище, вони просять користувача ввести його / її дані, а потім надсилати їх (або маркер доступу, згенерований з них) з кожним запитом у веб-службу.

Важливим моментом цього прикладу є те, що веб-сервісам RESTful потрібна автентифікація при кожному запиті .

Додатковий рівень безпеки в цьому сценарії додасть авторизацію клієнтських додатків на додаток до автентифікації користувача. Наприклад, якщо у вас є веб-клієнт, додатки iOS та Android, які використовують веб-сервіс, можливо, ви хочете, щоб сервер знав, хто з трьох клієнтів даного запиту, незалежно від того, хто такий користувач, який має автентифікацію. Це може дозволити веб-службі обмежувати певні функції лише певними клієнтами. Для цього ви можете використовувати ключі та секрети API, дивіться цю відповідь для деяких ідей щодо цього.

Аутентифікація у Facebook

Наведений вище робочий процес не працює для підключення до Facebook, оскільки вхід через Facebook має третя сторона, сам Facebook. Процедура входу вимагає перенаправлення користувача на веб-сайт Facebook, де облікові дані вводяться поза нашим контролем.

Тож давайте подивимось, як все змінюється:.

  • Користувач підключається до https://example.com
  • Сервер обслуговує багату програму Javascript, яка надає початкову сторінку. Деякі на сторінці є форма для входу, що включає кнопку "Увійти через Facebook".
  • Користувач натискає кнопку "Увійти за допомогою Facebook", яка є лише посиланням, на яке перенаправляється (наприклад) https://example.com/auth/facebook.
  • https://example.com/auth/facebookМаршрут обробляється passport.js (див документації )
  • Всі користувачі бачать, що сторінка змінюється, і тепер вони знаходяться на сторінці, розміщеній у Facebook, де їм потрібно увійти та авторизувати нашу веб-програму. Це абсолютно поза нашим контролем.
  • Користувач входить у Facebook та надає дозвіл нашому додатку, тому Facebook тепер переспрямовує назад до URL-адреси зворотного виклику, яку ми налаштували в налаштуваннях passport.js, яка за прикладом у документації :https://example.com/auth/facebook/callback
  • Обробник https://example.com/auth/facebook/callbackмаршруту passport.js для маршруту буде викликати функцію зворотного дзвінка, яка отримує маркер доступу від Facebook та деяку інформацію користувача від Facebook, включаючи електронну адресу користувача.
  • За допомогою електронного листа ми можемо знайти користувача в нашій базі даних і зберегти в ньому маркер доступу до Facebook.
  • Останнє, що ви робите у зворотному дзвінку Facebook, - це перенаправлення назад на багатий клієнтський додаток, але цього разу нам потрібно передати клієнту ім’я користувача та маркер доступу, щоб він міг ним користуватися. Це можна зробити різними способами. Наприклад, змінні Javascript можуть бути додані на сторінку через механізм шаблону на стороні сервера, інакше cookie може бути повернутий з цією інформацією. (дякую @RyanKimber за вказівку на проблеми безпеки при передачі цих даних у URL-адресу, як я спочатку запропонував).
  • Отже, ми знову запускаємо додаток на одній сторінці, але клієнт має ім’я користувача та маркер доступу.
  • Клієнтська програма може негайно викликати подію "входу" та дозволити різним частинам програми запитувати інформацію, яка їм потрібна, від веб-служби.
  • Усі запити, що надсилаються https://example.com/api, включатимуть маркер доступу до Facebook для аутентифікації або власний маркер доступу програми, створений з маркера Facebook через функцію "get_access_token" в API REST.
  • У додатках, що не входять до браузера, тут це трохи складніше, тому що для входу в систему OAuth потрібен веб-браузер. Щоб увійти з телефону або настільного додатка, вам потрібно буде запустити браузер для переадресації на Facebook, і ще гірше, ви потрібен спосіб, щоб браузер передавав маркер доступу Facebook назад до програми через якийсь механізм.

Я сподіваюся, що це відповість на більшість питань. Звичайно, ви можете замінити Facebook на Twitter, Google або будь-який інший сервіс аутентифікації на базі OAuth.

Мені було б цікаво дізнатися, чи є у когось простіший спосіб вирішити це.


5
Дякую за детальну відповідь. Лише одне питання: ти це кажеш Every single request they send to the web service will include the username and password, і все-таки ти кажеш you can have a "get_access_token" function in your RESTful service. Здається, суперечливим є твердження, що REST повинен бути без громадянства, але зберігання сторони сервера жетонів доступу нормально, оскільки цей акт зберігання жетонів доступу означає, що сервер зараз є стаціонарним. Буду вдячний за будь-які роз’яснення чи обґрунтування щодо цього. Дякую! :)
ryanrhee

6
Коли я думаю про вимогу без громадянства, я думаю про стан для певного сеансу з клієнтом. Я не бачу збереження даних, пов’язаних з користувачем, у базі даних, наприклад пароля або маркера доступу як "стану", принаймні не "стану сеансу". Ваш сервер, очевидно, повинен знати, хто такі користувачі, і їх паролі, але це дані програми, які не пов’язані з сеансом. Однак, багато людей використовують файли cookie в нібито послугах RESTful, тому наскільки суворо ви хочете дотримуватися специфікації REST, насправді залежить від кожного виконавця.
Мігель

1
@Dexter: У традиційному випадку входу користувач вводить ім’я користувача та пароль у форму, а після натискання кнопки «Надіслати» ця інформація розміщується на веб-сервері. У цьому випадку цього не відбувається, користувач заповнює форму, і коли він натискає Надіслати обробник Javascript (подія onClick у кнопці подання) фіксує дані та зберігає їх у контексті клієнта. У мене немає прикладу, готового показати, але слідкуйте за другою частиною цього підручника в своєму блозі, де я покажу, як це робиться: blog.miguelgrinberg.com/post/…
Мігель

2
Це дуже продумане написання, але містить один важливий контроль або помилку. Під час роботи з логіном у Facebook (або Github, twitter тощо) було б набагато кращим передати токен назад клієнту у файлі cookie, а потім видалити cookie на стороні клієнта, як тільки він буде виявлений. Передача маркера як частини рядка URL додасть цю URL-адресу в історію браузера і (якщо обробляються випадки неправильно) може призвести до того, що браузер запитує цю URL-адресу. Або робить маркер видимим. Кожен, хто потім скопіював URL, може підробляти цього користувача у вашій програмі.
Райан Кімбер

1
@Nathan: базовий auth через https захищений, так що так, це прийнятний механізм.
Мігель

11

Я дуже вдячний поясненням @ Miguel за повний потік у кожному випадку, але я хотів би додати деякі деталі в частині автентифікації Facebook.

Facebook надає Javascript SDK, який ви можете використовувати для отримання маркера доступу на клієнтському кінці безпосередньо, який потім передається на сервер і використовується для подальшого витягування всієї інформації про користувача з Facebook. Таким чином, вам не потрібно в основному ніяких перенаправлень.

Крім того, ви можете використовувати ту саму кінцеву точку API і для мобільних додатків. Просто використовуйте SDK для Android / iOS для Facebook, отримайте Facebook access_token на клієнтському кінці та передайте його на сервер.

Що стосується характеру без громадянства, як пояснено, коли get_access_token використовується для генерування маркера та передається клієнту, цей маркер також зберігається на сервері. Тож це так добре, як маркер сесії, і я вважаю, що це робить його важливим?

Тільки мої 2 копійки ..


1
Це дуже важливо, тому що таким чином ви можете зробити лише одну сторінку аутентифікації ajax за допомогою facebook. Токен РОЗПОВІДНО захищений як автентичний сеанс, різниця полягає лише в тому, що маркер можна передати іншому домену, а сеанс використовується лише в одному конкретному домені. Використовуючи expressjs та паспорт, ви можете зробити api, який зберігає стан, і використовувати автентифікацію сеансу.
jperelli

На жаль, api javascript чудовий, якщо ви хочете перевірити автентифікацію користувача на виконання дій проти Facebook, але марний сам по собі, якщо ви хочете перевірити користувача щодо вашого сервера / бази даних, наскільки я можу сказати.
Джеймс Вестгейт

4
Ви також можете скористатися методом, описаним вище Miguel, але потім видайте власний маркер JWT у вигляді файлу cookie, коли перенаправляєте клієнта у зворотній зв'язок з аутентичним викликом. Це дозволяє найкраще з обох світів - ваш односторінковий додаток може потурбуватися лише про один тип аутентифікації (JWT), зберігаючи однаковий рівень безпеки та забезпечуючи гнучкість для підтримки будь-якого з соціальних входів, не використовуючи специфічні API JavaScript для кожної соціальної мережі (Facebook, Twitter, LinkedIn, Google тощо). Це також дозволяє підтримувати підтримку в стилі AJAX для імені користувача / пароля та доступу REST.
Райан Кімбер

Наразі SDK для JavaScript Javascript не працює з Chrome iOS. Можливо, проблема для когось.
demisx

@RyanKimber Ви можете написати невеликий репо-гіт або подібне, коли це робиться як приклад, який повністю застряг
Simon Dragsbæk

3

Ось дивовижна стаття, яку я знайшов, яка може допомогти вам засвідчити автентифікацію:

  • Facebook
  • Twitter
  • Google
  • Місцевий авт

Легка автентифікація вузла: налаштування та локальне


Ваше посилання не призводить до статті, а натомість до списку статей з тегом "javascript"
Пауло Олівейра

1
Дякую. Оновлено посилання. Вони змінилися.
myusuf
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.