it-roy-ru.com

Как использовать токен доступа пользователя Facebook на стороне сервера?

Введение

Я занимаюсь разработкой нескольких веб-сервисов и нескольких клиентов (веб-приложений, мобильных устройств и т.д.), Которые будут взаимодействовать с указанными сервисами через HTTP (s). Моя текущая работа заключается в разработке решения для аутентификации и авторизации продукта. Я решил использовать внешние поставщики удостоверений, такие как Facebook, Google, Microsoft, Twitter и т.д. Для аутентификации.

Я пытаюсь решить проблему "когда на мой сервер приходит запрос, как я узнаю, кто пользователь и как я могу быть уверен?". Другие вопросы ниже также ...

Требования

  1. Положитесь на внешние идентификаторы, чтобы указать, с кем я имею дело (по сути, 'userId' - это все, что меня волнует).
  2. Система должна использовать аутентификацию на основе токенов (в отличие от файлов cookie, например, или базовой аутентификации).

    Я считаю, что это правильный выбор для масштабирования между несколькими клиентами и серверами при обеспечении слабой связи.

Workflow

Основываясь на моем прочтении и понимании аутентификации на основе токенов, я представляю себе рабочий процесс следующим образом. Давайте сосредоточимся на Facebook в веб-браузере . Я предполагаю, что другие внешние поставщики удостоверений должны иметь аналогичные возможности, хотя я пока не подтвердил.

Обратите внимание, что на момент написания статьи я пользуюсь следующей версией логина Facebook версии 2.2

  1. Клиент: Инициирует вход в Facebook, используя JavaScript SDK
  2. Facebook: Пользователь аутентифицирует и утверждает разрешения приложения (например, для доступа к общедоступному профилю пользователя)
  3. Facebook: Отправляет ответ клиенту, который содержит токен доступа пользователя, идентификатор и подписанный запрос
  4. Клиент: Сохраняет токен доступа пользователя в сеансе браузера ( удобно обрабатывается SDK )
  5. Клиент: Делает запрос к моему веб-сервису для безопасного ресурса, отправляя по токену доступа пользователя в заголовке авторизации + идентификатор пользователя (в настраиваемом заголовке). потенциально)
  6. Сервер: Считывает токен доступа пользователя из заголовка запроса и инициирует проверку, отправляя запрос в API-график debug_token, предоставленный Facebook
  7. Facebook: Отвечает на сервер с информацией о токене доступа пользователя (содержит appId и userId)
  8. Сервер: Завершает проверку токена, сравнивая appId с ожидаемым (известным для себя) и userId с тем, что было отправлено по клиентскому запросу.
  9. Сервер: Отвечает клиенту запрошенным ресурсом (при условии успешного пути авторизации)

Я предполагаю, что шаги 5-9 будут повторяться для последующих запросов к серверу (хотя маркер доступа пользователя действителен - не истек, отозван со стороны FB, изменены разрешения приложения и т.д.)

Вот диаграмма, чтобы помочь идти вместе с шагами. Пожалуйста, поймите, что эта система не является одностраничным приложением (SPA). Упомянутые веб-сервисы являются конечными точками API, которые по существу предоставляют данные JSON клиентам; они не обслуживают HTML/JS/CSS (за исключением серверов веб-клиентов).

Workflow diagram

Вопросы

  1. Прежде всего, есть ли какие-либо явные пробелы/ямы при описанном подходе, основанном на моем предисловии и требованиях?

  2. Требуется/рекомендуется ли выполнять исходящий запрос в Facebook для проверки токена доступа (шаги 6-8 выше) для каждого клиентского запроса ?

    Я знаю, по крайней мере, я должен проверить токен доступа, поступивший из запроса клиента. Однако рекомендуемый подход для последующих проверок после первого мне неизвестен. Если есть типичные шаблоны, мне интересно услышать о них. Я понимаю, что они могут зависеть от приложения в зависимости от моих требований; однако я пока не знаю, что искать. Я проявлю должную осмотрительность, как только у меня появится основная идея.

    Например, возможные мысли:

    • Хэшируйте пару токен доступа + userId после первой проверки и сохраните ее в распределенном кэше (доступном для всех веб-серверов) с истечением, равным токенам доступа. После последующих запросов от клиентов хэшируйте пару токен доступа + userId и проверьте ее наличие в кэше. Если имеется, то запрос авторизуется. В противном случае обратитесь к API графа Facebook, чтобы подтвердить токен доступа. Я предполагаю, что эта стратегия может быть осуществима, если я использую HTTPS (которым я буду). Однако как сравнить производительность?

    • Принятый ответ в этот вопрос StackOverflow рекомендует создавать токен пользовательского доступа после завершения первой проверки токена пользователя Facebook. Затем пользовательский токен будет отправлен клиенту для последующих запросов. Мне интересно, если это сложнее, чем вышеупомянутое решение, как бы то ни было. Это потребовало бы реализации моего собственного провайдера идентификации (чего я хочу избежать, потому что в первую очередь хочу использовать внешних провайдеров идентификации ...). Есть ли смысл этого предложения?

  3. Присутствует ли поле SignRequest в ответе на шаге 3 выше (упомянуто здесь ), эквивалентно параметру подписанного запроса здесь в потоке входа в игру холст?

    Похоже, они намекают как эквивалентные, так как первые ссылки на последние в документации. Однако я удивлен, что стратегия проверки, упомянутая на странице игр, не упоминается в "ручном создании потока входа" страница веб-документации.

  4. Если ответом на вопрос № 3 является "Да", может ли та же самая стратегия подтверждения личности декодировать подпись и сравниваться с тем, что, как ожидается, будет использоваться на стороне сервера?

    Decode & compare from FB docs

    Мне интересно, может ли это быть использовано вместо того, чтобы делать исходящий вызов графа API debug_token (шаг № 6 выше), чтобы подтвердить токен доступа в соответствии с рекомендациями здесь :

    debug_token graph API from FB docs

    Конечно, чтобы выполнить сравнение на стороне сервера, часть запроса с подписью должна быть отправлена ​​вместе с запросом на сервер (шаг № 5 выше). В дополнение к осуществимости, не жертвуя безопасностью, мне интересно, как производительность сравнится с исходящим вызовом.

  5. Пока я занимаюсь этим, в каком сценарии/для какой цели вы бы, например, сохранили токен доступа пользователя в базе данных? Я не вижу сценария, в котором мне нужно было бы это делать, однако я могу что-то упустить из виду. Мне любопытно, что некоторые распространенные сценарии могут заключаться в spark некоторых мыслях.

