Глобальная авторизация в веб-системах

Материал из YourcmcWiki
Перейти к: навигация, поиск

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

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

  • (П) Пользователь.
  • (С1) Система 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 С1 → (перенаправление браузера пользователя) → С2 с параметрами ga_id=ID&ga_url=URL&ga_check=CHECK
  • URL — URL для возврата на С1, на который С2 будет передавать данные и на который же С2 будет отправлять пользователя редиректом обратно.
  • Если CHECK=0 или не передаётся, и пользователь не авторизован в С2, она должна потребовать от него авторизоваться.

С2 даётся возможность прочитать cookie пользователя и получить данные о нём.

3 С2 → (делает POST-запрос напрямую) → С1 с параметрами 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 и переданных данных.

4 С2 → (перенаправление браузера пользователя) → С1 с параметрами ga_client=1&ga_id=ID&ga_res=CODE
  • CODE — HTTP-код статуса, полученный от POST-запроса из предыдущего пункта.

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

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

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

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

Что ещё нужно (TODO): обработка и передача описаний ошибок между серверами для их показа пользователю в удобном виде, чтобы не получалось, как в OpenID — «произошла ошибка» («она утонула» ©).

Плюс FoF

(Тем кто читает это в вебе, можно в принципе и не читать)

Теперь пусть есть некая система типа FoF, в которой действует данный метод глобальной авторизации, и которая должна авторизоваться на доверенных серверах, на которых эта глобальная авторизация тоже действует. Естественно, пароль пользователя хранить в открытом виде в базе при этом нельзя, да его в данных глобальной авторизации и нету. Пример такой ситуации — это FoF (фидридер) — он должен забирать RSS-ленты пользователя, в которых пользователь тоже авторизуется.

  • FoF видит в урле рсс’ки &fof_sudo=1, генерирует случайный ID и добавляет в запрос
    • Cookie: fof_sudo_id=ID
  • Генератор рсс видит cookie fof_sudo_id и делает запрос к fof: /fof-sudo.php?id=ID
  • FoF отвечает {'user_name':'user@custis.ru'} и забывает ID
  • Генератор рсс принимает тот факт, что теперь он работает под юзером user@custis.ru и отдаёт правильную рсс’ку