From 4c98d27e728e089f8603c57380ba18d2ebd27834 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 29 Mar 2026 21:52:19 +0000 Subject: [PATCH] fix: use csrf_user_token from cookies as auth token --- Untitled.ipynb | 6 +- .../__pycache__/client.cpython-312.pyc | Bin 59798 -> 60231 bytes src/kwork_api/client.py | 64 ++++++++++-------- 3 files changed, 38 insertions(+), 32 deletions(-) diff --git a/Untitled.ipynb b/Untitled.ipynb index c841c93..e677803 100644 --- a/Untitled.ipynb +++ b/Untitled.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 13, "id": "f28552f1-618c-4853-92e2-566554a2de2c", "metadata": {}, "outputs": [ @@ -12,7 +12,7 @@ "True" ] }, - "execution_count": 1, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -28,7 +28,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 14, "id": "953d142e-a575-41b7-927d-8cd1546d2747", "metadata": {}, "outputs": [ diff --git a/src/kwork_api/__pycache__/client.cpython-312.pyc b/src/kwork_api/__pycache__/client.cpython-312.pyc index 43a1a1d330e0bd65a27ef17f4c5f7a74b2712155..fb883fa0652c7fb5b8dab08da17e31b04299386e 100644 GIT binary patch delta 5179 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 delta 4873 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 diff --git a/src/kwork_api/client.py b/src/kwork_api/client.py index 16b5c37..b380e3d 100644 --- a/src/kwork_api/client.py +++ b/src/kwork_api/client.py @@ -51,8 +51,8 @@ class KworkClient: Аутентификация: Клиент использует двухэтапную аутентификацию Kwork: - 1. POST /signIn — получение session cookies - 2. POST /getWebAuthToken — получение web_auth_token + 1. POST /signIn - получение session cookies + 2. POST /getWebAuthToken - получение web_auth_token Примеры использования: # Вход по логину/паролю @@ -99,7 +99,7 @@ class KworkClient: token: Web auth token, полученный через `getWebAuthToken` или `login()`. Если указан, автоматически добавляется в cookies. cookies: Session cookies из предыдущей аутентификации. - Обычно не требуется — устанавливаются автоматически из token. + Обычно не требуется - устанавливаются автоматически из token. timeout: Таймаут HTTP запросов в секундах. По умолчанию 30 секунд. base_url: Кастомный базовый URL. Используется только для тестирования. @@ -197,8 +197,8 @@ class KworkClient: Аутентификация по логину и паролю. Выполняет двухэтапный процесс аутентификации Kwork: - 1. POST /signIn — проверка учётных данных, получение session cookies - 2. POST /getWebAuthToken — обмен cookies на web_auth_token + 1. POST /signIn - проверка учётных данных, получение session cookies + 2. POST /getWebAuthToken - обмен cookies на web_auth_token Полученный токен и cookies сохраняются в клиенте для последующих запросов. @@ -264,19 +264,25 @@ class KworkClient: raise KworkAuthError(f"Login failed: {response.status_code}") response_data = response.json() - - # Extract userId from response or cookies cookies = dict(response.cookies) - user_id = response_data.get("user_id") or cookies.get("userId") - + + # Debug: print response for troubleshooting + import logging + logger = logging.getLogger(__name__) + logger.debug(f"Login response: {response_data}") + logger.debug(f"Login cookies: {cookies}") + + # Extract userId from cookies + user_id = cookies.get("userId") + if not user_id: - raise KworkAuthError("Login failed: no userId in response") - - # Extract web_auth_token from response - web_token = response_data.get("web_auth_token") or cookies.get("web_auth_token") + raise KworkAuthError(f"Login failed: no userId in cookies. Response: {response_data}") + # Extract auth token from cookies (Kwork uses csrf_user_token or slrememberme) + web_token = cookies.get("csrf_user_token") or cookies.get("slrememberme") + if not web_token: - raise KworkAuthError("No web_auth_token in response") + raise KworkAuthError(f"No auth token in cookies. Cookies: {cookies}") # Create new client with token return cls(token=web_token, cookies=cookies, timeout=timeout) @@ -430,14 +436,14 @@ class KworkClient: Args: page: Номер страницы для пагинации (начиная с 1). category_id: ID категории для фильтрации. - Если None — все категории. + Если None - все категории. sort: Опция сортировки. Варианты: - - "recommend" — по рекомендации (по умолчанию) - - "price_asc" — по возрастанию цены - - "price_desc" — по убыванию цены - - "rating" — по рейтингу - - "reviews" — по количеству отзывов - - "newest" — по дате создания + - "recommend" - по рекомендации (по умолчанию) + - "price_asc" - по возрастанию цены + - "price_desc" - по убыванию цены + - "rating" - по рейтингу + - "reviews" - по количеству отзывов + - "newest" - по дате создания Returns: CatalogResponse содержащий: @@ -571,7 +577,7 @@ class KworkClient: Args: page: Номер страницы (начиная с 1). category_id: ID категории для фильтрации. - Если None — все категории. + Если None - все категории. Returns: ProjectsResponse содержащий: @@ -609,7 +615,7 @@ class KworkClient: независимо от их статуса (открыт, в работе, завершён). Returns: - Список проектов где текущий пользователь — заказчик. + Список проектов где текущий пользователь - заказчик. Example: orders = await client.projects.get_payer_orders() @@ -627,7 +633,7 @@ class KworkClient: назначен исполнителем. Returns: - Список проектов где текущий пользователь — исполнитель. + Список проектов где текущий пользователь - исполнитель. Example: work = await client.projects.get_worker_orders() @@ -691,11 +697,11 @@ class KworkClient: """ Получить отзывы пользователя. - Если user_id не указан — возвращает отзывы текущего пользователя. - Если указан — отзывы другого пользователя по ID. + Если user_id не указан - возвращает отзывы текущего пользователя. + Если указан - отзывы другого пользователя по ID. Args: - user_id: ID пользователя. Если None — текущий пользователь. + user_id: ID пользователя. Если None - текущий пользователь. page: Номер страницы для пагинации (начиная с 1). Returns: @@ -811,7 +817,7 @@ class KworkClient: """ Получить доступные дополнительные функции (features). - Features — это платные дополнения к кворкам: + Features - это платные дополнения к кворкам: - Увеличенные сроки - Дополнительные правки - Приоритетная поддержка @@ -996,7 +1002,7 @@ class KworkClient: """ Получить пользовательские предпочтения (wants). - Wants — это настройки интересов пользователя: + Wants - это настройки интересов пользователя: - Предпочитаемые категории - Ключевые слова для мониторинга - Фильтры для поиска