Глобальная авторизация в веб-системах — различия между версиями

Материал из YourcmcWiki
Перейти к: навигация, поиск
м
 
(не показано 14 промежуточных версий этого же участника)
Строка 1: Строка 1:
 +
{{Box|{{Warning}} Ниже описывается Велосипедная Реализация SSO. В современном мире доступен OAuth2, про него можно почитать тут: [[Кратко об SSO через OAuth2]]}}
 +
 
…или как реализовать простой [[rupedia:Технология единого входа|Single Sign-on]] в веб-системах.
 
…или как реализовать простой [[rupedia:Технология единого входа|Single Sign-on]] в веб-системах.
  
Строка 4: Строка 6:
  
 
* (П) Пользователь.
 
* (П) Пользователь.
* (С1) Система 1 — клиент глобальной авторизации. В неё пришёл пользователь без авторизации.
+
* ({{/С1}}) Система 1 — клиент глобальной авторизации. В неё пришёл пользователь без авторизации.
* (С2) Система 2 — сервер глобальной авторизации. В ней пользователь уже авторизован, скорее всего, через cookie.
+
* ({{/С2}}) Система 2 — сервер глобальной авторизации. В ней пользователь уже авторизован, скорее всего, через cookie.
  
 
{| class="simpletable"
 
{| class="simpletable"
Строка 12: Строка 14:
 
! кто
 
! кто
 
! что делает
 
! что делает
! кому
+
!width=35%| кому
 +
!width=45%| возможные ошибки
 
|-
 
|-
 
|
 
|
 
|valign=top| (П) →
 
|valign=top| (П) →
 
|valign=top| (переходит по ссылке) →
 
|valign=top| (переходит по ссылке) →
|valign=top| <tt style="background-color: #e0ffe0">C1</tt> с любыми параметрами.<br />
+
|valign=top| {{/С1}} с любыми параметрами.<br />
&rArr; <tt style="background-color: #e0ffe0">C1</tt> хочет перенять авторизацию у <tt style="background-color: #e0e0ff">С2</tt>. <br />
+
&rArr; {{/С1}} хочет перенять авторизацию у {{/С2}}. <br />
<tt style="background-color: #e0ffe0">C1</tt> генерирует случайный ID (ID) и ключ (KEY). Никаких ограничений на эти значения не накладывается, кроме того, что они должны быть достаточно стойки к подбору и, желательно, состоять из печатных символов. Например, за каждый из них можно взять 16 случайных байт, взятых из <tt>/dev/urandom</tt> в UNIX-системах и <tt>GetRandom()</tt> в Windows.
+
{{/С1}} генерирует случайный ID (ID) и ключ (KEY). Никаких ограничений на эти значения не накладывается, кроме того, что они должны быть достаточно стойки к подбору и, желательно, состоять из печатных символов. Например, за каждый из них можно взять 16 случайных байт, взятых из <tt>/dev/urandom</tt> в UNIX-системах и <tt>GetRandom()</tt> в Windows.
 +
|valign=top|
 
|-
 
|-
 
|valign=top align=center| 1
 
|valign=top align=center| 1
|valign=top| <tt style="background-color: #e0ffe0">C1</tt>&nbsp;&rarr;
+
|valign=top| {{/С1}}&nbsp;&rarr;
 
|valign=top| (делает GET-запрос напрямую)&nbsp;&rarr;
 
|valign=top| (делает GET-запрос напрямую)&nbsp;&rarr;
|valign=top| <tt style="background-color: #e0e0ff">С2</tt> с параметрами <tt style="background-color: #ffe0e0">ga_id=ID&ga_key=KEY</tt> <br />
+
|valign=top| {{/С2}} с параметрами {{/p}}ga_id=ID&ga_key=KEY</tt> <br />
&rArr; <tt style="background-color: #e0e0ff">С2</tt> запоминает соответствие ID и KEY.
+
&rArr; {{/С2}} запоминает соответствие ID и KEY.
 +
|valign=top|
 +
{{/С2}} не ответила или ответ не в формате JSON («по факту», а не по MIME-типу ответа):
 +
: '''Сервер авторизации XXX недоступен со стороны системы {{/С1}}; код HTTP: XXX; MIME-тип ответа: XXX.'''
 +
{{/С2}} ответила хешем в формате JSON, в котором присутствует поле «<tt>error</tt>» с непустым значением EEE:
 +
: '''Сервер авторизации XXX сообщает об ошибке начала сессии авторизации: EEE.'''
 
|-
 
|-
 
|valign=top align=center| 2
 
|valign=top align=center| 2
|valign=top| <tt style="background-color: #e0ffe0">С1</tt>&nbsp;&rarr;
+
|valign=top| {{/С1}}&nbsp;&rarr;
 
|valign=top| (перенаправление браузера пользователя)&nbsp;&rarr;
 
|valign=top| (перенаправление браузера пользователя)&nbsp;&rarr;
|valign=top| <tt style="background-color: #e0e0ff">С2</tt> с параметрами <tt style="background-color: #ffe0e0">ga_id=ID&ga_url=URL&ga_check=CHECK</tt>
+
|valign=top| {{/С2}} с параметрами {{/p}}ga_id=ID&ga_url=URL&ga_check=CHECK</tt> и опционально параметром {{/p}}ga_message=TEXT</tt>.
  
* URL — URL для возврата на <tt style="background-color: #e0ffe0">С1</tt>, на который <tt style="background-color: #e0e0ff">С2</tt> будет передавать данные и на который же <tt style="background-color: #e0e0ff">С2</tt> будет отправлять пользователя редиректом обратно.
+
* URL — URL для возврата на <tt style="background-color: #e0ffe0">С1</tt>, на который {{/С2}} будет передавать данные и на который же {{/С2}} будет отправлять пользователя редиректом обратно. Если URL не передаётся, за него принимается HTTP-заголовок Referer.
* Если CHECK=0 или не передаётся, и пользователь не авторизован в <tt style="background-color: #e0e0ff">С2</tt>, она должна потребовать от него авторизоваться.
+
* Если CHECK=0 или не передаётся, и пользователь не авторизован в {{/С2}}, она должна потребовать от него авторизоваться.
 +
* TEXT — название {{/С1}} или любое сообщение, которое нужно показать пользователю в случае запроса авторизации.
  
&rArr; <tt style="background-color: #e0e0ff">С2</tt> даётся возможность прочитать cookie пользователя и получить данные о нём.
+
&rArr; {{/С2}} даётся возможность прочитать cookie пользователя и получить данные о нём. Если это необходимо, на этом же шаге {{/С2}} может запросить у пользователь подтверждение передачи его учётных данных внешней системе, и в случае отрицательного решения передать на следующем шаге NOLOGIN.
 +
|valign=top|
 +
URL, на который происходит перенаправление, недоступен пользователю.
 +
: ''Увы, в данном случае никакого сообщения об ошибке выдать не получится :(. Некому — с {{/С1}} уже ушли, а на {{/С2}} ещё не пришли.''
 +
 
 +
В запросе не передан или передан некорректный параметр ga_url, и также отсутствует HTTP-заголовок Referer, по крайней мере такой, который можно распарсить.
 +
: '''В запросе авторизации от клиентской системы отсутствует URL (''или неверный: URL'') обратной связи.'''
 
|-
 
|-
 
|valign=top align=center| 3
 
|valign=top align=center| 3
|valign=top| <tt style="background-color: #e0e0ff">С2</tt>&nbsp;&rarr;
+
|valign=top| {{/С2}}&nbsp;&rarr;
 
|valign=top| (делает POST-запрос напрямую)&nbsp;&rarr;
 
|valign=top| (делает POST-запрос напрямую)&nbsp;&rarr;
|valign=top| <tt style="background-color: #e0ffe0">С1</tt> с параметрами <tt style="background-color: #ffe0e0">ga_client=1&ga_id=ID&ga_key=KEY&ga_data=DATA&ga_nologin=NOLOGIN</tt>
+
|valign=top| {{/С1}} с параметрами {{/p}}ga_client=1&ga_id=ID&ga_key=KEY&ga_data=DATA&ga_nologin=NOLOGIN</tt>
  
 
* DATA — данные о вошедшем пользователе в произвольном формате, кодированные в [[rupedia:JSON|JSON]].
 
* DATA — данные о вошедшем пользователе в произвольном формате, кодированные в [[rupedia:JSON|JSON]].
* NOLOGIN=1 и DATA="" (пустой строке), если и только если CHECK=1 и пользователь не авторизован в <tt style="background-color: #e0e0ff">С2</tt>.
+
* NOLOGIN=1 и DATA="" (пустой строке), если и только если CHECK=1 и пользователь не авторизован в {{/С2}}.
 
* Иначе NOLOGIN не передаётся или NOLOGIN=0.
 
* Иначе NOLOGIN не передаётся или NOLOGIN=0.
  
 
&rArr; <tt style="background-color: #e0ffe0">С1</tt> запоминает соответствие ID и переданных данных.
 
&rArr; <tt style="background-color: #e0ffe0">С1</tt> запоминает соответствие ID и переданных данных.
 +
|valign=top|
 +
 +
ID неизвестен серверу.
 +
: '''Попытка авторизоваться по неверному или устаревшему идентификатору сессии, попробуйте ещё раз ''(ссылка — URL к {{/С1}})''.'''
 +
 +
CHECK=0 и авторизовать пользователя в {{/С2}} невозможно.
 +
: '''Сайт XXX ''(описание: TEXT)'' требует авторизации, а вы не авторизованы в {{/С2}}.''' (TEXT из предыдущего шага)
 +
: Логично, если данное сообщение показывает пользователю {{/С2}}.
 +
 +
Пользователь не доверяет {{/С1}} и запретил {{/С2}} передавать ей свои авторизационные данные.
 +
: Логично, если {{/С2}} покажет пользователю ошибку и/или страницу настройки доверия к {{/С1}}.
 +
 +
{{/С1}} не ответила или ответ не в формате JSON. MIME-тип, опять-таки, не проверяется.
 +
: '''Сайт XXX ''(описание: TEXT)'' недоступен с сервера авторизации. Код HTTP: XXX; MIME-тип ответа: XXX.'''
 +
 +
{{/С1}} ответила хешем в формате JSON, в котором присутствует поле «<tt>error</tt>» с непустым значением EEE:
 +
: '''Сайт XXX ''(описание: TEXT)'' сообщает об ошибке авторизации: EEE.'''
 +
: Ошибками EEE могут быть, например:
 +
:* ID неизвестен клиенту: <br /> '''Попытка авторизоваться по неверному или устаревшему идентификатору сессии, попробуйте ещё раз ''(ссылка)''.'''
 +
:* Известному на клиенте ID соответствует неверный ключ KEY: <br /> '''Секретный ключ неверен, проверка подлинности сервера авторизации XXX не удалась. Возможно, вас дурят.'''
 +
:* Не вышло декодировать DATA или считать NOLOGIN: <br /> '''Формат данных, переданных с сервера авторизации, неизвестен.''' ''Опционально — показать также и сами данные.''
 +
:* Не удалось сохранить данные в хранилище состояний клиента (базе данных, кэше и т. п.): <br /> '''Внутренняя ошибка клиента — хранилище состояний недоступно.'''
 
|-
 
|-
 
|valign=top align=center| 4
 
|valign=top align=center| 4
|valign=top| <tt style="background-color: #e0e0ff">С2</tt>&nbsp;&rarr;
+
|valign=top| {{/С2}}&nbsp;&rarr;
 
|valign=top| (перенаправление браузера пользователя)&nbsp;&rarr;
 
|valign=top| (перенаправление браузера пользователя)&nbsp;&rarr;
|valign=top| <tt style="background-color: #e0ffe0">С1</tt> с параметрами <tt style="background-color: #ffe0e0">ga_client=1&ga_id=ID&ga_res=CODE</tt>
+
|valign=top| {{/С1}} с параметрами {{/p}}ga_client=1&ga_id=ID&ga_res=CODE</tt>
  
 
* CODE — HTTP-код статуса, полученный от POST-запроса из предыдущего пункта.
 
* CODE — HTTP-код статуса, полученный от POST-запроса из предыдущего пункта.
  
&rArr; <tt style="background-color: #e0ffe0">С1</tt> может взять сохранённые в предыдущем пункте данные и на их основе авторизовать пользователя.
+
&rArr; {{/С1}} может взять сохранённые в предыдущем пункте данные и на их основе авторизовать пользователя.
 +
|valign=top|
 +
ID неизвестен клиенту:
 +
: '''Попытка авторизации по неверному или устаревшему идентификатору сессии, либо сервер авторизации не смог передать данные учётной записи. Попробуйте ещё раз ''(ссылка)''.'''
 +
 
 +
<span style="color:red">Самая противная ошибка</span> — это ''Redirect-Loop'' — бесконечное перенаправление, которое можно спровоцировать, если при ошибке авторизации никак не сохранить факт ошибки в браузере пользователя (например, с помощью cookie) и вместо того, чтобы показать страницу с ошибкой, отправить пользователя обратно на шаг 2 (через шаг 1).
 
|}
 
|}
  
И ID и ключ являются «секретными», но ID знают и сервера, и пользователь (ID передаётся в браузер), а ключ — только сами сервера. За счёт этого достигается безопасность: пользователь не может сам передать произвольные данные авторизации на сервер, не зная ключа.
+
Важный момент: после успешной обработки любого запроса глобальной авторизации (запроса с параметрами ga_id или ga_client и т. п.), пришедшего со стороны браузера пользователя, как клиентской, так и серверной стороной, должно осуществляться перенаправление (<tt>HTTP 302 Moved temporarily</tt>) на адрес, не содержащий в себе параметров ga_id и т. п., для исключения повторной отправки запроса пользователем при нажатии «Обновить страницу».
  
Для дополнительной защиты всё это можно просто пустить через HTTPS (SSL).
+
И ID и ключ являются «секретными», но ID знают и сервера, и пользователь (ID передаётся в браузер), а ключ — только сами сервера. За счёт этого достигается безопасность: пользователь не может сам передать произвольные данные авторизации на сервер, не зная ключа. Для дополнительной защиты всё это можно просто пустить через HTTPS (SSL). Понятно, что кэш, в котором сохраняются соответствия ID и ключа, не должен быть доступен для чтения внешнему пользователю, иначе вся защита накрывается медным тазом. Для усиления защиты опять-таки можно дополнительно создать список доверенных серверов и при приёме данных авторизации проверять IP.
  
== Плюс FoF ==
+
Как уже было сказано выше, данные авторизации — произвольные в JSON-формате. Однако, удобно специфицировать его чуть точнее: хеш, в котором есть поля <tt>user_name</tt> (логин), <tt>user_email</tt> (адрес электронной почты) и необязательные поля <tt>user_url</tt> (URL «домашней страницы» пользователя) и <tt>user_real_name</tt> («настоящее имя» пользователя).
  
(Тем кто читает это в вебе, можно в принципе и не читать)
+
OpenID, на самом деле, работает похоже, но ''почему-то'' адски глючит и его страшно использовать на своих внутренних ресурсах. :)
  
