Недавно мы в Voximplant улучшали авторизацию в SDK. Посмотрев на результаты, я несколько опечалился, что вместо простого и понятного токена их стало две штуки: access token и refresh token. Которые мало того что надо регулярно обновлять, так еще документировать и объяснять в обучающих материалах. Помня, что в OAuth два токена нужны в основном из-за разных сервисов, на которых они используются (даже вопрос на stackoverflow есть), а у нас такой сервис один, я несколько офигел и пошел на второй этаж вытрясать души из разработчиков. Ответ получился неожиданным. Его нет на stackoverflow. Зато он есть под катом.

Зачем вообще нужны токены


При разработке приложений, общающихся с сервером, обычно вылезают следующие этапы:

  1. Приложение разрабатывает один разработчик, нет ни идентификации, ни аутентификации, ни авторизации (кстати, рекомендую замечательную хабрастатью на эту тему от DataArt), запросы напрямую ходят к эндпоинтам сервера, разработчик счастлив.

  2. Появляется второй разработчик, или тестировщик, или заказчик. Серверу становится важно знать, кто именно отправляет запрос. Добавляется шаг идентификации: простенькое окно «представьтесь» перед стартом приложения и крики «кто опять заходил под тремя единичками и все сломал?!?»

  3. Когда команда чуток устанет от «кто заходил под тремя единичками», на backend запилят форму регистрации с логином-паролем, которая будет проверять на уникальность логина. В таком виде продукт и будет разрабатываться до первой бета версии.

  4. В первой бета версии окажется, что сохранять пароль в plain text на устройстве — это как-то не очень безопасно. У сферического пользователя в вакууме один пароль от большинства сервисов, и очень не хочется быть крайним, через кого у пользователя взломали мейл, вконтактик и все остальное. Начинаются поиски решения «что бы такое сделать, чтобы пароль в явном виде не хранить». И через некоторое время команда приходит к тому или иному варианту «auth token».

Зачем нужен первый токен


Есть много разных токенов. Обычные, криптографические, «access key», «session token», разные схемы получения, использования и revoke. При этом одна из ключевых идей заключается в том, что если кто нехороший получит чужой токен, то самое неприятное, что случится — это доступ похитителя к сервису, от которого токен похищен. Пароль, тот самый, который один на все сервисы, похититель не получит. А пользователь, если поймет, что кроме него к сервису получил доступ кто-то другой, может токен отозвать. После чего получить себе новый, имея логин и пароль.


Зачем нужен второй токен


В OAuth 2 и некоторых других схемах авторизации (например, у нас) есть не один, а целых два токена. Первый, access token, используется при запросах к серверу (например, при логине). У него есть два свойства: он многоразовый и короткоживущий. Наш, к примеру, живет 48 часов, а у кого-то 30 минут. Время выбирается на основании того, как используется сервис. Второй, refresh token, используется для обновления пары access и refresh токенов. У него тоже есть два свойства, обратные первому токену: он одноразовый и долгоживущий. Совсем долгоживуший, наш живет месяц.

Схема использования у токенов следующая:

  • Пользователь логинится в приложении, передавая логин и пароль на сервер. Они не сохраняются на устройстве, а сервер возвращает два токена и время их жизни
  • Приложение сохраняет токены и использует access token для последующих запросов
  • Когда время жизни access token подходит к концу (приложение может само проверять время жизни, или дождаться пока во время очередного использования сервер ответит «ой, всё»), приложение использует refresh token, чтобы обновить оба токена и продолжить использовать новый access token

Примерно так это все объяснено в документации OAuth, на Википедии, в нашей документации. И такое объяснение не отвечает на вопрос нафига?!? Зачем нужны два токена, если можно обойтись одним? В вопросе на stackoverflow даны несколько объяснений уровня «ну, access token можно менее надежно хранить чем refresh token и не бояться использовать вне HTTPS соединений. Например, хранить access token на frontend, а refresh token на backend» или «refresh token используется для доступа к заведомо безопасному сервису, а access token'ом потом можно тыкать во всякие подозрительные места и не очень бояться, что его сольют». Это может быть разумно для авторизации через Facebook и последующего использования без HTTPS. Но у нас-то везде HTTPS! И сервис у нас тоже один, наш. Зачем нам второй токен?

Зачем на самом деле нужен второй токен



Все оказалось и проще, и сложнее чем я думал. Следите за руками:

Случай 1: Боб узнал оба токена Алисы и не воспользовался refresh

В этом случае Боб получит доступ к сервису на время жизни access token. Как только оно истечет и приложение, которым пользуется Алиса, воспользуется refresh token, сервер вернет новую пару токенов, а те, что узнал Боб, превратятся в тыкву.

Случай 2: Боб узнал оба токена Алисы и воспользовался refresh

В этом случае оба токена Алисы превращаются в тыкву, приложение предлагает ей авторизоваться логином и паролем, сервер возвращает новую пару токенов, а те, что узнал Боб, снова превратятся в тыкву (тут есть нюанс с device id, может вернуть ту же пару что и у Боба. В таком случае следующее использование refresh токена превратит токены Боба в то, что изображено справа).

