У 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 за то что текст который вы прочли был куда лучше того который я написал.


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

Комментарии (10)


  1. xystarcha
    10.09.2018 09:33
    +2

    Наверное, я плохо прочёл текст, но мне показалось, что там говорится, что аутентификация — это определение «кто попало»/ «не кто попало», а авторизация -определение «кто именно». На самом деле немного наоборот аутентификация это «кто именно» а авторизация «какие права»


    1. lostpassword
      10.09.2018 10:26

      В моём понимании, например, ступени такие:

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


      1. ta6aku Автор
        10.09.2018 12:24

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

        С аутентификацией и идентификацией проблема в том, что способы аутентификации без идентификации весьма редки. Watermarking возможно можно отнести к одному из них:

        en.wikipedia.org/wiki/Category:Watermarking

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


    1. leschenko
      10.09.2018 11:01

      Я на этом пункте перестал читать текст. Т.к. автор путает основные термины, то и дальше будет каша.


      1. ta6aku Автор
        10.09.2018 12:07

        Собственно, не только автор путает термины, путаница с терминами начинается с самого их определения, взять википедию, например.

        > Authentication is the act of confirming the truth of an attribute of a single piece of data claimed true by an entity.

        Описание, мягко говоря, непростое для понимания.
        Дальше с этим пытаются бороться, поясняя:

        > In contrast with identification, which refers to the act of stating or otherwise indicating a claim purportedly attesting to a person or thing's identity, authentication is the process of actually confirming that identity.

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

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

        > It might involve confirming the identity of a person by validating their identity documents, verifying the authenticity of a website with a digital certificate,[1] determining the age of an artifact by carbon dating, or ensuring that a product is what its packaging and labeling claim to be. In other words, authentication often involves verifying the validity of at least one form of identification.

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

        И да, везде про аутентификацию написано длинно, сложно и на нее старательно пытаются навесить функции авторизации и идентификации ей не свойственные. Из-за чего и начинается путаница.


        1. noodles
          12.09.2018 01:20

          везде про аутентификацию написано длинно, сложно и на нее старательно пытаются навесить функции авторизации и идентификации ей не свойственные. Из-за чего и начинается путаница

          Я себе запомнил так:

          регистрация = заказ столика в ресторане

          идентификация (ввод логина) = пришёл, подходишь к администратору ресторана, говоришь я здесь заказывал столик

          аутентификация (ввод пароля) = администратор говорит — докажи что ты это ты) Ты показываешь права или паспорт

          авторизация = администратор смотрит в список гостей и говорит — ваш вон тот столик у туалета


  1. chibitko
    10.09.2018 12:07

    OAuth — это протокол авторизации, а не аутентификации (https://ru.wikipedia.org/wiki/OAuth), а вот OpenID Connect — это протокол аутентификации по верх протокола авторизации (такая вот чехарда), т.к. наличие клэймов в JWT-токене (конкретно в id_token) гарантирует только само наличие, а вот автрозизацией уже будет заниматься приложение доверяющее, например, списку ролей в клэймах


    1. ta6aku Автор
      10.09.2018 12:12

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

      OpenID Connect по спецификации вообще слой идентификации поверх OAuth 2.0. Но тут, понятно, писать что это протокол авторизации поверх другого протокола, который тоже описан как протокол авторизации, было бы странно.

      Снова к описанию из вики:

      > Authorization is the function of specifying access rights/privileges to resources related to information security and computer security in general and to access control in particular.[1] More formally, «to authorize» is to define an access policy.

      Пользовательские роли, описывающие его access rights/privileges оказываются как раз среди клеймов в JWT, то есть в OpenID Connect.

      Я понимаю что спорить с официальными спеками довольно спорное развлечение, но тут другого варианта не вижу.


      1. mayorovp
        10.09.2018 17:14

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

        OpenID Connect — это протокол аутентификации и, возможно, авторизации пользователя поверх протокола авторизации приложения.


        1. ta6aku Автор
          10.09.2018 18:34

          Да, спасибо, сам я это сформулировать не осилил.
          OAuth авторизует приложение и аутентифицирует клиентское приложение, OpenID Connect авторизует пользователя для приложения.