Теперь пусть есть некая система типа FoF, в которой действует данный метод глобальной авторизации, и которая должна авторизоваться на доверенных серверах, на которых эта глобальная авторизация тоже действует. Естественно, пароль пользователя хранить в открытом виде в базе при этом нельзя, да его в данных глобальной авторизации и нету. Пример такой ситуации — это FoF (фидридер) — он должен забирать RSS-ленты пользователя, в которых пользователь тоже авторизуется.
+
В реализации протокола очень желательна обработка ошибок и их показ пользователю в удобном виде, чтобы не получалось, как в OpenID — «она утонула» © (то есть «произошла ошибка» без каких-либо деталей).
  
* FoF видит в урле рсс’ки &fof_sudo=1, генерирует случайный ID и добавляет в запрос
+
== FoF_Sudo ==
 +
 
 +
FoF_Sudo — беспарольная авторизация типа «от системы к системе».
 +
 
 +
Идея такая: пусть есть некая система (например [[lib:FeedOnFeeds|FoF]] — фидридер), в которой действует глобальная авторизация (в качестве сервера выступает другая система), и пусть эта система должна авторизоваться на доверенных серверах (в примере — FoF должен забирать защищённые RSS-ленты от имени разных пользователей). Но при этом (!) пароль пользователя хранить в открытом виде в базе FoF при этом нельзя. Да если бы и было можно, в данных глобальной авторизации его просто нету. При этом нужно, чтобы при передаче такой вот беспарольной авторизации какой-то совершенно левый пользователь не смог сделать так же и зайти под произвольным пользователем.
 +
 
 +