Таким образом, схема refresh + access токен ограничивает время, на которое атакующий может получить доступ к сервису. По сравнению с одним токеном, которым злоумышленник может пользоваться неделями и никто об этом не узнает.
Поделиться с друзьями
-->

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


  1. lair
    06.03.2017 11:27
    +5

    Вообще-то, спецификация OAuth 2 не требует инвалидации ранее выданных токенов при выдаче нового refresh token:


    The authorization server MAY revoke the old refresh token after issuing a new refresh token to the client.


    1. eyeofhell
      06.03.2017 11:55
      +1

      Звучит разумно. Если такая защита не нужна — зачем заставлять разработчиков ее делать? Поинт в том, что так можно и оно действительно неплохо защищает, если access достаточно короткоживущий.


      1. lair
        06.03.2017 11:59
        +1

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

        Это, гм, иллюзия. Если в качестве Алисы у вас unattended service, и Боб успешно обменял свой refresh на новый, у Алисы случился отказ в обслуживании, и сколько он продлится — зависит исключительно от того, насколько там подвижные админы. И все это время Боб будет иметь доступ к системе.


        1. eyeofhell
          06.03.2017 12:08
          +3

          Unattended это все ж таки другой кейс. И, в любом случае, он об этом сможет написать админам :) А если слился обычный токен — то никто об этом не узнает, нет каких-то сложных эвристик с IP


          1. lair
            06.03.2017 12:11
            +1

            А если слился обычный токен — то никто об этом не узнает, нет каких-то сложных эвристик с IP

            Вот поэтому обычные токены в схеме с рефрешами делают очень короткоживущими.


            (BTW, если он слился у вас, об этом точно так же никто не узнает)


            1. eyeofhell
              06.03.2017 12:14

              Об этом узнает пользователь SDK через 2-е суток, когда аппа снова попросит логин и пароль.


              1. lair
                06.03.2017 12:19
                +1

                Неа.


                Если Боб использует только аксесс, у пользователя SDK обмен рефреша на новый пройдет совершенно безболезненно. Поэтому Боб может радостно использовать аксесс до того момента, пока тот не будет инвалидирован (что скорее всего будет сделано по окончанию его срока годности).


                Иными словами, если хитрый Боб перехватил аксесс в момент выдачи, то у него — в вашем случае — есть двое суток на неотслеживаемый несанкционированный доступ.


                1. eyeofhell
                  06.03.2017 12:59

                  Двое суток — это лучше, чем два месяца. А время жизни и подкрутить можно :)


                  1. lair
                    06.03.2017 13:23

                    … это не отменяет того милого факта, что Боб творит свои акции незаметно.


                    1. eyeofhell
                      06.03.2017 13:46
                      -1

                      Для остальных есть Diffie-Hellman, Certificate Authority, server-side challenge и прочие радости криптографии.


                      1. lair
                        06.03.2017 13:48
                        +3

                        Ну просто заявленное вами в посте не очень достигнуто.


                      1. saipr
                        07.03.2017 09:52
                        -3

                        Облачный токен PKCS#11

                        и аутентификации в ОС семейства «Linux» с использованием смарт-карт или USB-токенов на базе российской криптографии


        1. dark_dwarf
          07.03.2017 09:19

          А вам не кажется, что фиг с ней с Алисой (раз уж допустила утечку токенов), а гораздо важнее не допустить «учетку данных» к Бобу? И на это в стандарте даже дается намек:

          If a refresh token is compromised and subsequently used by both the attacker and the
          legitimate client, one of them will present an invalidated refresh
          token, which will inform the authorization server of the breach.


          Таким образом, если refresh_token использован более 1 раза (Боб обновил, а за ним и Алиса пришла), сервер вполне имеет право отозвать и ранее выданные Бобу токены (access & refresh).


          1. lair
            07.03.2017 10:07

            А вам не кажется, что фиг с ней с Алисой (раз уж допустила утечку токенов)

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


            а гораздо важнее не допустить «учетку данных» к Бобу?

            Уже допустили: как минимум на время жизни access token.


            Таким образом, если refresh_token использован более 1 раза (Боб обновил, а за ним и Алиса пришла), сервер вполне имеет право отозвать и ранее выданные Бобу токены (access & refresh).

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


            1. dark_dwarf
              07.03.2017 13:35

              Во-первых, именно Алиса приносит деньги


              Иногда репутационные потери гораздо выше потери нескольких не очень аккуратных клиентов. Короче зависит.

              Иногда деньги приносит не Алиса, а тот ресурс, к которому Алиса «теряет ключи (токены)».

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

              Тут нет однозначного ответа.

              токенов допустила именно Алиса


              Что неужели сам authorization server?

              Уже допустили: как минимум на время жизни access token.


              Не совсем верно. Если по факт повторного использования refresh_token-а отзываются все токены выданные ранее этому пользователю, то на время от 0 до жизни access_token — зависит как Алиса придет обновлять токены.

              Сервер, несомненно, имеет право. Вопрос в том, будет ли он это делать


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

              Если в качестве Алисы у вас unattended service, и Боб успешно обменял свой refresh на новый, у Алисы случился отказ в обслуживании, и сколько он продлится — зависит исключительно от того, насколько там подвижные админы. И все это время Боб будет иметь доступ к системе.


              Собственно я не согласен с утверждением «всё это время» — не всё, если сервер готов защищаться активно (в том числе от нерасторопных Алис).


              1. lair
                07.03.2017 13:39

                Что неужели сам authorization server?

                Много вариантов.


                зависит как Алиса придет обновлять токены.

                … а придет она по истечении access-токенов.


                Собственно я не согласен с утверждением «всё это время» — не всё, если сервер готов защищаться активно (в том числе от нерасторопных Алис).

                Не просто активно, а аггрессивно. Что, как и любое повышение безопасности, будет идти за счет снижения комфорта клиентов.


                1. dark_dwarf
                  07.03.2017 18:43

                  Много вариантов.


                  Например? OAuth2.0/OpenID оно ж только через SSL, так что если только SSL сломают.

                  а придет она по истечении access-токенов.


                  Согласен, но ещё зависит как быстро Боб получил Алисины токены. Если вместе с ней, то да — время утечки равно времени жизни access-токена (если других мер не применяется).

                  Что, как и любое повышение безопасности, будет идти за счет снижения комфорта клиентов.


                  абсолютно согласен.


                  1. lair
                    07.03.2017 18:47

                    Например? OAuth2.0/OpenID оно ж только через SSL, так что если только SSL сломают.

                    Ну да, если есть контроль за устройством, можно подсадить свой рутовый сертификат и устроить MitM. Это самое простое, что в голову приходит.


                    Согласен, но ещё зависит как быстро Боб получил Алисины токены. Если вместе с ней, то да — время утечки равно времени жизни access-токена

                    Ну, мы вроде как исходим из того, что у него есть и access, и рефреш, а выдаются они одновременно.


    1. centur
      06.03.2017 11:58
      +3

      но там есть оговорки, все же абзац весь выглядит так :


      The authorization server MAY issue a new refresh token, in which case
      the client MUST discard the old refresh token and replace it with the
      new refresh token. The authorization server MAY revoke the old
      refresh token after issuing a new refresh token to the client. If a
      new refresh token is issued, the refresh token scope MUST be
      identical to that of the refresh token included by the client in the
      request.

      А вообще протокол часто используется не по делу =) для аутентификации, а не авторизации (хотя на фоне всех остальных протоколов аутентификации — он прост и понятен...)


      Вообще есть куда более простое объяснение — момент аутентификации\авторизации — более уязвим для атакующего чем момент доступа к ресурсам..


      Т.е. если Ева перехватила оба токена — то тут все довольно плохо — в зависимости от реализации — она может выдавить легального пользователя Алису. Но такой перехват — сложнее потому что нужно угадать момент.


      Перехват же access Token — проще — любой запрос и токен в заголовке. Просто и ценность такого токена низкая -его хватит только до конца его времени жизни, а потом он превращается в тыкву. Если сервис например делает время жизни токена в 15 минут — то Ева сможет почитать данные только 15 минут, потом всё.


      Речь конечно не идет о постоянном MitM на Алису — там никакие схемы и токены не помогут...


      1. lair
        06.03.2017 12:00
        +1

        но там есть оговорки, все же абзац весь выглядит так :

        Это оговорки про то, как клиент должен реагировать на новый выданный токен.


      1. eyeofhell
        06.03.2017 12:09

        По поводу сложности перехвата… Сейчас же все по TLS :( Это, ИМХО, уже не так актуально.


        1. lair
          06.03.2017 12:11
          +1

          Это актуально, как только вы попадаете в браузер, где перехват может быть не только в канале.


          1. eyeofhell
            06.03.2017 12:15

            Всегда хотел спросить — а где в таком браузере будет храниться refresh token? :) Ведь если скомпрометирован браузер, то он оба токена сольет. Или есть большая группа ситуаций, где браузер скомпрометирован, но refresh не сольется?


            1. lair
              06.03.2017 12:24
              +2

              А рефреш в браузере и не хранится. Он хранится на сервере, который меняет его на аксесс (обращением к авторизационному серверу), и отдает браузеру, который и идет с этим аксессом к ресурс-серверу.


              В implicit flow (который используется, если у нас только браузер, без активного сервера) рефреши выдаваться не могут: "The authorization server MUST NOT issue a refresh token."


      1. Razaz
        06.03.2017 12:41
        +1

        Для аутентификации там есть OpenId Connect. Голый OAuth2 как-то уже не комильфо.

        Refresh желательно выдавать только через backchannel соединение между серверами (тоесть никаких Implicit и Hybrid грантов, если говорить про OIDC).

        Так же есть уже спека на Proof of Posession.


    1. Neuyazvimy1
      07.03.2017 08:39

      У нас при разработке одной игры издатель отказался нам выдать права админа в гугл плэй сервисах. Тем самым мы не могли правами админа проверять действительно ли игрок получил ачивку. Мы это решили просто, мы отправляли access token игрока нам на сервер, где сервер этим токеном проверялось валидность ачивки. Резюмируя скажу что 2 токена нужны для того чтобы: пользователь мог авторизовываться не светя свой пароль лишний раз, также для удобства обратно же пользователя, ну и для сторонних сервисов как в примере. З.Ы в этой игре у нас тоже были свои токены, access — жил 30 минут, refresh — был перманентен.


  1. lega
    06.03.2017 11:41
    +1

    приложение предлагает ей авторизоваться логином и паролем, сервер возвращает новую пару токенов, а те, что узнал Боб, снова превратятся в тыкву
    С такой схемой будет невозможно быть залогиненным на двух устройствах? Дом + работа, или ноутбук + мобильный, каждый раз нужно будет вводить пароль? Это не удобно.


    1. napa3um
      06.03.2017 11:50

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


    1. eyeofhell
      06.03.2017 11:53

      См. там в скобках про device id


    1. jackes
      06.03.2017 11:53

      Если я правильно понял, ничто не мешает нам сгенерировать 2 пары токенов: одну для работы, другую для дома


    1. Evengard
      06.03.2017 11:55
      +1

      Суть в том, что при использовании рефреш-токена инвалидируется ТОЛЬКО тот, который использовался. Если у вас два рефреш токена, и вы рефрешнули только первый, второй-то никуда не делся, и остался валидным.


      1. lega
        06.03.2017 12:18

        использовании рефреш-токена инвалидируется ТОЛЬКО тот, который использовался
        В таком случае для второго примера, производный токен Боба будет продолжать работать. Надо инвалидировать все производные ключи (хранить деревья). И инвалидный refresh токен можно обноаить с паролем, иначе Алиса не сможет себе его обновить.


        1. lega
          06.03.2017 12:25

          Тогда Бобу надо испортить/стереть токены Алисы при копировании, чтобы она сгенерировала себе новые независимые токены. Возможно она не будет деактивировать все устройства, только из-за того что с неё очередной раз запросили пароль. В результате и Боб и Алиса будут иметь рабочие, обновляемые токены.


  1. Razaz
    06.03.2017 12:35
    -2

    Основной смысл Refresh token — оффлайновый доступ — даже называется offline_access. Access of a protected API as proof of authentication: Refresh tokens and assertions can be used to get access tokens without the user being present, and in some cases access grants can occur without the user having to authenticate at all.

    Что бы креденшалы не вводить — на AS/Idp тупо используется SSO сессия и при протухании access_token и валидной сессии ничего вводить не надо.

    Кейс Refresh token — вы дали авторизовали сервис, который автоматом делает вам красивые галереи из ваших фоток в фэйсбуке. Выгружать их он может и без вас, сидящего за компом, поэтому он попросит скоуп offline_access и будет получать токен для вызова апи когда ему надо.


    1. eyeofhell
      06.03.2017 13:04

      Кейс Refresh token — вы дали авторизовали сервис, который автоматом делает вам красивые галереи из ваших фоток в фэйсбуке. Выгружать их он может и без вас, сидящего за компом, поэтому он попросит скоуп offline_access и будет получать токен для вызова апи когда ему надо.


      Что мешает этот кейс сделать с одним токеном?


      1. Razaz
        06.03.2017 13:14

        Каким?


        1. eyeofhell
          06.03.2017 13:47
          -1

          Просто токен. GUID. В иллюстративных целях назовем его «razaz token» и будем выдавать после логина.


          1. Razaz
            06.03.2017 14:00

            И что вы с ним будете делать? :) Это как я понимаю ссылка на нормальный токен, который храниться на AS/IdP?
            Refrsh token — это креденшалы. По которым приложение от имени пользователя, который дал разрешение на этом может получить доступ в API.


            1. eyeofhell
              06.03.2017 14:28

              «Выгружать их он может и без вас, сидящего за компом, поэтому он попросит скоуп offline_access и будет получать токен для вызова апи когда ему надо.» < — а вот это и буду делать. Фоточки выгружать. Чем один токен при таком использовании хуже?


              1. Razaz
                06.03.2017 14:35

                Какой один? access_token короткоживущий. А refresh на недели выдаваться может.
                Вы уточните что вы подразумеваете под токеном и какие метаданные о нем вы храните.


                1. eyeofhell
                  06.03.2017 14:43

                  Просто токен. GUID. В иллюстративных целях назовем его «razaz token» и будем выдавать после логина, хранить на стороне Authority что сочтем нужным. Чем такой токен плох для offline, о котором вы писали?


                  1. Razaz
                    06.03.2017 14:51

                    1. Вы описали обычный Refresh. Получаете референс/handle(только не гуид, а сгенерированный нормальным криптографическим RNG). Используете его для получения access_token и все.
                    2. Это не стандарт со всеми вытекающими.


                    1. eyeofhell
                      06.03.2017 15:05

                      «Основной смысл Refresh token — оффлайновый доступ» слабо коррелирует с «Это не стандарт». Либо основной смысл в том что стандарт, либо что оффлайн. Если оффлайн — то непонятно, зачем для этого два токена, когда можно обойтись одним.


                      1. Razaz
                        06.03.2017 15:09

                        Это стандартный механизм получения доступа к защищенному API при отсутствии активной сессии пользователя(пользователь offline).

                        1. Есть такая штука как audience. Дак вот у refresh_token audience — это ваш AS/IdP, а у access_token — список ресурсов, к которым он применим. Если используются JWT, то в него все зашивается для access_token + скоупы.
                        2. refresh_token — Это креденшалы для аутентификации на AS/IdP. access_token — несет в себе(или ссылается) информацию для авторизации в API.


                        1. eyeofhell
                          06.03.2017 15:13

                          Это вы так прозрачно намекаете, что идея двух токенов в том, чтобы сервер аппы мог использовать refresh_token без авторизации, потому что ему вроде как «доверяют»?


                          1. Razaz
                            06.03.2017 15:26

                            Еще раз:
                            При refresh_topken гранте аутентифицируется клиент при помощи своего способа(basic, mutal tls, client assertions) + предоставляет креденшалы пользователя в виде refresh_token, а не логин/пароль.
                            Это все происходит на AS/IdP, а не на сервере с API.


    1. centur
      06.03.2017 13:36

      https://tools.ietf.org/html/rfc6749#page-10
      Спека с вами не согласна. скоупы там просто для того чтобы определить что можно с токеном звать а что нет, offline_access — это вообще чей то частный скоуп, в спеке ни слова про offline.
      Вы случайно не путаете чью-то реализацию Oauth 2.0 (Facebook? ) с самим протоколом?


      1. Razaz
        06.03.2017 13:43

        http://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess

        Причем тут фэйсбук?
        Все основные AS/Idp уже его поддерживают.


        1. centur
          07.03.2017 04:13
          +1

          топик про Oauth 2.0 не про OpenId Connect


  1. lair
    06.03.2017 13:25
    +2

    Кстати, я вот тут подумал что-то. А у вас клиент неаутентифицированный?


    1. eyeofhell
      06.03.2017 13:50

      Он же парой логин-пароль вначале представляется. Логин его идентифицирует, пароль — аутентифицирует. Сервер потом авторизирует :) Все по фен-шую.


      1. Razaz
        06.03.2017 13:57
        +1

        Клиент!=Пользователь ;)


      1. lair
        06.03.2017 13:57
        +1

        Логин-пароль — это вы, наверное, про пользователя говорите. А я про клиент (в смысле, программу) в терминах OAuth.


        1. eyeofhell
          06.03.2017 14:31

          Мы используем внутри сдк еще токен устройства, который сами генерим.


          1. lair
            06.03.2017 14:33

            Он валидируется сервером, или кто угодно может прислать любой?


            1. eyeofhell
              06.03.2017 14:44

              Валидируется логин+пароль или access token. Device ID позволяет одному пользователю залогиниться с нескольких устройств, зачем его валидировать? Всегда же можно разобрать клиентское SDK и посмотреть, как оно считается.


              1. lair
                06.03.2017 14:50
                +1

                Ну то есть клиент у вас неаутентифицированный. Просто одна из степеней защиты при рефреше токена — это проверка client credentials. Вам она, в силу того, что у вас public client, она недоступна.


              1. Razaz
                06.03.2017 14:54

                То есть у вас комбинация из public client + refresh_token + access_token лайфтайм 2 дня?


                1. eyeofhell
                  06.03.2017 15:09

                  Пока да. Если будут реалистичные кейсы почему это плохо — подкрутим. Да, вообщем-то, если придет клиент и скажет «хочу 10 минут» — ничто не помешает для его акка подкрутить. You are welcome.


                  1. Razaz
                    06.03.2017 15:23

                    1. refresh_token на публичном клиенте(то есть клиент не аутентифицируется сам) это фактически дырень, если у вас нет ротации токенов или какой-то хитрой эвристики, что бы 100% убедиться что это именно тот клиент которому вы его выдали.
                    2. access_token — короткоживущий. Час уже достаточно большое время жизни.


                    1. eyeofhell
                      06.03.2017 15:25

                      refresh_token на публичном клиенте(то есть клиент не аутентифицируется сам) это фактически дырень, если у вас нет ротации токенов или какой-то хитрой эвристики, что бы 100% убедиться что это именно тот клиент которому вы его выдали.


                      Сценарий атаки в студию!


                      1. Razaz
                        06.03.2017 15:27

                        XSS.


                        1. eyeofhell
                          06.03.2017 16:12

                          XSS для Android SDK? Как?


                          1. Razaz
                            06.03.2017 18:05
                            +1

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

                            Тут уже ваше дело — доверяете ли вы Android в хранении таких критических вещей или нет.


  1. GreenStore
    06.03.2017 15:20
    +4

    > Случай 1: Боб узнал оба токена Алисы и не воспользовался refresh
    > Случай 2: Боб узнал оба токена Алисы и воспользовался refresh

    Вопрос: а в случае только одного токена разве не тоже самое можно сделать?

    Копирую почти один-в-один, только с одним токеном:

    Случай 1: Боб узнал токен Алисы и не воспользовался refresh

    В этом случае Боб получит доступ к сервису на время жизни token. Как только оно истечет и приложение, которым пользуется Алиса, воспользуется token, сервер вернет новый токен, а тот, что узнал Боб, превратится в тыкву.

    Случай 2: Боб узнал токен Алисы и воспользовался refresh

    В этом случае токен Алисы превращается в тыкву, приложение предлагает ей авторизоваться логином и паролем, сервер возвращает новый токен, а тот, что узнал Боб, снова превратится в тыкву (тут есть нюанс с device id, может вернуть тот же что и у Боба. В таком случае следующее использование токена превратит токен Боба в то, что изображено справа).

    Ну и зачем тогда пара токенов?


    1. eyeofhell
      06.03.2017 15:24

      Хороший вопрос. А какое время жизни будет у этого токена? Предположим, 30 минут, как обычно у access (у нас 48 часов потому что use case специфичный). Алиса залогинилась, посмотрела фотки, выключила телефон и села в метро. Через час вышла из метро, включила телефон, запустила аппу… и аппа ее просит пароль, потому что токен протух. Не круто :)


      1. VasiliySS
        06.03.2017 17:54
        +1

        Позволять по access делать запросы в течение получаса, а регенерировать новый токен — в течение двух суток (ну или сколько там рефреш живет). По всем кейсам практически то же самое, но токен один.


        1. eyeofhell
          06.03.2017 17:55

          Single Responsibility Principle :)


          1. frol
            07.03.2017 19:13

            Вот это единственное верное оправдание для вашего юзкейса с одним сервером. Всё что приведено в статье можно реализовать на одном токене, как верно предложил VasiliySS, и никакой защищённости в этом нет. Ну, разве что refresh token реже отправляется, но если access token живёт 30 минут, то уже через 30 минут злумышленник достигнет своей цели. OAuth2 НЕЛЬЗЯ использовать в незащищённых подключениях, и это ключевое изменение, которое позволило значительно упростить OAuth1.


            Из хороших объяснений зачем нужно два ключа я для себя выделил одно:


            Использование двух токенов позволяет проверять access token без необходимости хранить его в БД, то есть можно существенно уменьшить нагрузку на БД (избавляемся от одного SQL запроса на каждом HTTP запросе). Как сделать проверку access токена без БД? Элементарно — сервер авторизации должен криптографически подписывать ID пользователя + срок годности токена + случайный текст (то есть access token будет выглядеть как <user_id><expiration_date>), тогда API серверу достаточно проверить цифровую подпись в access token и сверить время жизни, и если всё ОК, то можно считать, что пришёл запрос от пользователя user_id. Очевидный недостаток такого подхода — нельзя отозвать access token, поэтому делают короткоживущий access token и отзывают только refresh token.


        1. lair
          06.03.2017 18:12

          Если по access-токену можно сделать рефреш, то, перехватив access, можно дальше устроить себе сколько угодно рефрешей. Теряете в безопасности.


          1. frol
            07.03.2017 18:58

            Ваше умозаключение верно и для refresh_token. Перехватив regresh_token — можно сколько угодно раз получать новые пары access + refresh токенов.


            1. lair
              07.03.2017 18:59

              Ну да. Если он не отозван (вручную или автоматически).


    1. Rothmansua
      06.03.2017 20:53

      Чтобы реже спрашивать активного пользователя его пароль (см. комментарии ниже)


  1. Veha
    06.03.2017 17:30

    А что если access_token не хранить на стороне приложения, а использовать только для одной сесии и с коротким сроком жизни? А хранить refresh_token и каждый раз при инициализации приложения получать новый access_token и refresh_token. Таким образом если ктото украл токены и использовал refresh_token то при запросе пользователя после инициализации или попытке обновить токены, refresh_token не совпадет с тем что в базе и пользователя попросят ввести логин пароль, что приведет к обновлению refresh_token в базе и украденым уже нельзя будет воспользоватся


    1. eyeofhell
      06.03.2017 17:30

      Да, если нужно усиление безопасности — то можно хоть на каждый коннект :)


    1. lair
      06.03.2017 17:38

      А что если access_token не хранить на стороне приложения, а использовать только для одной сесии и с коротким сроком жизни?

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


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

      А почему не совпадет?


      1. Razaz
        06.03.2017 18:14

        1. Зачем? Как раз все правильно. Запрос на рефреш отправлять если токена нет или истекает.
        2. Если сделана инвалидация старых рефреш токенов то будет такое поведение.

        Тут вопрос в том, что если канал скомпрометирован, то поздно пить Боржоми ;)


        1. lair
          06.03.2017 18:18

          Зачем? Как раз все правильно. Запрос на рефреш отправлять если токена нет или истекает.

          Потому что если "не хранить" access-токен, то у нас всегда сценарий "токена нет".


          Если сделана инвалидация старых рефреш токенов то будет такое поведение.

          Ключевое слово "если". Она не обязательна.


          1. Razaz
            06.03.2017 18:20

            Я имею ввиду не хранить перзистентно :) В памяти и при закрытиии приложения удалять.

            Согласен. Но лучше ее по умолчанию включить если есть возможность :)


            1. lair
              06.03.2017 18:23

              Я имею ввиду не хранить перзистентно :) В памяти и при закрытиии приложения удалять.

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


              1. Razaz
                06.03.2017 18:30

                Не спорю, что access_token должен быть короткоживущим. Чем меньше мобилка знает — тем лучше.


  1. Rothmansua
    06.03.2017 18:17

    Refresh Token нужен для того, чтобы можно было задавать разные таймауты для «активной» сессии (когда пользователь непрерывно работает) и для «неактивной» сессии (когда ушел не закрыв браузер).

    Т.е. повышение удобства использования (реже спрашивать пароль) без значительного понижения уровня безопасности.


    1. Razaz
      06.03.2017 18:19

      Если клиент — браузер, то достаточно SSO + access_token. Рефреш тут не нужен. Время основной сессии контролируется на IdP, а сессия клиента короткоживущая и периодически рефрешится у IdP простым редиректом на authorize эндпоинт.


      1. Rothmansua
        06.03.2017 18:25

        Речь идет о такого рода настройке:
        «если пользователь непрерывно работает со страницей, то сессия истекает через 8 часов рабочего дня»
        «если пользователь не делал никаких операций, сессия закрывается через 15 минут ожидания»

        Решается это разным таймаутом валидности для access и refresh token'a.

        «периодически рефрешится у IdP простым редиректом на authorize эндпоинт»
        вот здесь поподробнее пожалуйста, снова пользователю пароль вводить?


        1. lair
          06.03.2017 18:26

          Речь идет о такого рода настройке:
          «если пользователь непрерывно работает со страницей, но сессия истекает через 8 часов рабочего для»
          «если пользователь не делал никаких операций, сессия закрывается через 15 минут ожидания»

          … и как вы это сделаете через refresh + access?


          1. Rothmansua
            06.03.2017 18:32

            1) Access token валиден 15 минут
            2) Через 15 минут клиент получает «отлуп» при попытке выполнить очередную операцию.
            3) Вместо того чтобы снова спрашивать пользователя ввести пароль, клиент отправляет refresh token для аутентификации.
            4) Refresh token валиден 8 часов
            5) Повторять шаги 2-4 8 часов подряд
            6) Через 8 часов попытка аутентификации с помощью refresh токена закончится неудачей и тогда уже можно спрашивать пароль по-новой


            1. lair
              06.03.2017 19:04

              Эмм.


              1. У вас через восемь часов "сессия закрывается" — нужен пароль, а через 15 минут "сессия закрывается" — пароль не нужен. Немножко не одно и то же.
              2. Чем отличается "клиент ничего не делал 15 минут и получил отлуп по истечению access-токена" и "клиент что-то делал 15 минут и получил отлуп по истечению access-токена"?


              1. Rothmansua
                06.03.2017 19:13

                Смысл refresh token'a как раз в том, что с ним для получения access token'a не нужен пароль.
                Если есть валидный refresh token, то пользователь в UI ничего не заметит когда его access token истечет и просто незаметно продолжит работу дальше, и так 8 часов.

                Если бы у пользователя/клиента не было бы валидного refresh token'a, то единственным способом получить/обновить access token для продолжения работы была бы аутентификация «с самого начала», т.е. предъявления своего секрета (пароля). В этом случае, очевидно, пришлось бы вводить пароль каждые 15 минут.

                Если «украли/утек» refresh токен — то конечно беда на следующие 8 часов, лучше его бережно хранить на клиенте. Но зато он хоть путешествует по сети редко (раз в 15 минут, а не при каждом запросе сервера), так что перехватить его труднее (а по SSL и того сложнее).

                https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/


                1. eyeofhell
                  06.03.2017 19:20

                  Хорошая статья, спасибо!


                1. lair
                  06.03.2017 22:34

                  Это все хорошо, но ваше же требование "если пользователь не делал никаких операций, сессия закрывается через 15 минут ожидания" не выполнено:


                  1. с точки зрения пользователя нет разницы, работал он 15 минут, или ничего не делал — все равно UI незаметно воспользуется рефрешем для получения нового токена
                  2. с точки зрения системы тоже нет разницы, работал пользователь 15 минут или ничего не делал — все равно через 15 минут один токен будет заменен на другой.

                  При этом, что характерно, ваша пара требований прекрасно решается одним токеном со следующей комбинацией правил: скользящее время устаревания с окном в 15 минут + абсолютный максимум устаревания в 8 часов. Пользователь не работал 15 минут — все, токен протух, вперед вводить логин-пароль. Пользователь работал каждые десять минут — через восемь часов все равно вводи логин-пароль (но все эти восемь часов будет один и тот же токен, одна и та же отслеживаемая сессия).


                  1. Rothmansua
                    07.03.2017 01:21

                    2. С точки зрения системы, если пользователь закрыл браузер и ушел никто не сможет воспользоваться перехваченным access token'om через 15 минут. Refresh token при этом перехватить сложнее, потому что существенно реже передается по сети.

                    Теперь неужели не ясна разница в уровне безопасности между двумя следующими сценариями:
                    1. Один и тот же access token гуляет по сети при каждом серверном запросе 8 часов (хоть следите вы на сервере за разной длинной активной/неактивной сессии, хоть не следите)
                    2. Access token меняется каждые 15 минут атоматически.

                    По вашему это равнозначно и можно обойтись только access токеном?


                    1. lair
                      07.03.2017 10:12

                      С точки зрения системы, если пользователь закрыл браузер и ушел никто не сможет воспользоваться перехваченным access token'om через 15 минут.

                      Если пользователь не закрывал браузера, все равно никто не сможет воспользоваться перехваченным токеном через 15 минут. Поэтому требование продолжает быть не выполнено.


                      Теперь неужели не ясна разница в уровне безопасности между двумя следующими сценариями:

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


                    1. jetexe
                      07.03.2017 10:13

                      Потому что вы забываете, что ещё есть «сессия», поверх неё защита от CSRF. Куда ещё больше токенов?


                      1. Rothmansua
                        07.03.2017 10:42

                        Почему это я забываю о «сессии»?
                        Вы видимо имеете в виду «HTTP сессию» на Resource Server'e. На мой взгляд, эта штука достаточно ортогональная «OAuth2 сессии», и, к тому же, которую следует избегать, чтобы не слишком усложнять дизайн системы (иначе приходится городить репликацию сессии или sticky session на load balancer'ах).


                      1. Rothmansua
                        07.03.2017 11:04

                        Пользовательскую сессию не обязательно реализовывать в виде HTTP сессии.
                        Если делать сервисы stateless и не хранить ничего пользовательского в памяти, вполне можно обойтись OAuth2 токенами для определения когда сессия, определенная ими, истекла: как только authorization server отказывается аутентифицировать access и/или refresh token переправляем пользователя на Login screen.


                        1. jetexe
                          07.03.2017 11:28

                          Если клиент в браузере, то HTTP сессия у него уже есть. И для получения access можно использовать её. OAuth2 немного не для этого всё же.


        1. Razaz
          06.03.2017 18:26

          1. Максимальное время продления SSO сессии.
          2. Базовое время жизни SSO сессии.
          Sliding expiration называется такая штука.

          Зачем? У юзера есть SSO сессия. Пока она активна IdP не будет предпринимать попыток заново аутентифицировать пользователя.


          1. Rothmansua
            06.03.2017 18:29

            OAuth2 и SSO — две разные и независимые вещи


            1. Razaz
              06.03.2017 18:31

              OAuth2 обычно идет в довесок к OpenID Connect у основных провайдеров. А там и SSO обычно вкручен.


              1. Rothmansua
                06.03.2017 18:34

                «обычно» :)
                Повторюсь, OAuth2/OpenID Connect и SSO (который на чем душа пожелает можно реализовывать, хоть SAML2, хоть JWT) — две совершенно разные вещи


                1. Razaz
                  06.03.2017 18:38

                  JWT не SSO :)

                  Можете заменить SSO на сессию IdP.


                  1. Rothmansua
                    06.03.2017 18:41

                    1) Где я сказал, что JWT это SSO?
                    2) Так я о том же, SSO тут ни при чем, уберите его из своих комментариев и мы с вами быстро согласимся :)


                    1. Razaz
                      06.03.2017 18:45

                      Сойдемся на том, что хватит перзистентной сессии на IdP и не надо заново ничего вводить :)
                      Но SSO уже в каждом нормальном IdP есть ;)


          1. Rothmansua
            06.03.2017 18:30

            Расшифруйте IdP пожалуйста «ID Provider»?


            1. Razaz
              06.03.2017 18:32

              Identity Provider. Может выступать в роли Authorization Server.


              1. Rothmansua
                06.03.2017 18:35

                Тот который следит за временем валидности токенов (сессии) в моем случае.


                1. Razaz
                  06.03.2017 18:39

                  За временем валидности может следить как клиент так и IdP.
                  Клиент может смотреть клэймы если это JWT токен напрмиер.


                  1. Rothmansua
                    06.03.2017 18:44

                    Вы мешаете кучу терминов в одну кучу… Очень долго раскручивать.
                    JWT — это формат носителя SSO token'a
                    К OAuth2 и refresh token'у это [прямого] отношения не имеет.

                    Повторяю еще раз, мое утверждение валидно для OAuth2.
                    Я не утверждал и не собираюсь доказывать, что оно валидно для SSO.


                    1. Razaz
                      06.03.2017 18:48
                      -1

                      JWT это формат передачи Assertions. Для SSO он не сдался. Информацию о сессии IdP может просто хранить в куке.
                      Начинает иметь, когда access_token представлен в виде JWT и содержит exp клэйм.

                      Думаю надо просто взять чуток шире и добавить OIDC ;)


      1. Rothmansua
        06.03.2017 18:28

        И SSO тут вообще ни при чем.


        1. Razaz
          06.03.2017 18:32

          Описанная вами проблема решается не через refresh_token.


          1. Rothmansua
            06.03.2017 18:38

            Refresh Token — часть OAuth2 протокола.
            Моя проблема не имеет ничего общего с SSO.
            В ней речь идет об аутентификации в пределах одной системы, а не Single Sign On в несколько разных независимых.


            1. Razaz
              06.03.2017 18:40

              И основной кейс — это получение доступа к ресурсу, когда нет пользовательской сессии.


              1. Rothmansua
                06.03.2017 18:46

                Я скорее не соглашусь.
                Но смотря как вы определяете пользовательскую сессию.
                Если пользовательськая сессия — это время валидности access token'а, то да.
                Иначе — нет.


                1. Razaz
                  06.03.2017 18:51

                  access_token — выдается обычно для обращения к ресурсу. То есть вам делегируется временный доступ куда-то. Использовать его для своей сессии некорректно. Как раз тут надо включать еще OIDC.


                  1. Rothmansua
                    06.03.2017 19:01

                    Извините, но это (e.g. «использовать его для своей сессии») все больше напоминает какую-то демагогию.
                    Предлагаю разобраться самостоятельно в спокойной обстановке.


                    1. Razaz
                      06.03.2017 19:18

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


                      1. Razaz
                        06.03.2017 19:35

                        Туплю. В OAuth2 expires приезжает в респонзе. Но сути это не меняет :)


  1. Veha
    06.03.2017 19:31

    Разница между access_token и refresh_token:

    — access_token не нужно хранить в базе данных, с помощью JWT можно хранить данные в токене — например userId, таким образом при каждом запросе к серверу мы избавляемся от лишнего запроса к базе данных так как ID юзера можно получить из токена

    — refresh_token хеш который хранится в базе данных.

    1. Сервер получает логин/пароль — создает access_token и refresh_token, сохраняет в базу refresh_token, отдает токены на клиент

    2. Клиент использует access_token для доступа к данным пока приложение не будет закрыто, сохраняет refresh_token в хранилище

    3. Если получает от сервера сообщение что срок access_token истек — отправляет запрос на авторизацию с refresh_token, если refresh_token не истек и совпадает с тем что в базе — сервер создает access_token и refresh_token, обновляет в базе refresh_token и отдает на клиент. Если refresh_token не валиден просит ввести логин/пароль

    4. При инициализации приложения если есть refresh_token — делает тоже что и в 3


    1. OlegMax
      06.03.2017 19:58
      -1

      Наконец-то первый правильный ответ. Да, два токена сделаны для удобства сервера.
      «Implementations MUST support the revocation of refresh tokens and SHOULD support the revocation of access tokens». Это позволяет пользоваться access token'ом без обращения к базе.
      The access tokens may be self-contained so that a resource server needs no further interaction with an authorization server issuing these tokens to perform an authorization decision of the client requesting access to a protected resource.


      1. Rothmansua
        06.03.2017 20:46

        Тут какое-то явное противоречие.
        С одной стороны мы поддерживаем revocation access token'a, а с другой не обращаемся к authorization server'у и БД чтобы проверить, а не «отозван» ли наш токен :-/


        1. OlegMax
          06.03.2017 21:45

          Именно, что мы можем не поддерживать revocation access token'a. SHOULD имеет значение «хорошо бы, но если нет, то и так сойдет».


          1. Rothmansua
            06.03.2017 22:06

            Ну да, на логаут тоже можно забить. :) (что такое revocation по-вашему?)
            И вообще, зачем с этой аутентификацией возиться ;)


    1. Rothmansua
      06.03.2017 20:48

      «access_token не нужно хранить в базе данных»
      в базе данных чего?


    1. lair
      06.03.2017 22:36
      +1

      access_token не нужно хранить в базе данных,

      Только в том случае, если он self-contained. Иногда — в том числе и по соображениям безопасности — access-токен все равно используется ссылочный. Это не противоречит никакому стандарту.


      с помощью JWT можно хранить данные в токене — например userId, таким образом при каждом запросе к серверу мы избавляемся от лишнего запроса к базе данных так как ID юзера можно получить из токена

      Не надо так делать. Используйте OIDC и identity token.


  1. Rothmansua
    06.03.2017 20:42

    А как проверять правильный ли переданный access token, если не хранить его в БД Authorization Server'a?
    Можно конечно держать их в памяти и сравнивать, но тогда ваш Authorization Server перестает быть stateless, скалируется плохо, начинает требовать репликации данных в памяти между инстансами, возможно sticky-sessions на load balancer'e и все это только потому что мы решили не хранить его в БД.


    1. Razaz
      06.03.2017 20:53

      1. Rothmansua
        06.03.2017 20:56

        И?
        Я это и имею в виду.


        1. Razaz
          06.03.2017 21:08

          Хранение токенов в принципе обосновано. Мы(IdP) храним все выданные токены до момента их протухания/использования. Тут зависит от настроек клиента — хочет он получать полный токен или сслыку, будет он использовать интроспекцию или нет, будет он пробрасывать токен дальше и тд.

          Масштабируется вполне прилично — пара инстансов на 200к+ юзеров.


          1. Razaz
            06.03.2017 21:23

            Edit: хранится все в БД :) Уже туплю под вечер. Извиняюсь.


          1. Rothmansua
            06.03.2017 21:24

            И что, все токены в памяти «вашего IdP» (а не БД) всегда хранятся?
            Собственно это даже круто (быстрее ответ), только решение существенно сложнее (вы же не сами «ваш IdP» писали?)


            1. Razaz
              06.03.2017 21:26

              Следующий коммент. Все токены в БД. Никакого состояния в памяти.


              1. Rothmansua
                06.03.2017 21:28

                Ок, так годится :)


    1. Veha
      06.03.2017 21:02

      А как проверять правильный ли переданный access token

      Вы читали как работает JWT? access token не нужно держать на стороне сервера. Пример использования на сервере:

      — создаем токен:
      var token = jwt.sign({
      userID: '123'
      }, 'secret', { expiresIn: '1h' });

      — проверяем:
      var decoded = jwt.verify(token, 'secret');
      // decoded.userID = 123


      1. Razaz
        06.03.2017 21:05

        Забыли самую малость: issuer, audience, scope.


        1. lair
          06.03.2017 22:37

          Мелочи какие.


      1. Rothmansua
        06.03.2017 21:07

        Как я уже писал выше, JWT — это формат. К OAuth2 протоколу прямого отношения не имеет. Используется чаще для установления SSO между независимыми системами (из-за своей способности нести в себе дополнительную информацию).

        Теперь возвращаемся «к нашим баранам».
        Итак, как верификация в вашем примере (того что JWT токен не поддельный) поможет без обращения к системе, которая его выдала, проверить отозван токен или нет?


      1. lair
        06.03.2017 22:37

        А jwt.verify святым духом работает? Валидность подписи вы как проверяете?


        1. Rothmansua
          07.03.2017 00:53

          При чем здесь это?!
          jwt.verify проверяет только что
          1) токен подписан и выдан исходным authorization server
          2) (опционально) он зашифрован с использованием секрета, известного вам
          Если вы потеряли access токен, люой может его использовать для OAuth2 аутентифкации пока он валиден.

          Теперь при взломе или досрочном прекращении работы (logout) вы должны ивалидировать access token (чтобы кто-нибудь не смог его использовать после конца вашей работы и до конца срока его действия.

          При чем здесь jwt.verify() ?!


          1. Rothmansua
            07.03.2017 01:26

            А и да, jwt.verify() не подразуменвает раскодирования, так что пункт 2) зачеркните там (шифрование SSO token'ов практикуется в определенных схемах SSO).
            Перехваченный валидный (до тех пор пока не было логаута) OAuth2 access token может использовать любой злоумышленник для аутентификации.

            Нет логаута — все ваши JWT access token'ы (что вы прицепились к этому JWT, очень много систем используют простой GUID в качестве access token'a) остаются валидными в руках злоумышленника до истечения срока их годности.


          1. lair
            07.03.2017 10:03

            Ровно при том, что даже если мы отказались от token revocation, использование JWT все равно не позволит обойтись без обращений к серверу при проверке токена — потому что нельзя "просто взять и проверить", что токен выдан именно тем, кем утверждается, что он выдан.


            (мне кажется, вы перепутали, кому я отвечал)


            1. Rothmansua
              07.03.2017 10:49

              Сорри, если я что перепутал.
              Но вынужден снова не согласиться. Смысл подписи и jwt.verify() именно в том, чтобы удостовериться в том, кто выдал токен, без обращения к нему.
              Это свойство часто используется в схемах SSO систем без центрального/общего identy provider.
              Т.е., грубо говоря, если мы отказываемся от revocation и логаута, то, похоже, можно обойтись без обращения с authorization server'у.


              1. lair
                07.03.2017 10:50

                Смысл подписи и jwt.verify() именно в том, чтобы удостовериться в том, кто выдал токен, без обращения к нему.

                Угу, и как же именно?


                1. Rothmansua
                  07.03.2017 10:57

                  Ну как, святым духом вестимо :)
                  https://jwt.io/introduction/

                  Вкратце, с помощью ассиметричных алгоритмов шифрования.


                  1. lair
                    07.03.2017 11:10

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


                    1. GreenStore
                      07.03.2017 11:18

                      Токены же проверяются на сервере, поэтому организация PKI или дистрибуция Shared Key не является проблемой. Или я не понял суть вопроса?


                      1. Rothmansua
                        07.03.2017 11:22

                        Ну обычно да.
                        Resource Server обращается при каждом клиентском запросе к authorization server, чтобы убедиться, что все ок.
                        Но
                        Если вдруг authorization server недоступен (как например в сценарии SSO между двумя независимыми системами), то обращаться за проверкой некуда (например нету сетевой связи с AS, выдавшим токен).
                        Тут его подлинность можно проверить только с помощью криптографической подписи токена.


                        1. Razaz
                          07.03.2017 11:26

                          С чего вы взяли? Онлайн проверка используется для референс токенов. Для self-contained в большинстве ситуаций смысла нет. Ну если вы не ставите лайфтайм неадекватно большим.


                          1. Rothmansua
                            07.03.2017 11:29

                            Ну да, все верно (и противоречий с моим предыдущим мессаджем нет).


                        1. GreenStore
                          07.03.2017 11:32

                          > Resource Server обращается при каждом клиентском запросе к authorization server, чтобы убедиться, что все ок.

                          А зачем ему это делать каждый раз, если он кэширует актуальный сертификат CA или имеет сконфигурированный/периодически обновляемый Shared Key?

                          И если это необходимо, может периодически обновлять список отозванных токенов.

                          Кроме этого, необходимые данные authorization server легко реплицируются, т.е. система без труда горизонтально масштабируется.


                          1. Razaz
                            07.03.2017 11:38

                            Токены могут быть self-contained(содержать все в себе) или reference(ссылка на токен, который хранится на AS).


                            1. GreenStore
                              07.03.2017 11:42

                              Если речь о JWT, то видно, что они в исходном варианте self-contained.


                      1. lair
                        07.03.2017 11:29

                        Это если у вас AS и RS находятся под общим контролем.


                    1. Rothmansua
                      07.03.2017 11:20

                      Почему это сертификат?
                      Ассиметричное шифрование предполагает использование пары public и private ключей.
                      Подпиши токен private ключем и раздай private key всем пользователям, кто об этом попросит, свой public key и вот они уже могут счастливо проверять подлинность JWT токенов без лишних раундтрипов к тебе же.
                      (я такое делал, и именно с JWT токенами, все работает)


                      1. Razaz
                        07.03.2017 11:23

                        CRL, Chain trust, Expiry. Раздают публичную часть.


                        1. Rothmansua
                          07.03.2017 11:26

                          Да можно просто в Яваскрипте public key хардкодить.
                          Если доверяешь домену, с которого скачиваешь его, то этого должно быть досточно.


                          1. Razaz
                            07.03.2017 11:29

                            Какой паблик кей в JS??? Не достаточно. Сертификат для подписи не должен совпадать с сертификатом для TLS


                            1. Rothmansua
                              07.03.2017 11:32

                              Да ну глупости это :)
                              Зайдите на jwt.io и проверяйте токены, используя любые пары ключей.


                              1. Razaz
                                07.03.2017 11:42

                                Ох. Ну начнем.
                                Факт валидности подписи не означает что вы можете доверять этому токену.
                                Вы обязаны проверить что ваш audience совпадает с audience токена. Тоесть если токен выдан на https://domain1, а вы на https://domain2 — его использовать нельзя.
                                Так же надо проверять валидность сертификата, что бы злоумышленник не мог подписать токен отозванным сертификатом при его компрометации.
                                Еще Issuer забыл. Его то же.


                                1. Rothmansua
                                  07.03.2017 11:46

                                  Да да? :)
                                  А если я сам пишу AS, RS и клиент (вы же мне этого не запретите?)
                                  И плевать хотел на все issuer и audience (допустим у меня их «по одному»).
                                  Что выходит мне JWT запрещено использовать? :)


                                  1. Razaz
                                    07.03.2017 11:51

                                    Тогда у вас не OAuth2 а свой велосипедик, похожий на него)).

                                    Могу только посоветовать не писать AS ;)

                                    С таким подходом возможен как минимум misuse токенов в других приложениях, которые доверяют вашему AS.

                                    Зачем изобретать велосипед, когда все это есть в стандартных либах? :)


                                    1. Rothmansua
                                      07.03.2017 11:52

                                      OAuth2 — это протокол, а не AS или как вы выражаетесь «IdP»
                                      Хочу — реализую, хочу — беру готовое решение.


                                      1. Razaz
                                        07.03.2017 11:54

                                        AS — участник, описанный в спецификации протокола.
                                        IdP может выполнять роль AS или STS. Тут уже и другие протоколы есть.

                                        Есть спека, есть implementors guide, есть threat model. Ну не хотите — флаг в руки как говорится.


                                        1. Rothmansua
                                          07.03.2017 11:57

                                          Да при чем тут implementor's guide?
                                          Issuer, Audience и подобные поля имеют смысл, если в них есть что писать :)
                                          Если у вас только 1 issuer и все токены только для одного Audience («все пользователи»), что толку заполнять эти поля?
                                          Чем они повысят безопасность?


                                          1. Razaz
                                            07.03.2017 12:54

                                            Вы обязаны в них записать эти параметры.

                                            Audience — uri приложения. Вы наверное со скоупом попутали.

                                            Зачем вам тогда OAuth2? :)


                                            1. Rothmansua
                                              07.03.2017 13:18

                                              Я вам все-таки настойчиво рекомендую разобраться в чем разница между JWT и OAuth2.


                            1. Rothmansua
                              07.03.2017 11:35

                              А, или я вообще не понял смысл комментария. Я что писал, что «Сертификат для подписи должен совпадать с сертификатом для TLS»?


                              1. Razaz
                                07.03.2017 11:39

                                «Если доверяешь домену»?


                                1. Rothmansua
                                  07.03.2017 11:44

                                  Ну да, тому откуда приложение в браузер загружается.
                                  Если все части распределенной OAuth2 системы скомпрометированы (включая AS и клиент), то тут уже «поздно пить Боржоми».
                                  Но TLS сертификат к механизму подписи JWT отношения не имеет.


                      1. Rothmansua
                        07.03.2017 11:27

                        читать " и раздай public key всем пользователям, кто об этом попросит"


                      1. lair
                        07.03.2017 11:30

                        Почему это сертификат?

                        Да можно и public key с тем же успехом написать, суть не изменится.


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

                        … потом заложи в эту схему угрозу "я продолбал private key", и все станет понятно.


                        1. Rothmansua
                          07.03.2017 11:34

                          Не, ну да.
                          Но кстати если «хардкодить public key в яваскрипт», то при потере и последующей смене private key клиенты мало что заметят, потому что будут скачивать и использовать новый/обновленный public key.


                          1. lair
                            07.03.2017 11:35

                            "Скачивать". То есть обращение к серверу. О чем и была речь с самого начала.


                            1. Rothmansua
                              07.03.2017 11:37

                              Ну… не совсем.
                              Яваскрипт-то не обязательно идет с AS, он идет с Resource Server'a.
                              В случае смены private/public key пары да, придется две стороны обновлять.
                              Но клиент все равно не затронут :)

                              Хотя, возможно, подробности зависят от конкретного OAuth2 flow.


                              1. lair
                                07.03.2017 11:38

                                Ну так речь не о том, затронут ли клиент, речь о том, сколько обращений нужно сделать, чтобы быть уверенным в валидности access token.


                                1. Rothmansua
                                  07.03.2017 11:40

                                  Первая загрузка клиентского приложения (например SPA в браузер) все же не в счет. Этого обращения к серверу, как и первого логина с паролем, не избежать.


                                  1. lair
                                    07.03.2017 11:56

                                    … и закэшировать на тот месяц, пока пользователь браузер на закрывает? А если за это время надо инвалидировать ключи?


                                    1. Rothmansua
                                      07.03.2017 11:59

                                      Ну да, это слабое место, согласен.


                                      1. lair
                                        07.03.2017 12:01
                                        +2

                                        Это как раз типичный компромис между "временем жизни" (чего угодно, что токена, что ключа, что сертификата) и безопасностью. Больше время жизни — меньше нагрузка, меньше безопасность, и наоборот.


                1. Veha
                  07.03.2017 11:12

                  1. Rothmansua
                    07.03.2017 11:25

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


                    1. Razaz
                      07.03.2017 11:27

                      Зато есть описание того, как их использовать с OAuth2/OpenID Connect. И там много всего интересного. Настоятельно рекомендую ознакомиться :)


                  1. lair
                    07.03.2017 11:28

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


                    1. Veha
                      07.03.2017 11:35

                      issuer

                      The iss (issuer) claim identifies the principal that issued the JWT. The processing of this claim is generally application specific. The iss value is a case-sensitive string containing a StringOrURI value. Use of this claim is OPTIONAL.


                      1. lair
                        07.03.2017 11:36
                        +1

                        И что мне мешает написать туда что угодно?


                        1. Veha
                          07.03.2017 11:56
                          -1

                          Ну пиши что угодно в чем проблема?


                          1. lair
                            07.03.2017 11:57

                            В том, что если кто угодно может написать что угодно, то провалидировать это невозможно.


                        1. Razaz
                          07.03.2017 12:00

                          В валидации как минимум присутствуют:
                          1. Проверка самой подписи.
                          2. Проверка доверя к издателю — Issuer + Key пара. По факту траст.
                          3. Проверка времени жизни токена.
                          4. Проверка Proof of posession токена.

                          Ну это я так, понудеть :D


                          1. lair
                            07.03.2017 12:02

                            Да знаю я, знаю.


                            Проверка доверя к издателю — Issuer + Key пара. По факту траст.

                            Вот это место (выше уже обсудили до дыр) требует либо инфраструктуры, либо обращения к серверу, и требует выбора решения на пространстве "время кэширования — актуальность данных".


                            1. Razaz
                              07.03.2017 12:08

                              Конечно.
                              Есть вариант гонять тело публичного ключа в x5c, но это будет ооочень жирный токен)).
                              Есть еще OIDC Discovery + WebFinger:
                              Я просто затягиваю дискавери документ и кеширую на небольшой срок(минут 10 например) и проблема решена. Там же и ключики приезжают.


                              1. lair
                                07.03.2017 12:12

                                … обращение к серверу раз в десять минут, как вы могли.


                                1. Razaz
                                  07.03.2017 12:49

                                  Я могу))
                                  Можно и без этого обойтись, если требования не сильно высоки. Можно на время жизни приложения закешировать.

                                  На самом деле трешачина начинается тут https://tools.ietf.org/html/draft-ietf-oauth-token-exchange-07 ;)


                                  1. lair
                                    07.03.2017 12:50

                                    ОМГ.


    1. Veha
      06.03.2017 21:07

      В БД храним только refresh token, но обращаемся к БД только когда access token истек


      1. Rothmansua
        06.03.2017 21:13

        Какую БД вы имеете в виду?

        • Client Application
        • Resource Server
        • Authorization Server

        http://tutorials.jenkov.com/oauth2/roles.html


      1. Rothmansua
        06.03.2017 21:18

        Все что хранится не в БД, а в памяти сервера делает его stateful и умешает его scalability.
        Если Client Application это SPA или мобильное приложение (один одновременный пользователь/сессия), то там нет смысла что-либо хранить «в БД» (там и БД-то обычно недоступно).


    1. GhostArt
      07.03.2017 09:19

      Как минимум можете проверить его подпись =)


  1. Rothmansua
    06.03.2017 21:06


    1. Veha
      06.03.2017 21:13

      Поставить срок жизни access token маленький например 30 мин, можно удалить в БД refresh token юзера, тогда нельзя будет получить новый access token и надо будет вводить логин пароль. Если токен не был украден то пользователь не заметит как приложение обновит access token через 30 мин, а если ктото другой использовал refresh token то они не совпадут и пользователю надо будет ввести логин пароль, в итоге украденый refresh token будет не валиден


      1. Rothmansua
        06.03.2017 21:14

        В БД чего?


        1. Veha
          06.03.2017 21:23

          в БД Authorization Server


          1. Rothmansua
            06.03.2017 21:27

            Ясно.
            Итак, вы предлагаете, не хранить access token'ы в БД Authorization Server'а, только refresh токены.
            Значит придется их хранить в памяти — хозяин барин.


            1. Veha
              06.03.2017 21:28

              Заем access token хранить в памяти Authorization Server'а? для каких целей?


              1. Rothmansua
                06.03.2017 21:29

                Например чтобы поддерживать их «отзыв» (revocation)


              1. Rothmansua
                06.03.2017 21:30

                Например чтобы поддерживать Single Logout


                1. Razaz
                  06.03.2017 21:35

                  https://openid.net/specs/openid-connect-session-1_0.html
                  http://openid.net/specs/openid-connect-backchannel-1_0.html
                  http://openid.net/specs/openid-connect-frontchannel-1_0.html

                  Все до безобразия просто :) Не надо токены держать для этого.

                  IdP/AS просто хранит в сессии список клиентов, которые в рамках нее обращались например.


            1. Razaz
              06.03.2017 21:29

              В определенных случаях(нет интроспекции и все клэймы в токене и клиент поддерживает backchannel логаут) может иметь право на жизнь.


              1. Rothmansua
                06.03.2017 21:31

                Не только «иметь право на жизнь». Без этого вооще не понятно как logout реализовывать.


                1. Veha
                  06.03.2017 21:35

                  Удалить из БД refresh token и юзер не сможет получить новый access token, надо будет вводить логин/пароль


                  1. Razaz
                    06.03.2017 21:38

                    При условии, что вы не выставляете access_token на два дня как автор поста ;D


                  1. Rothmansua
                    07.03.2017 01:34

                    Ну так это не логаут, а просто expiration of refresh token.
                    Этот интервал обычно гораздно дольше чем expiration of access token.
                    Безопасность тут будет хромать на обе ноги.


                    1. Rothmansua
                      07.03.2017 10:52

                      Вернее expiration of access token.
                      Это и происходит при логауте, только оба токена удаляются/деактивируются из БД authorization server'a.


                1. Razaz
                  06.03.2017 21:37

                  Выше привел ссылки на спеки. Можно и к чистому OAuth2 AS накрутить. Вариантов достаточно.


            1. GreenStore
              06.03.2017 22:03

              > Итак, вы предлагаете, не хранить access token'ы в БД Authorization Server'а,
              > только refresh токены. Значит придется их хранить в памяти — хозяин барин.


              Как я понимаю, если токен — не случайное число, а криптографический токен, то для проверки его подлинности не нужно использовать БД и где либо вообще его хранить.

              Если требуется реализовать механизм отзыва, то храниться/распространяться должны токены из черного списка, а не рабочие токены. Отзыв токена — гораздо более редкая ситуация (плохих токенов много меньше, чем рабочих), плюс к этому, срок жизни access-токена сильно ограничен.


              1. Rothmansua
                06.03.2017 22:09

                Логаут — это и есть «механизм отзыва».
                Вы предлагаете отказаться от logout'a не понятно для чего (для оригинальности?).

                «срок жизни access-токена сильно ограничен.»
                15 минут — это по-вашему «сильно ограничен»?

                Да зачем вообще с этой аутентификацией возиться, без нее вообще ничего нигде хранить не надо.


                1. GreenStore
                  06.03.2017 22:13

                  > Логаут — это и есть «механизм отзыва»

                  По-моему, логаут — это стирание данных о токенах на клиенте. Т.е. это операция со стороны клиента, а не сервера.


                  1. Razaz
                    06.03.2017 22:14

                    Не совсем. Есть логаут инициированный клиентом, а есть — сервером :D


                  1. Rothmansua
                    06.03.2017 22:17

                    Ну это не верно.


                    1. GreenStore
                      06.03.2017 22:22

                      Что бы не было бессмысленных споров, приведите ваше определение термину «логаут».


                      1. Rothmansua
                        06.03.2017 23:58

                        Да любое определение лучше.
                        Не с азов же тут обучение начинать.

                        Ваше определение — как страусу спрятать голову в песок: «если я тебя не вижу, значит и ты меня не видишь». Это называется "security theater":

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


                        1. GreenStore
                          07.03.2017 05:37

                          Я попросил вас привести определение без всякой задней мысли.

                          Пожалуйста, приведите ваше определение для «логаут». Иначе о чем может быть спор, ведь мы с вами должны оперировать одинаковыми терминами и понятиями.


                          1. Rothmansua
                            07.03.2017 10:54

                            Попробую определить логаут так:

                            «Прекращение доверия пользовательской сессии в лице access и refresh token'а на стороне authorization server».


              1. Razaz
                06.03.2017 22:14

                Думаю все просто надо собраться и почитать спеки:) Все уже придумано :D

                Если токен в виде JWT, то хранить или нет просто вопрос бизнес требований — в UI показать какие-нибудь метаданные админу, статистику пособирать и тд.

                Второй тип токена — это референс токен. Это по сути сгенерированный крипторандомом хэндл + обычный токен. Но токен хранится в бд, а не катается на клиента. И тут до кучи еще и интроспекция не помешает.

                Отзыв access_token с IdP/AS вообще странный кейс. Проще разлогинить юзера удаленно.


  1. rqnix
    07.03.2017 09:19

    А как мы можем обновлять, рефреш токен без логина/пароля ?


    1. lair
      07.03.2017 10:14

      Строго по стандарту: при выдаче нового аксесс-токена по рефреш-токену авторизационный сервер может выдать и новый рефреш-токен.


  1. tsabir
    07.03.2017 10:16

    Мои пять копеек в кучу теорий :) Поправьте меня, если несу чушь.


    Пара RefreshToken + AccessToken нужна потому что:


    1. RefreshToken можно использовать для получения следующего Access/Refresh Token, а AccessToken нельзя. Т.е. передавая AccessToken третьим лицам (ServiceProvider) или через каналы к которым у меня нет доверия, я избегаю утечки доступа к моей сессии (IdentityProvider).


    2. В случаях, когда AccessToken относительно быстро истекает, чтобы получить доступ к сервису, клиенту нужно будет обязательно сходить за ним к IdentityProvider, используя RefreshToken. Получается, что если отозвать RefreshToken, то это автоматом закроет возможность получения доступа к сервису при компрометации RefreshToken. Понятно, что ServiceProvider принципиально должен принимать только AccessToken, чтобы это работало.


    1. lair
      07.03.2017 10:30
      +1

      Да нет никакого "нужна": есть больше одного сценария, когда без рефреша прекрасно обходятся. Однако если нам надо обеспечить работу в отсутствие пользователя (всякие сервисы) и при этом мы не хотим долгоживущий access token, потому что мы параноики — тогда пара refresh-access будет единственным решением.


      1. tsabir
        07.03.2017 10:34

        есть больше одного сценария, когда без рефреша прекрасно обходятся

        не совсем понял, без рефреша как-то продлевают сессию?


        1. lair
          07.03.2017 10:36

          Отправляют пользователя обратно на AS. Если у него там открытая сессия (или авторизация, не требующая ввода данных) и нет необходимости подтверждать разрешение приложения, то обратно прилетает токен, пользователь может даже ничего и не заметить.


          1. tsabir
            07.03.2017 10:45

            Ну я бы не сказал, что этот способ прямо разительно отличается. Технически, вы просто заменили RefreshToken на Session cookie. Ну и, далеко не все клиенты в браузере.


            1. lair
              07.03.2017 10:47

              Технически, вы просто заменили RefreshToken на Session cookie

              Нет, технически я переложил ответственность за долгосрочную сессию с клиента на AS. Сам клиент в этом случае становится существенно проще.


              (а еще бывают ситуации, когда долгосрочная сессия вообще не нужна)


              Ну и, далеко не все клиенты в браузере.

              Я и не говорил, что этот подход всегда работает.


              1. tsabir
                07.03.2017 10:52
                -1

                Нет, технически я переложил ответственность за долгосрочную сессию с клиента на AS. Сам клиент в этом случае становится существенно проще.

                Позвольте не согласиться, вы переложили ответственность на веб-браузер.


                Ну и, далеко не все клиенты в браузере.
                Я и не говорил, что этот подход всегда работает.

                Думаю, в случае с RefreshToken предлагается более универсальный подход, нежели всякие Session Cookie и редиректы в браузерах


                1. lair
                  07.03.2017 10:55

                  Позвольте не согласиться, вы переложили ответственность на веб-браузер.

                  Не позволю. Аутентификацию производит именно AS, каким способом он это делает — его дело. Браузер выступает только агентом, осуществляющим перевод пользователя по шагам.


                  Думаю, в случае с RefreshToken предлагается более универсальный подход

                  … имеющий свои ограничения. Как следствие, каждый может сам оценить свои требования и понять, какой сценарий ему более выгоден.


                  редиректы в браузерах

                  Нельзя сделать OAuth без "редиректов в браузерах" (либо вам придется заставлять пользователя вводить информацию руками).


                  1. tsabir
                    07.03.2017 11:07

                    Нельзя сделать OAuth без "редиректов в браузерах" (либо вам придется заставлять пользователя вводить информацию руками).

                    Странный аргумент. Хотите сказать, что Resource Owner Password Credentials Grant уже не является частью OAuth?


                    1. lair
                      07.03.2017 11:11
                      +1

                      Нет, я хочу сказать, что resource owner налагает совсем другие требования по безопасности.


                      Хотя, конечно, здесь вы правы, я и сам про него вспомнил уже.


                    1. Razaz
                      07.03.2017 11:45

                      А за использование этого гранта можно и огрести. Он предполагается для совместимости с легаси приложениями.


                      1. tsabir
                        07.03.2017 12:07

                        А где в OAuth 2.0 написано про легаси?


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


                        Также некоторые AS позволяют без браузера при этом защищают через MFA например ну или через всякие Apple Social.


                        Я понимаю, что вы имеете ввиду всякие Фэйсбуки и Твиттеры и их политики, но OAuth не только под них же писан в конце-концов.


                        1. Razaz
                          07.03.2017 12:14
                          +1

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

                          Есть implicit flow — через него и решать.


                          1. tsabir
                            07.03.2017 12:30

                            А если нет возможности implicit flow? Почему вы прицепились к браузеру?


                            1. Razaz
                              07.03.2017 12:32

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


                  1. tsabir
                    07.03.2017 12:09

                    имеющий свои ограничения

                    Имеете ввиду какие-то технические ограничения? Можно примеры?


                    1. lair
                      07.03.2017 12:17

                      Имеете ввиду какие-то технические ограничения? Можно примеры?

                      Например, клиенту теперь надо реализовывать не один маршрут(нет access-токена/протух access-токен — пошли за токеном), а два с половиной: получение токенов и рефреш (включая замену рефреша, если он на рефреше обновился, простите за формулировку). Плюс рефреш надо хранить как confidential. Плюс нужна идентификация (и очень желательна аутентификация) клиента (и начинается цирк со всеми публичными клиентами).


                      1. tsabir
                        07.03.2017 13:02

                        Я если честно, вижу только 1 неприятный момент — это утечка Рефреш-токена. Остальное абстрагируется же библиотекой?


                        1. lair
                          07.03.2017 13:05

                          Аутентификация клиента библиотекой не абстрагируется, потому что это дополнительные действия администратора.


                          1. tsabir
                            07.03.2017 13:08

                            Тут я согласен, редирект однозначно это решает.


                            1. lair
                              07.03.2017 13:09

                              Эээ, я что-то не понимаю, какой редирект это решает и как.


                              1. tsabir
                                07.03.2017 13:19

                                Ну я в какой-то момент так предположил, но теперь что-то засомневался.


                                в случае без рефреш-токена, как этот вопрос решается?


                                1. lair
                                  07.03.2017 13:21

                                  В случае без рефреш-токена аутентификация клиента менее важна (проще говоря, иногда можно для публичных AS вообще не делать аутентификацию). Поэтому не "вопрос как-то решается", а "вопрос просто не встает".


                                  1. tsabir
                                    07.03.2017 13:29

                                    Ну вот, я это и имел ввиду. Если Аксесс-токен обновлять строго через редирект, то на клиенте не будет храниться ничего, поэтому нет смысла его защищать


                1. Razaz
                  07.03.2017 12:15

                  Не универсальный. Refresh это не сессия. Access это не сессия. Выше уже обсудили несколько раз.


                  1. tsabir
                    07.03.2017 12:28

                    Вот именно, что универсальный, так как не привязан вообще к сессиям и браузерам


                    1. Razaz
                      07.03.2017 12:35

                      Откуда вы это напридумывали?
                      Весь смысл включения рефреш токена — доступ к ресурсу. когда пользователь не имеет активной сессии.

                      Проимер: У вас есть клиент, который в фоне опрашивает ресурссный сервер. Когда пользователь залогинен — вы обновляете access_token редиректом на AS. Когда пользователя нет — запросом рефреш токена. Если вы сделаете рефреш токен сессией — вы делите на 0.


                      1. tsabir
                        07.03.2017 12:38

                        Я не могу никак понять, зачем вы все приплетаете сессии и браузеры? OAuth и OpenID Connect не ограничен ими.


                        1. Razaz
                          07.03.2017 12:44

                          А http клиенты с куками не умеют работать?

                          Раз уж вспомнили OIDC То рекомендую ознакомиться с полным набором спек:
                          http://openid.net/connect/

                          И в частности — http://openid.net/specs/openid-connect-session-1_0.html.

                          И никогда не завязываться на refresh as session. Для этого там есть id_token.


                          1. tsabir
                            07.03.2017 12:54

                            Если честно, я зашел в тупик.


                            я пишу


                            Думаю, в случае с RefreshToken предлагается более универсальный подход, нежели всякие Session Cookie и редиректы в браузерах

                            вы отвечаете:


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

                            То есть, если есть сессия, то пользоваться Рефреш-токеном для получения Аксес-токена — моветон? Зачем мне реализовывать в клиенте 2 способа, когда у меня есть Рефреш?


                            1. Razaz
                              07.03.2017 13:00
                              +1

                              Если у вас клиенту не нужен оффлайн доступ — вам не нужен рефреш. Вообще.
                              Просто редирект и все.
                              1. Сразу пропадает необходимость безопасного хранения токенов. Вы же не в открытом виде их храните так? Наверное для каждой сессии хотя бы генерите уникальный ключик и шифруете им?
                              2. Нет необходимости поддержки безопасного бэкченел соединения с AS.


                              1. tsabir
                                07.03.2017 14:23

                                В целом я согласен, что через редирект безопаснее, но все же:


                                1. есть Apple Keychain и его аналоги на других платформах
                                2. не понял. HTTPS же обычно есть


                                1. Razaz
                                  07.03.2017 14:34

                                  1. Эппл — частный случай. Есть еще Android :)
                                  2. У вас есть так называемый front channel и back channel. Запросы юзеров и клиентов приходят через front channel, а рефреш вы просите через back channel. Тут встает вопрос к инфраструктуре, её аудиту, 2-leg ssl. Если залогируют access-token — неприятно, но не смертельно. Если будут логировать refresh — полный треш. Это на вскидку :)

                                  В том же OIDC при использовании гибридного флоу и получении id_token + access_token через from_post — вам вообще не надо ходить на IdP в простейшем случае.

                                  Тут нет универсальных решений. Для каждого случая надо подбирать свое решение из доступных вариантов, инфраструктуры, требований.


  1. prijutme4ty
    07.03.2017 11:18

    Да ну, ничего не объяснили.
    Боб не дурак использовать refresh token и будет ждать, пока access token протухнет. Access токен в состоянии сам следить за временем своей жизни, refresh ему для этого не нужен.
    И где профит?


  1. olegi
    07.03.2017 11:36

    Применительно к мобильным приложениям(МП) или SDK, Oauth и токены как-то криво вписываются.
    Задача: юзер вводит логин и пароль, работает в МП. Через месяц юзер снова запускает МП, и продолжает работать.

    В связи с тем, что Authz(Authn)(Identity)Server поддерживают только OAuth и OpenID, эту задачу я решил так: access_token-у установил время жизни день, а refresh_token-у 3 года.

    1 день жизни access_token снижает нагрузку на сервер, если выписывать его каждые 30 минут.
    а имея рефреш токен позволяет отозвать сессию если юзер скомпрометирован.


    1. lair
      07.03.2017 11:38

      Применительно к мобильным приложениям(МП) или SDK, Oauth и токены как-то криво вписываются.

      Смотря для чего вы их используете. Почему криво-то?


      Задача: юзер вводит логин и пароль, работает в МП. Через месяц юзер снова запускает МП, и продолжает работать.

      Ну вот сразу: куда вводит логин/пароль? В мобильное приложение? Или в браузер? Что должно произойти, если за этот месяц юзер поменял пароль?


      1. olegi
        07.03.2017 12:13

        Смотря для чего вы их используете. Почему криво-то?

        На мой взгляд, это та же cookie с сессией, только сбоку.
        Но даёт сложности в виде проверки — если access_token протух, то через refresh получить новый access_token и повторить запрос. Ещё +1 обёртка над сетевыми запросами.

        Ну вот сразу: куда вводит логин/пароль? В мобильное приложение?

        В МП. Native наше всё. Ну, и по большому счету, имхо, нет разницы между native и браузером. Меня «радует» рекомендация PCI DSS для ввода данных банковских карт — давать вводить CC number и CVC2 в МП это не безопасно, а через webview это нормально.

        Что должно произойти, если за этот месяц юзер поменял пароль?

        При смене пароля можно отозвать refresh токен. А можно и не отзывать. Ведь возможность работы с функциональностью МП != от значений логина и пароля.

        Т.е. все пляски из-за того, чтобы просто не ломать oauth, но при этом нарушаем РЕКОМЕНДАЦИЮ, чтобы refresh давать только server side приложениям.


        1. Razaz
          07.03.2017 12:17

          Я в мобилках использую Implicit flow. А сессионная кука IdP просто валяется в суки контейнере.
          И не нужен рефреш.

          При смене пароля можете сделать логаут клиентов.


        1. lair
          07.03.2017 12:21

          На мой взгляд, это та же cookie с сессией, только сбоку.

          Семантика другая, но регулярно так же используют. Но семантика другая.


          В МП. Native наше всё.

          Тогда лично вам на OAuth можно уже положить — ваше (или замаскировавшееся под ваше фишинговое) приложение уже получило логин/пароль пользователя.


          Т.е. все пляски из-за того, чтобы просто не ломать oauth, но при этом нарушаем РЕКОМЕНДАЦИЮ, чтобы refresh давать только server side приложениям.

          "Authorization servers MAY issue refresh tokens to web application clients and native application clients."


        1. Razaz
          07.03.2017 13:10

          Вот тут вам для девайсов кстати: https://self-issued.info/?p=1642


  1. Skit25
    07.03.2017 14:07
    +1

    Это прямо более, чем актуальная тема!
    Объяснение чёткое и понятное.
    Комменты полезные.

    В общем, спасибо!


    1. eyeofhell
      07.03.2017 14:08

      Торт Хабр или не торт — от нас зависит :) Присоединяйтесь!


      1. Razaz
        07.03.2017 14:10
        +1

        Вы бы хоть статью поправили. В коментах все разжевали во всех возможных вариантах.


        1. eyeofhell
          07.03.2017 14:33

          Предлагай что править. Сейчас там два, на мой взгляд, самых «ярких» пункта со стека (про использование токенов на разных сервисах и про раздельное хранение) плюс что мне понравилось про ограничение времени атаки. Что, на твой взгляд, можно улучшить?


          1. Razaz
            07.03.2017 14:57

            На стеке то же ошибаются. Еще как :) Есть спецификации, комментарии к ним. Вообще такие вопросы лучше задавать и смотреть на http://security.stackexchange.com. Но обязательно прочитав спеки, так как много очень странных ответов, которые не модерируются.

            1. refresh_token- это креденшалы для доступа в API при отсутствии сессии юзера. access_token — короткоживущий токен для доступа к ресурсу.
            2. Разные требования к хранению и передаче(и выдаче). Тоесть узнать refresh_token вы обычно не можете, так как он не гоняется через front channel.
            3. Работу с общей сессией лучше делегировать на ваш IdP/AS/OP по выбору. Спецификации для этого в комментах привел. Там можете и следить. и ограничивать, и включать любые политики какие вам захочется.

            Если у вас задача аутентификации и авторизации в API и ваши клиенты это серверные приложения, то у вас есть так называемый Client credentials flow. И вам не нужен рефреш в принципе. Просто смотрите за лайфтаймом токена и обновляете его по необходимости.
            Если есть возможность использования сертификатов, то есть client assertion grant: клиент генерирует JWT токен со своей информацией и подписывает его своим приватным ключиком. Сервер валидирует его, проверяет публичную часть (что у регистрации клиента привязан этот сертификат) и если все ок, выдает уже access_token для доступа в API.

            Если веб(SPA) или есть webview то implicit flow. Получаете access_token и вперед в API. Как протухнет — редирект за обновлением или тихонечко в айфрейме запросик. Можно поковырять кишки вот тут.

            Если есть вопросы — спрашивайте тут или в личку.


            1. eyeofhell
              07.03.2017 15:03

              И вот что — это все запихивать в статью, превращая ее в Wall of text? :(
              Пока дописал вот: «Например, хранить access token на frontend, а refresh token на backend»


              1. Razaz
                07.03.2017 15:13

                Если дальше расписывать, но будет еще одна статья :) А времени на нее пока нет :( Тут уж вам посмотреть и решить как лучше. Тут коллективный разум постарался всю проблему в комментах разобрать :)


                1. eyeofhell
                  07.03.2017 15:47

                  267 комментов и продолжает обсуждаться :)


                  1. Razaz
                    07.03.2017 16:21
                    +1

                    Тема очень объемная. Много кейсов, спек, рекомендаций. Сами спеки позволяют что-то либо делать либо нет. Плюс многие крупные вендоры не всегда грамотно имплементируют спеки, что еще добавляет энтропии.


  1. netch80
    11.03.2017 09:11
    +1

    У меня претензия к именованию участников. Обычно в криптографии Алиса и Боб — две стороны защищённого взаимодействия, тут скорее Алиса держала бы сервер, выдающий токены, а Боб — законный пользователь. А воры токенов — это в первую очередь Крейг (Craig). Вот вроде полный список.
    Результат — читая статью, на каждом абзаце приходится переименовывать в голове.
    Данную статью уже не исправить из-за комментариев, где повторены эти имена, но на будущее прошу не сбивать в таких основах.


  1. Blumfontein
    12.03.2017 20:48
    +2

    >> В этом случае Боб получит доступ к сервису на время жизни access token. Как только оно истечет и приложение, которым пользуется Алиса, воспользуется refresh token, сервер вернет новую пару токенов, а те, что узнал Боб, превратятся в тыкву.

    Пользователи с хреновым интернетом (обычно мобильным) будут постоянно ловить логауты из-за того, что запрос на рефреш ушел на сервер, а ответ не получен из-за сбоя подключения. Сервер сгенерирует новые токены, но клиент их не получит, и его (клиента) токены превратятся в тыкву. Можно не инвалидировать старые токены при генерации новых, но тогда Боб может вечно пользоваться своим рефреш-токеном.


    1. Razaz
      13.03.2017 00:07

      Это одна из причин, почему refresh_token мобилкам не надо отдавать. Он должен кататься только между серверами. На мобилке вполне вариант хранить куку сессии с IdP/AS.