Skip to main content

s-authkit-server

СЕРВЕРНОЕ ядро авторизации для Skillery проектов: JWT RS256, refresh-token management, Argon2 hashing. Импорт — authkit_server.

Версия: 0.1.1
Лицензия: MIT
Зависимости: python-jose[cryptography], passlib[argon2], cryptography

Какая это сторона

Кит Импорт Чью авторизацию решает
s-authkit-server (этот) authkit_server НАШ сервис авторизует СВОИХ пользователей: свои токены, свои пароли, свои refresh
s-authkit-client authkit_client НАШ код получает доступ к ЧУЖИМ сервисам: сессии, секреты, OAuth-обновление, живая проба

Суффикс имени и есть указатель стороны. До 0.1.1 этот кит назывался s-authkit (импорт authkit) — из-за пары «authkit / authkit-client» серверный кит читался как SDK к клиентскому. См. раздел «Переименование» ниже.

Что входит

JWT access-токены (RS256)

  • JoseTokenIssuer — выпуск и верификация JWT
  • TokenPair / TokenClaims — value objects для типизации
  • ensure_jwt_keypair — генерация RSA 2048 ключей

Persistent refresh-tokens

  • IRefreshTokenStore — protocol для БД-реализации
  • RefreshTokenRecord — агрегат с хешированием и валидацией
  • Rotation с audit-trail (rotated_from)

Password hashing

  • Argon2PasswordHasher — Argon2id через passlib

Примеры

1. Инициализация

from pathlib import Path
from authkit_server import (
    JoseTokenIssuer,
    Argon2PasswordHasher,
    ensure_jwt_keypair,
)

# Создаём ключи (если нет)
keys_dir = Path.home() / ".myapp" / "keys"
ensure_jwt_keypair(
    private_path=keys_dir / "jwt_private.pem",
    public_path=keys_dir / "jwt_public.pem",
)

# Прочитаем ключи
private_key = (keys_dir / "jwt_private.pem").read_text()
public_key = (keys_dir / "jwt_public.pem").read_text()

# Создаём компоненты
hasher = Argon2PasswordHasher()
issuer = JoseTokenIssuer(
    private_key_pem=private_key,
    public_key_pem=public_key,
    issuer="myproject.com",
    access_ttl_min=15,
    refresh_ttl_days=30,
    refresh_store=your_store_impl,  # реализуете вы
)

2. Login (выпуск пары токенов)

async def login_with_password(username: str, password: str):
    # Получаем юзера из БД
    user = await db.get_user_by_username(username)
    if not user:
        raise ValueError("User not found")
    
    # Проверяем пароль
    if not hasher.verify(password, user.password_hash):
        raise ValueError("Invalid password")
    
    # Выпускаем пару
    pair = await issuer.issue_pair(
        user_id=str(user.id),
        company_id=str(user.active_company_id) if user.active_company_id else None,
        permissions={"skill.read", "skill.install"},
    )
    return pair

3. Middleware (верификация токена)

from authkit_server import TokenError

async def auth_middleware(request, call_next):
    auth_header = request.headers.get("Authorization", "")
    if not auth_header.startswith("Bearer "):
        return Response("Unauthorized", status_code=401)
    
    token = auth_header[7:]
    try:
        claims = issuer.verify_access(token)
    except TokenError as e:
        return Response(f"Invalid token: {e}", status_code=401)
    
    request.state.claims = claims
    return await call_next(request)

4. Refresh (rotation)

async def refresh_session(refresh_token: str):
    try:
        new_pair = await issuer.rotate_refresh(refresh_token)
    except RefreshTokenError as e:
        raise Unauthorized(f"Refresh failed: {e}")
    return new_pair

5. Реализация IRefreshTokenStore

from authkit_server import RefreshTokenRecord, IRefreshTokenStore