Как это сделать? Ответ — одноразовые ключи на каждый запрос:
 +
 
 +
* FoF видит в урле рсс’ки &fof_sudo=(что_угодно). Что_угодно — совершенно что угодно, единственно, в случае FoF оно должно быть разное для разных пользователей, иначе FoF двух пользователей подпишет на один и тот же защищённый фид, а обновлять будет вообще как попало — то от имени одного, то от имени другого пользователя.
 +
* FoF генерирует случайный ID, запоминает соответствие этому ID пользователя и добавляет в запрос кукис:
 
** Cookie: fof_sudo_id=ID
 
** Cookie: fof_sudo_id=ID
* Генератор рсс видит cookie fof_sudo_id и делает запрос к fof: /fof-sudo.php?id=ID
+
* Система, к которой произошло обращение, видит этот кукис (fof_sudo_id) и делает обратный запрос к fof: /fof-sudo.php?id=ID
* FoF отвечает {'user_name':'user@custis.ru'} и забывает ID
+
* FoF отвечает данными пользователя, которые он помнит по этому ID, в формате JSON ({'user_name':'user@custis.ru'}) и забывает ID
* Генератор рсс принимает тот факт, что теперь он работает под юзером user@custis.ru и отдаёт правильную рсс’ку
+
* Внешняя система верит, что теперь надо авторизоваться под именем юзера user@custis.ru (и, например, отдаёт правильную RSS’ку)
 +
 
 +
{{Box|{{Warning}} И это тоже покрывается OAuth2.}}
  
 
[[Категория:Разработка]]
 
