• web programming AUTH А какой у нас сейчас state of the art для авторизации на связанных сайтах. Есть один главный сайт foo.bar.com, есть N сервисов (только web sockets) на которые юзверь так же может посылаться. На foo.bar.com есть авторизация через oauth, так же проверяются права на то, какие сервисы есть права и где они живут и туда посылается юзверь.

    Вопрос в том, как сделать так чтобы сервис мог бы дешево проверять что юзверь это действительно нормальный юзверь и имеет доступ, не реализуя логику авторизации на двух узлах
    ♡ recommended by @anton0xf, @rufuse

Replies (29)

  • @qnikst, Правильно я понимаю, что если он авторизовался на foo.bar.com, то всегда можно этот факт сохранить либо на foo.bar.com, либо где-то ещё, а потом с других точек можно пробрасывать логику сравнения туда, где хранится факт авторизации?
  • @agr, сервисы и foo.bar.com могут общаться между собой по приватной сети. Но если этого можно как-то избежать, то было бы круче.
  • @qnikst, сохранять тоже можно.
  • @agr, Это получаются токены, да?
  • @Renha, да
  • @segfault, Не дочитал. Не реализуя на двух узлах — это фантастика имх.
  • @segfault, ну сервер может знать публичный ключ сервиса и шифровать какое-то сообщение, которое вместе с токеном клиент отдает для авторизации, итого авторизация есть, но возможно дешевле чем oauth
  • @segfault, удваиваю OAuth. Я его на днях ковырял.
  • @alar, oauth и так на главном, иметь его на каждом узле как-то печально
  • @qnikst, Так оно же из двух кусков состоит: сервер и relying party.
  • @alar, мне ж на каждом из сервисов придется медленную авторизацию с беганием к провайдеру делать?
  • @qnikst, к серверу OAuth, да. Чтобы убедиться, что то, что тебе клиент впарил в качестве токена — действительно выдал сервер. Вот лично у меня всё крутится вне открытых интернетов, и провайдеру я доверяю, и можно было бы обойтись двумя кэшами секретных токенов: у меня и у провайдера, и схемой их регенерации (а её для OAuth2 всё равно надо городить), но это получится свой велосипед, некруто.
  • @alar, мне очень не хочется делать вторую авторизацию видимой для пользователя и жрущую время идеально это должно быть невидимо.
  • @qnikst, у меня по замыслу она тоже должна быть невидимой: пользователь ко мне приходит по ссылке с провайдера сразу с токеном. Правда эти токены надо как-то заранее сгенерить, этот момент на схеме OAuth2 условно не показан, да.
  • @alar, я не понимаю.. мне нужно чтобы когда его пошлют на сервис пользователь не ходил к провайдеру ещё раз, это возможно?
  • @qnikst, Для этого его должны на сервис послать уже с каким-то токеном доступа, так?
  • @alar, видимо
  • @qnikst, а этот токен он откуда получит? В классическом OAuth2 его посылают за токеном к провайдеру редиректом. В моём случае юзер на мой сервис придёт по ссылке с провайдера, и предполагается, что провайдер откуда-то заранее узнает токен и предусмотрительно ему подложит в ссылку.
  • @alar, смотри, у меня в 0 вопрос, как бы сделать это без редиректа к провайдеру; мне в ответ предлагают сделать редирект к провайдеру, мне это не очень нравится. Я вижу что то, что я хочу в принципе возможно сделать, но поидее оно обрастет кучей костылей для того, чтобы быть полноценным решением, чего бы мне не хотелось :
  • @qnikst, Я из тебя пытаюсь вытянуть, как клиент получит токен, который он в классическом случае добывает через редирект к провайдеру. В твоём юзкейсе.

    поидее оно обрастет кучей костылей для того, чтобы быть полноценным решениемВ моём юзкейсе OAuth2, но он уже обрастает костылями. Потому что иначе неудобно. Один костыль — наколенная схема регенерации токенов растёт прямо из самого OAuth2.
  • @alar, в том, что я вижу, мы можем на сервере сгенерировать блоб, в котором публичным ключем клиента зашифрованы: информация для авторизации, timestamp, token + token, клиент шлёт этот блоб при создании соденинения и по нему происходит аутенфикация, дальше для этой сессии достаточно только токена.
    В этой схеме не хватает механизма обновления токенов разве что.
  • @qnikst, в этой схеме не раскрыто, как клиент получает "блоб", сгенерированный на сервере
  • @alar, клиент делает коннект к foo.bar.com авторизуется через oauth, делает запрос на сервис, ему в ответ приходят адрес и блоб
  • @qnikst, умный клиент сам раскрывает блоб и дальше спокойно работает?
  • @alar, не раскрывает, а тупо пересылает сервису когда конеектится
  • @qnikst, Пользуясь случаем сначала хочу заметить что oauth2 это просто хипстерская реимплементация Kerberos-а =)

    Теперь по сути. Про термины. SSO состоит из логинилки и верификатора. Логинилка содержит сколь угодно сложную логику хоть с рассылкой sms хоть с oauth (как у тебя) и делает аутентификацию пользователя (выясняет что он тот за кого себя выдает) а затем иногда может делать авторизацию (выяснят что тому разрешено) если конечно логинилка имеет необходимый доступ до разных систем привилегий всего зоопарка приложений. Но часто логинилка делает только аутентификацию и оставляет авторизацию конкретному приложению. При успешной аутентификации логинилка возвращает клиенту нечто (назовем его cookie), что необходимо затем скармливать с каждым запросом к любому из сервисов. Верификатор же, должен проверять валидность cookie в каждом запросе. Дальше посмотрим на варианты верификатора:

    Лично предпочитаю при любом дальнейшем раскладе выносить верификатор на http фронтенд а ля nginx при помощи модуля ngx_http_auth_request_module nginx.org
    nginx делает авторизационный подзапрос который к случае успеха вернут 200 и пару дополнительных http заголовков с именем пользователя и возможно списком привилегий для веб приложений. В пределах минуты авторизационные ответы можно закэшировать штатными средствами и подразгрузить верификатор. Главное что мы получаем единую логику верификатора для всего зоопарка.

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

    2. В качестве cookie используем plaintext набор параметров содержащих timestamp протухания и HMAC всей исходной строки ( exp=1234567890&data=pupkin&digest=HMAC )
    Схема подробно разобрана тут : pdos.csail.mit.edu
    Главное использовать не hash а именно HMAC и не сходить с тропинки. =) Логинилка и сервера фронтендов шарят между собой только секрет используемый для генерации HMAC. Динамическое расшареное состояние отсутствует вообще. То что cookie протух можно проверить до его полной верификации ибо plaintext. Минус что нельзя пихать чувствительную информацию о пользователе ибо подпись есть, а криптации нет. Но во много схема оптимальна.

    3. Используем public key криптографию для cookie. Логинилка подписывает своим приватным ключем, а затем криптует публичным ключем верификаторов. Если фронтенд серверов больше одного, то нужно шарить среди них приватный ключ верификатора. Однако отсутствует расшаренное состояние с логинилкой. Компрометация приватного ключа верификатора позволяет прочитать авторизационную информацию, но не компрометирует саму авторизацию. Очень легко тут накосячить в реализации, от существующих в дикой природе волосы встают дыбом. Вероятен смертный грех "изобретения своего криптографического протокола"
  • @gbdj, спасибо!
  • @qnikst, на здоровье =)