Глобальная авторизация в веб-системах
…или как реализовать простой Single Sign-on в веб-системах.
Ниже описан простейший протокол, который даёт возможность нам сказать внешней системе, кто к нам вошёл, так, что внешняя система знает, что это говорим ей именно мы, а мы знаем, что мы говорим это именно ей.
- (П) Пользователь.
- (С1) Система 1 — клиент глобальной авторизации. В неё пришёл пользователь без авторизации.
- (С2) Система 2 — сервер глобальной авторизации. В ней пользователь уже авторизован, скорее всего, через cookie.
шаг | кто | что делает | кому |
---|---|---|---|
(П) → | (переходит по ссылке) → | C1 с любыми параметрами. ⇒ C1 хочет перенять авторизацию у С2. | |
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
⇒ С2 даётся возможность прочитать cookie пользователя и получить данные о нём. |
3 | С2 → | (делает POST-запрос напрямую) → | С1 с параметрами ga_client=1&ga_id=ID&ga_key=KEY&ga_data=DATA&ga_nologin=NOLOGIN
⇒ С1 запоминает соответствие ID и переданных данных. |
4 | С2 → | (перенаправление браузера пользователя) → | С1 с параметрами ga_client=1&ga_id=ID&ga_res=CODE
⇒ С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 и отдаёт правильную рсс’ку