[[Категория:Разработка]]

Текущая версия на 18:50, 1 марта 2017

Warning Warning: Ниже описывается Велосипедная Реализация SSO. В современном мире доступен OAuth2, про него можно почитать тут: Кратко об SSO через OAuth2

…или как реализовать простой Single Sign-on в веб-системах.

Ниже описан простейший протокол, который даёт возможность нам сказать внешней системе, кто к нам вошёл, так, что внешняя система знает, что это говорим ей именно мы, а мы знаем, что мы говорим это именно ей.

  • (П) Пользователь.
  • (C1) Система 1 — клиент глобальной авторизации. В неё пришёл пользователь без авторизации.
  • (С2) Система 2 — сервер глобальной авторизации. В ней пользователь уже авторизован, скорее всего, через cookie.
шаг кто что делает кому возможные ошибки
(П) → (переходит по ссылке) → C1 с любыми параметрами.

C1 хочет перенять авторизацию у С2.
C1 генерирует случайный ID (ID) и ключ (KEY). Никаких ограничений на эти значения не накладывается, кроме того, что они должны быть достаточно стойки к подбору и, желательно, состоять из печатных символов. Например, за каждый из них можно взять 16 случайных байт, взятых из /dev/urandom в UNIX-системах и GetRandom() в Windows.

