Skip to main content

s-authkit-client — слой ДОСТУПА

Библиотека, отвечающая на один вопрос: сервис меня узнаёт? Всё, что нужно, чтобы представиться сервису и удержать это представление живым — хранилище сессий, хранилище пары токенов, OAuth-обновление по сроку и живая проба доступа.

Браузера внутри нет: ни импорта, ни зависимости, ни лениво (см. ниже).

pip install s-authkit-client          # Python >= 3.11
pip install "s-authkit-client[probe]" # + сетевое колесо для живой пробы

Место в семействе китов

    clientkit                     <- сборщик клиентов по декларации
    /        \
authkit-client  browserkit        <- доступ (кто ты) / чеканка (как войти)
    \        /
      netkit                      <- сеть (чем ходить)
         |
      corekit                     <- основание (значения и чистые правила)

Обязательная зависимость одна — s-corekit (значения SessionRef, ExecutionContext, канон диагностики доступа). Соседи сверху и сбоку — отдельные пакеты, и ни один из них этому киту не нужен.

Что внутри

модуль что делает
authkit_client.sessions SessionStore: каталог — источник правды, SQLite — производный индекс без секретов, storage_state под Fernet (DEK в .dek.enc, обёрнут KEK из keyring / env / парольной фразы)
authkit_client.secret_store SecretStore: пара токенов (access/refresh) в keyring с прозрачным file-fallback (Windows CredWrite ломается на длинных JWT — WinError 1783)
authkit_client.oauth OAuthSession: проактивное обновление по TTL (живой токен = ноль сетевых вызовов), single-flight, окончательный OAuthRevoked вместо вечного ретрая
authkit_client.access_probe ЖИВАЯ проба: спросить сервер, а не посмотреть на файл с куками
authkit_client.session_fingerprint инвариант «один egress + один профиль»: сессия, отчеканенная через один выход, не уезжает в другой
authkit_client.ports протоколы SessionStorePort / SecretStorePort / FetchPort / MinterPort и реестр чеканщиков
authkit_client.context_port ссылка на носителя ExecutionContext (мультитенантность), если его завёл вышележащий слой
authkit_client.diagnosis реэкспорт AccessState / AccessVerdict / classify_access из corekit

Импорт самого кита ничего тяжёлого не исполняет: sqlite3, cryptography, keyring, platformdirs подтягиваются по месту использования (PEP 562) — платит тот, кто действительно открыл хранилище или полез в keyring.

Хранилище сессий

import asyncio
from corekit import SessionRef
from authkit_client import SessionStore

store = SessionStore(root="~/.myapp")           # состояние ляжет в <root>/sessions/
ref = SessionRef(profile="work", social="example", account_id="me@example.com")

async def main() -> None:
    await store.save_state(ref, {"cookies": [], "origins": []})  # шифруется
    state = await store.load_state(ref)                          # расшифровывается
    print(store.path_for(ref), await store.list(profile="work"))

asyncio.run(main())

Каталоги 0700 / файлы 0600 выставляются кросс-платформенно (на Windows — через ACL). storage_state шифруется Fernet-ом: случайный DEK лежит в .dek.enc, обёрнутый KEK из OS keyring, переменной окружения или парольной фразы (Argon2id). Открытым текстом состояние молча не пишется никогда: если KEK недоступен, а среда неинтерактивна — отказ с громким предупреждением.

Пара токенов

from authkit_client import SecretStore

secrets = SecretStore("myapp")               # keyring-namespace = "myapp"
secrets.save_tokens("me@example.com", access="…", refresh="…")
access, refresh = secrets.load_tokens("me@example.com")