class PostgresRefreshTokenStore:
    def __init__(self, db_engine):
        self.engine = db_engine
    
    async def save(self, record: RefreshTokenRecord) -> None:
        # INSERT/UPDATE в БД
        async with self.engine.begin() as conn:
            await conn.execute(
                "INSERT INTO refresh_tokens (user_id, token_hash, ...) VALUES (...)"
            )
    
    async def get_by_hash(self, token_hash: str) -> RefreshTokenRecord | None:
        # SELECT * FROM refresh_tokens WHERE token_hash = ?
        ...
    
    async def revoke_all_for_user(self, user_id: str) -> int:
        # UPDATE refresh_tokens SET revoked_at = NOW() WHERE user_id = ?
        ...
    
    # Реализуете остальные методы Protocol'а

Архитектура

authkit_server/
├── __init__.py              # Public API
├── exceptions.py            # AuthKitError, TokenError, ...
├── token/
│   ├── models.py            # TokenPair, TokenClaims
│   ├── jose_issuer.py       # JoseTokenIssuer
│   └── keys.py              # ensure_jwt_keypair
├── refresh/
│   └── models.py            # RefreshTokenRecord, IRefreshTokenStore
└── password/
    └── hasher.py            # Argon2PasswordHasher

Тестирование

pytest tests/                      # Все тесты
pytest tests/ -v --cov           # С coverage report
pytest tests/ -k "test_verify"   # Конкретный тест

Coverage gate: ≥ 80%.

Что НЕ входит (v0.1)

  • OAuth (YandexOAuth, ExchangeCode) — в v0.2+
  • PermissionResolver — остаются на стороне приложения
  • Redis cache invalidation — в v0.2+ как optional
  • Signing PK/SK rotation — будущая фича
  • Sync обёртки — только async API в v0.1

Интеграция в существующее приложение

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

  1. Импортирует from authkit_server import ... вместо локального кода
  2. Обёрнет RefreshTokenRecord в ORM-адаптер для своей БД
  3. Оставит PermissionResolver/OAuth/ExchangeCode локально

Переименование (0.1.0 → 0.1.1)

Кит переименован: дистрибутив s-authkits-authkit-server, импорт authkitauthkit_server. Публичный API (имена классов и функций) не менялся — правится только строка импорта и строка зависимости.

Со старым дистрибутивом на PyPI:

  • s-authkit==0.1.0 остаётся опубликованным как есть — его не отзываем (yank) и не удаляем: он рабочий, а отзыв сломал бы любую уже собранную сборку;
  • новых выпусков под именем s-authkit больше не будет — имя заморожено на 0.1.0;
  • переименование на PyPI «на месте» невозможно: s-authkit-server — это НОВЫЙ дистрибутив, первый его выпуск — 0.1.1 (нумерация продолжает историю кита, а не начинается заново, чтобы версия читалась как «то же ядро, новое имя»).

Миграция потребителя — две строки:

-"s-authkit>=0.1.0",
+"s-authkit-server>=0.1.1",
-from authkit import Argon2PasswordHasher, ensure_jwt_keypair
+from authkit_server import Argon2PasswordHasher, ensure_jwt_keypair

Лицензия

MIT

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_server-0.1.1.tar.gz (66.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_server-0.1.1-py3-none-any.whl (18.8 kB view details)

Uploaded Python 3

File details

Details for the file s_authkit_server-0.1.1.tar.gz.

File metadata

  • Download URL: s_authkit_server-0.1.1.tar.gz
  • Upload date:
  • Size: 66.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for s_authkit_server-0.1.1.tar.gz
Algorithm Hash digest
SHA256 b6a44ed205f63a0f61d1ec1d07edb2e10af408ac5a9cd0a84b9bdc4a2b95d229
MD5 a59355ac81bff3d13e3e8afe3b62224b
BLAKE2b-256 717cd00017cf941395c6489007271de8f6089c31a1b7b50568f9fb322bc1a595

See more details on using hashes here.

File details

Details for the file s_authkit_server-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: s_authkit_server-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 18.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for s_authkit_server-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 eaf8dbb16b8cb36e8179f4cf5f7818c9a168fefc86509db1d4c616a15bdf3962
MD5 d97e671825d22591d8a66adc795e5645
BLAKE2b-256 52522a114b209faadcb52c04ac51c2edb79cd168dfbfca585ea0ff3a55b32dfd

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.1 This release

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