1 C1 → (делает GET-запрос напрямую) → С2 с параметрами ga_id=ID&ga_key=KEY

С2 запоминает соответствие ID и KEY.

С2 не ответила или ответ не в формате JSON («по факту», а не по MIME-типу ответа):

Сервер авторизации XXX недоступен со стороны системы C1; код HTTP: XXX; MIME-тип ответа: XXX.

С2 ответила хешем в формате JSON, в котором присутствует поле «error» с непустым значением EEE:

Сервер авторизации XXX сообщает об ошибке начала сессии авторизации: EEE.
2 C1 → (перенаправление браузера пользователя) → С2 с параметрами ga_id=ID&ga_url=URL&ga_check=CHECK и опционально параметром ga_message=TEXT.
  • URL — URL для возврата на С1, на который С2 будет передавать данные и на который же С2 будет отправлять пользователя редиректом обратно. Если URL не передаётся, за него принимается HTTP-заголовок Referer.
  • Если CHECK=0 или не передаётся, и пользователь не авторизован в С2, она должна потребовать от него авторизоваться.
  • TEXT — название C1 или любое сообщение, которое нужно показать пользователю в случае запроса авторизации.

С2 даётся возможность прочитать cookie пользователя и получить данные о нём. Если это необходимо, на этом же шаге С2 может запросить у пользователь подтверждение передачи его учётных данных внешней системе, и в случае отрицательного решения передать на следующем шаге NOLOGIN.