Порядок чтения: env MYAPP_ACCESS_TOKEN / MYAPP_REFRESH_TOKEN → keyring → файловый fallback. Fallback включается сам, когда keyring отказал (например, Windows Credential Manager на blob'е длиннее ~2.5 КБ).

OAuth с проактивным обновлением

from authkit_client import OAuthSession, OAuthTokens

session = OAuthSession(
    OAuthTokens(access_token="…", refresh_token="…", expires_at=1893456000.0),
    refresher=refresh_call,   # async (payload) -> Mapping: чем ходить — дело вызывающего
    on_tokens=persist,        # куда сохранять обновлённую пару
)
await session.maybe_refresh()  # внутри срока — сравнение чисел, ноль запросов

Своего HTTP-клиента модуль не имеет: обмен кода и обмен refresh-токена делает колбэк потребителя. Отказ invalid_grant считается окончательным — дальнейшие вызовы падают OAuthRevoked мгновенно, вместо того чтобы долбить сервер.

Живая проба доступа

from authkit_client import probe_access, probe_for

@probe_for("example", endpoint_markers=("unknown method",))
async def _probe(ctx):
    return await client.get("https://example.com/", follow_redirects=True)

verdict = await probe_access("example", ctx)
if verdict.needs_human:
    ...  # сервер действительно отверг куки, а не «сети нет»

Вердикт выносится по подписи ответа, а не по наличию файла с куками: «сервер увёл на страницу входа» (нужен человек) и «до сервера не дошли» (чинить выход) — разные состояния, и путать их дорого. Сеть приносит сама проба: своего HTTP-клиента у кита нет, подойдёт httpx, curl_cffi или что угодно утиное.

Браузера здесь нет

Минт сессии — дело браузерного слоя, и он приходит сюда через порт:

from authkit_client.ports import register_minter, minted_state

class NodriverMinter:                       # живёт в браузерном ките
    name = "nodriver"
    async def mint(self, ref, ctx=None): ...

register_minter(NodriverMinter())
state = await minted_state("nodriver", ref)  # слой доступа не знает про браузер

Причина простая: клиенту белого API с api-ключом браузер не нужен никогда, а жадный импорт стоил бы ему playwright/nodriver на каждом старте. Инвариант закреплён tests/test_no_browser_imports.py — тест смотрит на sys.modules свежего интерпретатора, а не на строчки исходника.

Хранилища подменяемы

SessionStorePort / SecretStorePort — структурные протоколы: подходит любой объект с нужными методами. Реализация «из памяти» проходит их без единой строки SQL, и код потребителя при подмене не правится (tests/test_ports.py).

Совместимость

Кит выделен из s-librarykit, поэтому у тех, кто ставит их вместе, прежние пути импорта — librarykit.sessions, librarykit.secret_store, librarykit.access_probe, librarykit.oauth, librarykit.session_fingerprint — продолжают работать: там это sys.modules-алиасы на те же объекты модулей, а не копии, включая приватные имена, которые патчат тесты. Сам по себе s-authkit-client самодостаточен и librarykit не требует.

Сохранены и прежние имена переменных окружения / keyring-namespace'ов: новые ключи пишутся в нейтральный namespace, а ранее созданные сессии по-прежнему расшифровываются через fallback.

Лицензия

MIT — см. LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

s_authkit_client-0.0.2.tar.gz (59.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

s_authkit_client-0.0.2-py3-none-any.whl (59.4 kB view details)

Uploaded Python 3

File details

Details for the file s_authkit_client-0.0.2.tar.gz.

File metadata

  • Download URL: s_authkit_client-0.0.2.tar.gz
  • Upload date:
  • Size: 59.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for s_authkit_client-0.0.2.tar.gz
Algorithm Hash digest
SHA256 45046732125e814663dfa1d0ca7169c97372e08fb203f722bb55cf0b87c15f2b
MD5 a314609b184e35c855737df5b271d24a
BLAKE2b-256 0cdcf016067fcc1e5566d3bbb5d8f4fbe692f75b143681f9965094ec8207bcb4

See more details on using hashes here.

File details

Details for the file s_authkit_client-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: s_authkit_client-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 59.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for s_authkit_client-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 95c53ab59d4ee7623e142a6a511ccf635dad47227f227b67a032cb8562a3215d
MD5 e32f31100efadd61c69b4d4c07037dce
BLAKE2b-256 154927dbbbc563ff2bfce751fdc02d18bdcbaf96efe926c933d9ec51228cf058

See more details on using hashes here.

Release history Release notifications | RSS feed

0.0.4

2 files

0.0.3

2 files

This release

0.0.2 This release

2 files

0.0.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page