Спасибо!

63
Scott Lin

Из того, что вы описываете, я бы предложил использовать поток входа в систему на стороне сервера, как описано в

так что токен уже находится на вашем сервере, и его не нужно передавать от клиента. Если вы используете незашифрованные соединения, это может быть угрозой безопасности (например, для атак "человек посередине").

Шаги будут:

(1) Вход людей

Вам необходимо указать разрешение, которое вы хотите получить от пользователей, в параметре scope. Запрос может быть запущен только по обычной ссылке:

GET https://www.facebook.com/dialog/oauth?
    client_id={app-id}
   &redirect_uri={redirect-uri}
   &response_type=code
   &scope={permission_list}

Увидеть

(2) Подтвердите личность

GET https://graph.facebook.com/oauth/access_token?
    client_id={app-id}
   &redirect_uri={redirect-uri}
   &client_secret={app-secret}
   &code={code-parameter}

(3) Проверьте токен доступа

Вы можете проверить токен, как вы уже сказали в своем вопросе через

GET /debug_token?input_token={token-to-inspect}
    &access_token={app-token-or-admin-token}

Это следует делать только на стороне сервера, поскольку в противном случае вы сделаете свой токен доступа к приложению видимым для конечных пользователей (не очень хорошая идея!).

Увидеть

(4) Расширение токена доступа

Как только вы получили (недолговечный) токен, вы можете позвонить, чтобы расширить токен, как описано

как следующее:

GET /oauth/access_token?grant_type=fb_exchange_token
    &client_id={app-id}
    &client_secret={app-secret}
    &fb_exchange_token={short-lived-token}

(5) Хранение токенов доступа

Что касается хранения токенов на сервере, FB предлагает сделать следующее:

(6) Обработка маркеров доступа с истекшим сроком

Поскольку FB не уведомляет вас, если срок действия токена истек (и если вы не сохраняете дату истечения срока действия и не сравниваете ее с текущей меткой времени перед выполнением вызова), возможно, что вы получите сообщения об ошибках от FB, если токен стал недействительным (после макс. 60 дней). Код ошибки будет 190:

{
  "error": {
    "message": "Error validating access token: Session has expired at unix 
                time SOME_TIME. The current unix time is SOME_TIME.", 
    "type": "OAuthException", 
    "code": 190
  }
}

Увидеть

Если токен доступа становится недействительным, решение состоит в том, чтобы человек снова вошел в систему, после чего вы сможете снова выполнять вызовы API от его имени. Порядок входа в систему, который ваше приложение использует для новых людей, должен определить, какой метод вам нужно принять.

21
Tobi
  1. Я не вижу никаких явных пробелов/падений, но я не эксперт по безопасности.
  2. Как только ваш сервер проверил данный токен (шаг 8), как вы сказали:

Принятый ответ на этот вопрос StackOverflow рекомендует создавать токен пользовательского доступа после первой проверки токена пользователя Facebook. Затем пользовательский токен будет отправлен клиенту для последующих запросов. Мне интересно, если это сложнее, чем вышеупомянутое решение, как бы то ни было. Это потребовало бы реализации моего собственного провайдера идентификации (чего я хочу избежать, потому что в первую очередь хочу использовать внешних провайдеров идентификации ...). Есть ли смысл этого предложения?

ИМХО это путь. Я бы использовал https://jwt.io/ , который позволяет вам кодировать значения (например, userId), используя секретный ключ. Затем ваш клиент прикрепляет этот токен к каждому запросу. Таким образом, вы можете проверить запрос без необходимости третьей стороны (вам также не нужны запросы к базе данных). Приятно, что нет необходимости хранить токен в вашей БД.

Вы можете определить дату истечения срока действия токена, чтобы заставить клиента снова аутентифицироваться с третьей стороной, когда вы захотите.

  1. Допустим, вы хотите, чтобы ваш сервер мог выполнять какие-либо действия без взаимодействия с клиентом. Например: Открыть графические истории . В этом сценарии, потому что вам нужно опубликовать что-то от имени пользователя, вам потребуется токен доступа, хранящийся в вашей БД.

(Не могу помочь с 3 и 4 вопросами, извините).

1
ilopezluna