Pull to refresh

OpenID Connect 1.0 На Пальцах

Reading time7 min
Views68K

У OpenID Connect есть спецификация, есть туториалы, статьи на хабре и не на хабре. Довольно бессмысленно лепить очередную пошаговую инструкцию, ведущую от глубокого недоумения к работающей сквозной авторизации и аутентификации. Задача текста ниже иная, описать лежащие в основе спецификаций идеи (их больше одной).


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



1.


Задача минимум — просто не пускать кого попало к какому-то своему ресурсу. Закрываем его логином\паролем, кто знает подходящую пару из логина и пароля на ресурс попадет, кто нет — нет. Эта штука называется аутентификация, для нее можно использовать не только логины с паролями (код из СМС, например, или аппаратный USB-ключик), но это детали несущественные для нашей темы. Так же опущу обязательный абзац про опасность передачи паролей через интернет в открытом виде, за которую мы все не любим Basic access authentication.


Лучше замечу вот что: никто из пользователей не любит вводить логины с паролями. Коды в СМС ничуть не лучше, а USB-ключи так и вовсе просто ненавидят. Чтобы не заставлять пользователя вводить логин с паролем на каждый запрос сервер в ответ на них присылает строчку абракадабры, именуемую сессионным ключем. И дальше этот ключ цепляется клиентом к каждому запросу на сервер (обычно HTTP-заголовком, но это не существенно), и сервер проверяет есть ли у него такая сессия.


Cессия с ключиком — явления, по определению, временные, золотое сечение для времени жизни сессии составляет, приблизительно, “пока открыта вкладка браузера, но не дольше суток”


2.


Пустили кого надо — это хорошо. Теперь нужно понять кого именно мы пустили. И не только вывести то что он ввел в качестве имени в правом верхнем углу, но и решить к чему его подпускать, а чему нет.


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


3.


Ок. У нас есть сайт, на сайте что-то секретное, на входе в секретную часть мы требуем пароль, каждому показываем только его секретики, и не показываем чужие. Жизнь не стоит на месте, и у нас появился еще один сайт. И тут мы снова встречаемся с проблемой из пункта 1, никто не любит вводить логины и пароли! Можно объединить базу пользователей и это избавит их от необходимости регистрироваться дважды, но как избавить их от повторного ввода логина и пароля на входе? С учетом существования такой штуки как Same Origin Policy (а сайты наши расположены, естественно, на разных доменах, значит куки с ключиком сессии одного другому не видны)? Тут, для придания важности моменту, я начну новый пункт.


4.



SSO, Single Sign On — какой бы ни была реализация, майкрософтовский Kerberos, SAML или что-то OAuth 2.0, поверх которого построен OpenID Connect, про который я вам тут пишу, на самом деле под капотом всегда одно и то же: есть отдельный сервер авторизации, и любой желающий авторизовать пользователя перенаправляет пользователя на него. Если пользователь уже авторизован, сессия подхватывается, и он тут же улетает с сервера авторизации обратно и попадает куда хотел. Если не авторизован — сервер авторизации решает эту проблему как умеет, запросом логина с паролем как правило, и уже при успешном решении отправляет пользователя обратно.


При этом SAML на данный момент решение, так скажем, устаревшее. А Kerberos вообще совершенно отдельное закрытое майкрософтовское волшебство, сильно выходящее за рамки протокола HTTP. Ну а мы сосредоточимся именно на нем. И тут же подойдем к следующей проблеме.



Уже есть понятный сценарий работы — в любой непонятной ситуации отправляй пользователя на сервер авторизации, пусть тот решит что с ним делать и вернет готовый ответ. Но как именно сервер авторизации расскажет тому, другому серверу о том что пользователь авторизован? Тут мы возвращаемся снова к идеям пункта первого, а именно — к сессионному ключу. Вернемся к истокам: наличие сессионного ключа — признак авторизованности, сам сессионный ключ отпирает дверку к информации о пользователе, и, вы не поверите, к информации о сессии. Значит сервер авторизации авторизует и отдает сессионный ключ другому серверу.


Теперь, правда, он называется уже не сессионный ключ, а токен.
А точнее говоря (по протоколу OAuth 2.0, поверх которого написан OpenID Connect) это сразу два токена — Access Token, чтобы цеплять его ко всем запросам как деды цепляли ключи сессии, и Refresh Token, чтобы обновлять Access Token когда он протухнет.


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


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


6.


С одной стороны не сложней, а наоборот проще. Можно сделать редирект и не возиться самому с отрисовкой логинно-парольной формы. С другой стороны — очень, очень не хочется таскать токены через браузер в открытом виде. Это почти так же омерзительно небезопасно как не зашифрованный пароль в Basic access authentication. А ведь никто не хочет повторить ту старую страшную ошибку.


