Подтвердить что ты не робот

Пользовательский учет/аутентификация в REST API

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

Я делаю довольно простой REST API (Silex-PHP), который должен быть изначально использован только одним SPA (базовым приложением). Я не хочу комментировать все несколько методов проверки подлинности в этом вопросе, поскольку эта тема уже полностью включена в SO. Я в основном создаю токен для каждого пользователя, и этот токен будет прикреплен в каждом запросе, который требует аутентификации со стороны SPA. Все транзакции SPA-Server будут работать под HTTPS. На данный момент мое решение заключается в том, что токен не истекает. Токены, которые истекают/токены за сеанс, не соответствуют безгражданству REST, не так ли? Я понимаю, что там есть много возможностей для улучшения безопасности, но пока что я пока недоступен.

У меня есть модель для токенов и, следовательно, таблица в базе данных для токенов с FK для user_id. Под этим я подразумеваю, что токен не является частью моей модели пользователя.

REGISTER

У меня есть POST/users (не требует аутентификации), который создает пользователя в базе данных и возвращает нового пользователя. Это соответствует одному правилу одного запроса. Однако это вызывает у меня определенные сомнения:

  • Моя идея заключается в том, что на момент создания нового пользователя создайте новый токен для пользователя, чтобы немедленно вернуть его с помощью Response и, таким образом, улучшить UX. Пользователь сразу же сможет начать использовать веб-приложение. Однако возврат маркера для такого ответа приведет к нарушению правила возврата только ресурса. Должен ли я вместо этого сделать два запроса вместе? Один, чтобы создать пользователя и один для извлечения токена без необходимости повторного ввода учетных данных пользователям?

  • Если я решил вернуть токен вместе с пользователем, то я считаю, что POST/пользователи будут путать для потребителя API, а затем появится что-то вроде POST/auth/register. Еще раз мне не нравится эта идея, потому что она включает в себя глагол. Мне очень нравится простота, предлагаемая в этом ответе. Но опять же, мне нужно будет сделать два запроса вместе, POST/users и POST/tokens. Как неправильно делать два запроса вместе, а также, как я точно отправлю соответствующую информацию для токена, который будет прикреплен к определенному пользователю, если оба запроса отправлены вместе?

Теперь мой поток работает следующим образом:

1. Register form makes a POST /users request
2. Server creates a new user and a new token, returns both in the response (break REST rule)
3. Client now attaches token to every Request that needs Authorization

Знак не истекает, сохраняя безгражданство REST.

ЭЛЕМЕНТНОЕ ПОЛОЖЕНИЕ

Большинство текущих веб-приложений требуют проверки подлинности электронной почты, не нарушая UX для пользователей, то есть пользователи могут сразу использовать webapp после регистрации. С другой стороны, если я верну маркер с запросом на регистрацию, как было предложено выше, пользователи сразу получат доступ к каждому ресурсу без проверки электронной почты.

Обычно я бы использовал следующий рабочий процесс:

1. Register form sends POST /users request.
2. Server creates a new user with validated_email set to false and stores an email_validation_token. Additionally, the server sends an email generating an URL that contains the email_validation_token. 
3. The user clicks on the URL that makes a request: For example POST /users/email_validation/{email_validation_token}
4. Server validates email, sets validated_email to true, generates a token and returns it in the response, redirecting the user to his home page at the same time.

Это выглядит сложнее и полностью разрушает UX. Как ты это сделал?

ВХОД

Это довольно просто, потому что теперь я делаю это так, поэтому, пожалуйста, исправьте меня, если не так:

1. User fills a log in form which makes a request to POST /login sending Basic Auth credentials.
2. Server checks Basic Auth credentials and returns token for the given user.
3. Web app attached the given token to every future request.

логин - это глагол и, таким образом, нарушает правило REST, все, похоже, соглашаются на это так.

LOGOUT

Почему всем кажется, что нужна конечная точка /auth/logout? С моей точки зрения, нажатие на "выход из системы" в веб-приложении должно в основном удалить токен из приложения и не отправлять его в дополнительные запросы. Сервер не играет никакой роли.

Как возможно, токен хранится в localStorage, чтобы предотвратить потерю маркера при возможном обновлении страницы, выход из системы также подразумевал бы удаление токена из localStorage. Но все равно это не влияет на сервер. Я понимаю, что люди, которые должны иметь POST/logout, в основном работают с токенами сеанса, которые снова нарушают безгражданство REST.

ПОМНИТЕ ME

Я понимаю, что помню, что я в основном ссылаюсь на сохранение возвращенного токена на localStorage или нет в моем случае. Правильно ли это?

Если вы порекомендовали бы какие-либо дальнейшие чтения по этой теме, я очень благодарен. Спасибо!

4b9b3361

Ответ 1

REGISTER

Токены, которые истекают/токены за сеанс, не соответствуют безгражданству REST, правильно?

Нет, в этом нет ничего плохого. Многие схемы проверки подлинности HTTP имеют истекающие токены. OAuth2 очень популярен для служб REST, и многие реализации OAuth2 заставляют клиента обновлять токен доступа время от времени.

