--- title: Сайт ведёт сессию на куках description: Объявите это одной строкой на корне SDK, и куки сайта будут ходить между запросами сами, как в браузере. sources: - eazy_sdk.auth.Cookies - eazy_sdk.cookies.CookieJar - eazy_sdk.cookies.CookieState - eazy_sdk.cookies.StoredCookie - eazy_sdk.identity.Identity api_fingerprint: "sha256:9bd88bfe3cfdd65bc956a74024813db3ebd3476598837539957de805570d8878" --- Вход на обычный сайт занимает несколько шагов. Страница входа ставит одни куки, форма входа другие, переход на почту третьи, и каждый следующий запрос должен нести то, что поставили предыдущие. Браузер делает это сам. Перечислять куки в операциях и носить их от ответа к запросу руками не нужно. Объявите `Cookies` на корне SDK. С этого момента каждая кука, которую поставил сайт, возвращается туда, куда положено: в следующую операцию, в следующий шаг перехода, на другой хост того же сайта. ```{literalinclude} ../../../../../../examples/docs/auth_redirect_login.py :language: python :start-after: "# region docs: redirect-login-root" :end-before: "# endregion docs: redirect-login-root" ``` `required=("sid",)` называет куку, без которой сессии нет. Это единственное место, где SDK упоминает куку сайта по имени. ## Шаги входа Шаги входа остаются обычными операциями. Первый читает исход из адреса перехода, второй открывает страницу почты и достаёт токен из встроенного скрипта. Ни одна операция не объявляет поле для куки. ```{literalinclude} ../../../../../../examples/docs/auth_redirect_login.py :language: python :start-after: "# region docs: redirect-login-steps" :end-before: "# endregion docs: redirect-login-steps" ``` Сервис входа вызывает шаги по порядку. Кука `sid` из ответа на первый шаг уходит во второй запрос без его участия. ```{literalinclude} ../../../../../../examples/docs/auth_redirect_login.py :language: python :start-after: "# region docs: redirect-login-session" :end-before: "# endregion docs: redirect-login-session" ``` Модель сессии держит только то, что SDK кладёт в запрос сам: токен и адрес ящика. Кук в ней нет. На сайте, где вся сессия живёт в куках, модель может не размещать ничего. :::{dropdown} Запускаемый файл целиком ```{literalinclude} ../../../../../../examples/docs/auth_redirect_login.py :language: python ``` ::: ```text folders: Inbox, Sent POST /cgi-bin/auth GET /mailbox/ GET /sdc GET /mailbox/ GET /api/folders GET /api/folders wrong password: fail 1 ``` Вход выполнен один раз: второй вызов `folders()` использует ту же сессию. Отказ входа доходит до вызывающего тем типом, каким его объявила операция. ## Переход, записанный в странице Между вторым и четвёртым запросом видно, чего SDK не объявлял. Учебный сайт отвечает на первое открытие почты страницей-заглушкой с ``, которая отправляет на второй хост. Тот ставит ещё одну куку и возвращает обратно обычным `302`. Почта открывается только после этого. Такой переход клиент выполняет сам, если его включить: `Resilience(max_redirects=3, client_redirects=True)`. Он расходует тот же запас, что и `302`. Куки, поставленные по дороге, доходят до следующего шага. Страница считается заглушкой, только если её не приняла ни одна модель операции. Страницу, которую операция описала, клиент не трогает. ## Чьи это куки Куки принадлежат пользователю, то есть объекту `Identity`, а не клиенту и не соединению. | Ситуация | Что происходит | |---|---| | два `Identity` на одном клиенте | куки не смешиваются | | один `Identity` на двух клиентах | куки общие | | роутеры одного корня | делят куки одного пользователя | | `Identity(cookies=...)` | пользователь начинает с готового набора, например из браузера | Каждая кука хранится со своей областью действия: хост или домен с поддоменами, путь, срок, признак `Secure`. Кука без атрибута `Domain` возвращается только на тот хост, который её поставил. Кука без срока хранится наравне с остальными. ## Между запусками Снимок кук сохраняется вместе с сессией в том же `SessionStore`, под той же ревизией. Новый процесс читает его из хранилища и продолжает без входа. Библиотека не очищает куки сама: ни перед входом, ни после отказа. Кука исчезает, когда её удалил сервер или истёк срок. Так сайт узнаёт устройство при повторном входе и реже просит второй фактор. Чтобы начать с чистого листа, удалите запись пользователя вызовом `store.invalidate(key)` или заведите новый `Identity`. ## Куки, которые выбирает вызывающий Правило одно. Куку, которую поставил сайт, несёт библиотека. Куку, значение которой выбирает вызывающий, объявляют полем операции. | Чьё значение | Как объявить | |---|---| | сайта, из `Set-Cookie` | `Cookies(...)` на корне, имя писать не нужно | | вызывающего: язык, согласие, параметр из документации | поле `Cookie[str]` у операции | Поле `Cookie[str]` перекрывает одноимённую куку сайта только в этом запросе и не сохраняется. `Http.get(..., cookies=False)` выводит операцию из общего порядка: она не получает кук и не сохраняет их. Значение куки как данные читает маркер `FromCookie("act")` в модели ответа. Он нужен, когда значение идёт в тело запроса, например защитный токен формы. ## Что проверяется при объявлении | Объявление | Причина отказа | |---|---| | `CookieScheme` в `security` вместе с `Cookies` | сайт либо ведёт сессию сам, либо получает ключ от вызывающего | | заголовок `Cookie` полем или `Inject` | заголовок строит библиотека | | `cookies=False` на сервисе без `Cookies` | отказываться не от чего | | `Identity(cookies=...)` для сервиса без `Cookies` | куки некуда положить | Вход, после которого сайт не поставил куку из `required`, заканчивается ошибкой проверки сессии: обмен завершился, а сессии нет. ## Дальше - {doc}`Опишите исходы входа по адресу перехода `. - {doc}`Положите токен из сессии в query или заголовок `. - {doc}`Передайте куку, которую выбирает вызывающий `.