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— выпуск и верификация JWTTokenPair/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
Интеграция в существующее приложение
Типовой путь перевода существующего приложения на модуль:
- Импортирует
from authkit_server import ...вместо локального кода - Обёрнет
RefreshTokenRecordв ORM-адаптер для своей БД - Оставит PermissionResolver/OAuth/ExchangeCode локально
Переименование (0.1.0 → 0.1.1)
Кит переименован: дистрибутив s-authkit → s-authkit-server, импорт authkit →
authkit_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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b6a44ed205f63a0f61d1ec1d07edb2e10af408ac5a9cd0a84b9bdc4a2b95d229
|
|
| MD5 |
a59355ac81bff3d13e3e8afe3b62224b
|
|
| BLAKE2b-256 |
717cd00017cf941395c6489007271de8f6089c31a1b7b50568f9fb322bc1a595
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eaf8dbb16b8cb36e8179f4cf5f7818c9a168fefc86509db1d4c616a15bdf3962
|
|
| MD5 |
d97e671825d22591d8a66adc795e5645
|
|
| BLAKE2b-256 |
52522a114b209faadcb52c04ac51c2edb79cd168dfbfca585ea0ff3a55b32dfd
|