Решение проблеме нашлось не сказать чтоб очень изящное, но рабочее. Сперва все идет как обычно, переход на авторизацию, собственно авторизация. Потом, когда наступает время вернуться назад с токенами, происходит обратный редирект. Но вместо токенов к адресу возврата прицепляется одноразовый код. Одноразовый код только что порожден сервером авторизации только для этого конкретного момента. У него очень малое время жизни. Едва получив одноразовый код другой сервер должен подоткнуть юбки, выпучить глаза и срочно нестись на сервер авторизации снова — чтобы по одноразовому коду получить таки вожделенные токены.


Для похода с кодом за токенами на сервере авторизации есть специальный ресурс. Принимает он, по спецификации, уже не GET, а POST. Что как-бы намекает нам, что делать этот запрос нужно не из браузера, а с сервера на сервер.


По этой же причине на любом уважающем себя сервере авторизации CORS для POST запросов запрещен.


7.


Кстати, вы еще помните про аутентификацию и авторизацию? Аутентификация — это когда кого-то просто пускают по логину и паролю, или не пускают. А авторизация — это когда уже пустив, начинают разбираться кого же именно пустили.


А помните про OAuth 2.0? Я упоминал его пару раз выше, как некий фундамент для OpenID Connect.


А помните про OpenID Connect? Это ведь статья как раз он нем.


Так вот, OAuth 2.0 — это аутентификация. Вся описанная ранее слегка запутанная процедура с тремя участниками, паролем, кодом и токеном — это все про аутентификацию, просто про то чтобы кого-то запустить куда-то. Протокол OAuth 2.0.


OpenID Connect же — это авторизация. То есть к OAuth он добавляет те части, где выясняется кого пустили.


Для этого в список токенов добавляется еще один, называется он ID Token. Те кто прошел по ссылке возможно удивлены, не встретив по ней ничего ни про какой ID Token. Пусть удивление не переходит в испуг, ID Token это и есть JWT, возвращаемый как base64-encoded матрешка в том же JSON, что и Access Token и Refresh Token. В любом случае, все что вы хотели знать о пользователе — в нем.


И еще есть специальный ресурс на сервере авторизации под названием userinfo, куда можно стукнуться с Access Token, и получить в ответ тот же JSON, что и в ID Token. Только зачем он нужен, если ID Token уже есть? Вопрос к авторам спеки.


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


8.


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


Client ID и Client Secret. Client на языке протокола OpenID Connect это вовсе не браузер, а тот самый другой сервер, которому нужно авторизовать пользователя. Предположим, у вас есть сайт, и вы хотите прикрутить к нему модную авторизацию через фейсбук. И через гугол. И уже не такую модную через твиттер. Реализовать в коде протокол будет не достаточно. Вам еще нужно будет зарегистрироваться в фейсбуке, и в гугле, и в твиттере, но не в качестве пользователя, а в качестве того самого клиента, который, как сервер, может пользоваться их авторизацией. При регистрации вы получите от условного фейсбука Client ID и Client Secret. И при запросе авторизации среди прочего отправите Client ID. А когда пойдете с одноразовым кодом за токеном от вас еще и Client Secret потребуют.


Redirect URI. Тут все просто. Отправляя пользователя на условный фейсбук авторизовываться, нужно сообщить фейсбуку куда ему возвращать коды и токены после авторизации. Конечно, вы все равно передаете ему свой Client ID. Но отдельный Redirect URI позволяет перебрасывать после авторизации разных пользователей на разные страницы, например админов на админку, а рядовых пользователей на их персональные странички. Практично. К тому же, прописанный в настройках клиента на условном фейсбуке разрешенный список возможных Redirect URI это дополнительная безопасность.


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


State. Помните про одноразовый code, по которому выдаются токены, как талончик в электронной очереди? Так вот, state — это code наоборот, если code сервер авторизации выдает другому серверу чтобы тот его вскоре вернул, тот state другой сервер выдает серверу авторизации чтобы тот вернул его при редиректе. Нужен он, насколько я понимаю, в случае если другой сервер уже успел создать свою собственную сессию, чтобы она во всех этих редиректах не потерялась.


Есть еще другие параметры, вроде типа запроса авторизации и времени жизни токенов, но чтобы понять зачем они я вам не нужен.



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


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


Спасибо JM за то что текст который вы прочли был куда лучше того который я написал.


Удачи вам, и не забывайте вовремя обновлять сертификаты.

Tags:
Hubs:
Total votes 11: ↑10 and ↓1+9
Comments13

Articles