URL, на который происходит перенаправление, недоступен пользователю.

Увы, в данном случае никакого сообщения об ошибке выдать не получится :(. Некому — с C1 уже ушли, а на С2 ещё не пришли.

В запросе не передан или передан некорректный параметр ga_url, и также отсутствует HTTP-заголовок Referer, по крайней мере такой, который можно распарсить.

В запросе авторизации от клиентской системы отсутствует URL (или неверный: URL) обратной связи.
3 С2 → (делает POST-запрос напрямую) → C1 с параметрами ga_client=1&ga_id=ID&ga_key=KEY&ga_data=DATA&ga_nologin=NOLOGIN
  • DATA — данные о вошедшем пользователе в произвольном формате, кодированные в JSON.
  • NOLOGIN=1 и DATA="" (пустой строке), если и только если CHECK=1 и пользователь не авторизован в С2.
  • Иначе NOLOGIN не передаётся или NOLOGIN=0.

С1 запоминает соответствие ID и переданных данных.

ID неизвестен серверу.

Попытка авторизоваться по неверному или устаревшему идентификатору сессии, попробуйте ещё раз (ссылка — URL к C1).

CHECK=0 и авторизовать пользователя в С2 невозможно.

Сайт XXX (описание: TEXT) требует авторизации, а вы не авторизованы в С2. (TEXT из предыдущего шага)
Логично, если данное сообщение показывает пользователю С2.

Пользователь не доверяет C1 и запретил С2 передавать ей свои авторизационные данные.

Логично, если С2 покажет пользователю ошибку и/или страницу настройки доверия к C1.

C1 не ответила или ответ не в формате JSON. MIME-тип, опять-таки, не проверяется.

Сайт XXX (описание: TEXT) недоступен с сервера авторизации. Код HTTP: XXX; MIME-тип ответа: XXX.

C1 ответила хешем в формате JSON, в котором присутствует поле «error» с непустым значением EEE:

Сайт XXX (описание: TEXT) сообщает об ошибке авторизации: EEE.
Ошибками EEE могут быть, например:
  • ID неизвестен клиенту:
    Попытка авторизоваться по неверному или устаревшему идентификатору сессии, попробуйте ещё раз (ссылка).
  • Известному на клиенте ID соответствует неверный ключ KEY:
    Секретный ключ неверен, проверка подлинности сервера авторизации XXX не удалась. Возможно, вас дурят.
  • Не вышло декодировать DATA или считать NOLOGIN:
    Формат данных, переданных с сервера авторизации, неизвестен. Опционально — показать также и сами данные.
  • Не удалось сохранить данные в хранилище состояний клиента (базе данных, кэше и т. п.):
    Внутренняя ошибка клиента — хранилище состояний недоступно.
4 С2 → (перенаправление браузера пользователя) → C1 с параметрами ga_client=1&ga_id=ID&ga_res=CODE
  • CODE — HTTP-код статуса, полученный от POST-запроса из предыдущего пункта.

C1 может взять сохранённые в предыдущем пункте данные и на их основе авторизовать пользователя.

ID неизвестен клиенту:

Попытка авторизации по неверному или устаревшему идентификатору сессии, либо сервер авторизации не смог передать данные учётной записи. Попробуйте ещё раз (ссылка).

Самая противная ошибка — это Redirect-Loop — бесконечное перенаправление, которое можно спровоцировать, если при ошибке авторизации никак не сохранить факт ошибки в браузере пользователя (например, с помощью cookie) и вместо того, чтобы показать страницу с ошибкой, отправить пользователя обратно на шаг 2 (через шаг 1).

Важный момент: после успешной обработки любого запроса глобальной авторизации (запроса с параметрами ga_id или ga_client и т. п.), пришедшего со стороны браузера пользователя, как клиентской, так и серверной стороной, должно осуществляться перенаправление (HTTP 302 Moved temporarily) на адрес, не содержащий в себе параметров ga_id и т. п., для исключения повторной отправки запроса пользователем при нажатии «Обновить страницу».

И ID и ключ являются «секретными», но ID знают и сервера, и пользователь (ID передаётся в браузер), а ключ — только сами сервера. За счёт этого достигается безопасность: пользователь не может сам передать произвольные данные авторизации на сервер, не зная ключа. Для дополнительной защиты всё это можно просто пустить через HTTPS (SSL). Понятно, что кэш, в котором сохраняются соответствия ID и ключа, не должен быть доступен для чтения внешнему пользователю, иначе вся защита накрывается медным тазом. Для усиления защиты опять-таки можно дополнительно создать список доверенных серверов и при приёме данных авторизации проверять IP.

Как уже было сказано выше, данные авторизации — произвольные в JSON-формате. Однако, удобно специфицировать его чуть точнее: хеш, в котором есть поля user_name (логин), user_email (адрес электронной почты) и необязательные поля user_url (URL «домашней страницы» пользователя) и user_real_name («настоящее имя» пользователя).

OpenID, на самом деле, работает похоже, но почему-то адски глючит и его страшно использовать на своих внутренних ресурсах. :)