Моя идея заключается в том, что на момент создания нового пользователя создайте новый токен для пользователя, чтобы немедленно вернуть его с помощью Response и, таким образом, улучшить UX. Пользователь сразу же сможет начать использовать веб-приложение. Однако возврат маркера для такого ответа приведет к нарушению правила возврата только ресурса. Должен ли я вместо этого сделать два запроса вместе? Один для создания пользователя и один для получения токена без необходимости повторного ввода учетных данных?

Как правило, если вы создаете новый ресурс, следуя лучшим методам REST, вы не возвращаете что-то в ответ на POST, как это. Это сделало бы вызов более RPC-подобным, поэтому я бы согласился с вами здесь... это не совсем RESTful. Я предлагаю два решения:

  • Игнорируйте это, нарушите лучшие практики. Может быть, это к лучшему в этом случае, и делать исключения, если они делают больше смысла, иногда лучше всего делать (после тщательного рассмотрения).
  • Если вы хотите быть более RESTful, я предложу альтернативу.

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

Это все еще оставляет вам проблему создания пользователя в вашем API и в ответ на этот вызов (POST), возвращающий секрет, который может использоваться в качестве токена доступа/обновления.

Моя альтернатива такова:

Вам не нужно иметь пользователя, чтобы начать сеанс.

Вместо этого вы можете начать сеанс перед созданием пользователя. Это гарантирует, что для любого будущего вызова вы знаете, что разговариваете с одним клиентом.

Если вы запустите свой процесс OAuth2 и получите токен доступа/обновления, вы можете просто выполнить аутентифицированный запрос POST на /users. Это означает, что ваша система должна знать о двух типах пользователей, прошедших проверку подлинности:

  • Пользователи, которые вошли в систему с именем пользователя/паролем (`grant_type = passsword1).
  • Пользователи, которые вошли в систему "анонимно" и намерены создать пользователя после факта. (grant_type = client_credentials).

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

ЭЛЕКТРОННАЯ ВАЛИДИЯ

Оба ваши предложения:

  • Предотвратите использование пользователем приложения до завершения проверки по электронной почте.
  • Разрешить пользователю немедленно использовать приложение.

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

Вот пример, где вы не хотите этого делать. Скажите, если адрес электронной почты используется другими членами вашей системы, чтобы добавить пользователя в качестве друга, адрес электронной почты является типом личности. Если вы не заставляете пользователей проверять свои электронные письма, это означает, что я могу действовать от имени кого-то с другим адресом электронной почты. Это похоже на возможность получать приглашения и т.д. Является ли это вектором атаки? Тогда вам может потребоваться заблокировать пользователя от использования приложения до тех пор, пока оно не будет проверено.

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

Здесь нет правильного ответа, это зависит только от того, как вы собираетесь использовать адрес электронной почты.

ВХОД

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

Большинство реализаций сервера PHP OAuth2 невелики. Они делают слишком много и с ними сложно интегрироваться. Катиться самостоятельно не так сложно, и вы уже достаточно близко к созданию чего-то подобного.

LOGOUT

Две причины, по которым вам может понадобиться конечная точка выхода, следующие:

  • Если вы используете аутентификацию на основе cookie/сеанса и хотите сказать серверу забыть сеанс. Похоже, это не проблема для вас.
  • Если вы хотите сообщить серверу об истечении срока действия токена доступа/обновления. Да, вы можете просто удалить их из localstorage, и это может быть достаточно хорошим. Принуждение к их истечению на стороне сервера может дать вам такую ​​дополнительную уверенность. Что, если кто-то смог MITM вашего браузера и теперь имеет доступ к вашим жетонам? Возможно, мне захочется быстро выйти из системы и выпустить все существующие токены. Это краевой случай, и я лично никогда этого не делал, но это может быть причиной, по которой вам это нужно.

ПОМНИТЕ МЕ

Да, реализация "помни меня" с локальным хранилищем звучит как хорошая идея.

Ответ 2

Вы правы, СЕЗОН не разрешен в REST, поэтому нет необходимости входить в систему или выходить из системы в службе REST и /login,/logout не являются существительными.

Для аутентификации вы можете использовать

  • Обычная проверка подлинности через SSL
  • Дайджест-аутентификация
  • OAuth 2
  • HMAC и т.д.

Я предпочитаю использовать PUBLIC KEY и PRIVATE KEY [HMAC]

Частный ключ никогда не будет передаваться через Интернет, и мне не нужен открытый ключ. Открытый ключ будет использоваться для выполнения конкретных действий пользователя [Кто держит ключ api]

Частный ключ будет известен клиентскому приложению и серверу. Закрытый ключ будет использоваться для создания подписи. Вы создаете токен подписи с использованием закрытого ключа и добавляете ключ в заголовок. Сервер также сгенерирует подпись и подтвердит запрос для установления связи.

Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b

Теперь, как вы получите секретный ключ? вы должны сделать это вручную, как если бы вы ставили facebook, twitter или google api key на вашем приложении.

Однако в некоторых случаях вы также можете вернуть [не рекомендуется] ключ только один раз, как это делает Amazon S3. Они предоставляют секретный ключ доступа AWS в ответ на регистрацию.