В реализации протокола очень желательна обработка ошибок и их показ пользователю в удобном виде, чтобы не получалось, как в OpenID — «она утонула» © (то есть «произошла ошибка» без каких-либо деталей).

FoF_Sudo

FoF_Sudo — беспарольная авторизация типа «от системы к системе».

Идея такая: пусть есть некая система (например FoF — фидридер), в которой действует глобальная авторизация (в качестве сервера выступает другая система), и пусть эта система должна авторизоваться на доверенных серверах (в примере — FoF должен забирать защищённые RSS-ленты от имени разных пользователей). Но при этом (!) пароль пользователя хранить в открытом виде в базе FoF при этом нельзя. Да если бы и было можно, в данных глобальной авторизации его просто нету. При этом нужно, чтобы при передаче такой вот беспарольной авторизации какой-то совершенно левый пользователь не смог сделать так же и зайти под произвольным пользователем.

Как это сделать? Ответ — одноразовые ключи на каждый запрос:

  • FoF видит в урле рсс’ки &fof_sudo=(что_угодно). Что_угодно — совершенно что угодно, единственно, в случае FoF оно должно быть разное для разных пользователей, иначе FoF двух пользователей подпишет на один и тот же защищённый фид, а обновлять будет вообще как попало — то от имени одного, то от имени другого пользователя.
  • FoF генерирует случайный ID, запоминает соответствие этому ID пользователя и добавляет в запрос кукис:
    • Cookie: fof_sudo_id=ID
  • Система, к которой произошло обращение, видит этот кукис (fof_sudo_id) и делает обратный запрос к fof: /fof-sudo.php?id=ID
  • FoF отвечает данными пользователя, которые он помнит по этому ID, в формате JSON ({'user_name':'user@custis.ru'}) и забывает ID
  • Внешняя система верит, что теперь надо авторизоваться под именем юзера user@custis.ru (и, например, отдаёт правильную RSS’ку)
Warning Warning: И это тоже покрывается OAuth2.