Может ли Django определить доступ к сторонним приложениям
У меня есть приложение Django, которое аутентифицируется с помощью allauth и, для всего, что связано с REST, dj-rest-auth.
Я нахожусь в процессе формализации API
- Мое приложение Django использует конечные точки API в общем случае через команды javascript fetch. Оно аутентифицируется с помощью системы аутентификации Django (с помощью allauth). Оно не использует dj-rest-auth для аутентификации, оно использует встроенную систему аутентификации Django.
- Мой бот Discord использует API, как это типично для сторонних приложений. Он аутентифицируется через dj-rest-auth, что означает, что он внутренне обрабатывает токены обновления и сессии, как определено в документации dj-rest-auth.
В настоящее время API полностью открыт, что означает, что любой может использовать cURL для доступа к конечным точкам, некоторые из которых могут быть доступны анонимному пользователю. Другие требуют аутентификации пользователя, и это делается с помощью данных заголовка запроса: Authorization: Bearer
, о чем заботится dj-rest-auth (это то, что использует мой бот Discord).
Теперь я хочу расширить это, включив в пакет API Django Rest Framework API, чтобы API-ключи были необходимы для идентификации сторонних клиентских приложений. Например, мой бот Discord будет использовать данные заголовка Authorization: Api-Key
для идентификации себя как клиента API. Сторонние разработчики должны будут следить за тем, чтобы их API-ключ не был использован не по назначению. Идея заключается в том, что API-ключ используется как дополнительный уровень авторизации, позволяющий применять различные/дополнительные средства доступа и дросселирования. Например, моему боту Discord будет предоставлено более мягкое дросселирование, чем, скажем, обычному пользователю, который подал заявку на API-ключ.
Теперь мой вопрос...
API-ключи можно легко скрыть в созданном на заказ боте Discord, написанном на python, потому что они скрыты от пользователей бота, но что мне делать с моим обычным/текущим Django-приложением, использующим систему шаблонов? Если я использую HasAPIKey
проверку прав доступа для всех конечных точек, как описано в пакете Django Rest Framework API, то моему собственному Django-приложению будет отказано в доступе.
Я вижу два варианта
- Либо вставлять Api-Key через промежуточное ПО (и таким образом обращаться с моим Django-приложением как с любым другим клиентом), либо,
- Добавление проверки разрешения в мои конечные точки DRF, например, IsUsingFirstPartyApp.
Проблема в том, что я могу придумать только один способ определить "доступ первой стороны", и это через данные заголовков запросов HTTP_ORIGIN и HTTP_REFERER.
У меня может получиться что-то вроде следующего (комментарии к коду удалены для краткости)...
class IsUsingFirstPartyApp(permissions.BasePermission):
def __init__(self):
self.ALLOWED_ORIGIN = getattr(settings, 'SITE_URL', None)
if not self.ALLOWED_ORIGIN:
raise ValueError("SITE_URL is not set in Django settings.")
def has_permission(self, request, view):
origin = request.META.get('HTTP_ORIGIN')
referer = request.META.get('HTTP_REFERER')
def is_valid_origin(header_value):
try:
parsed_origin = urlparse(header_value)
return f"{parsed_origin.scheme}://{parsed_origin.netloc}" == self.ALLOWED_ORIGIN
except ValueError: # Catch specific parsing errors
return False
if origin:
return is_valid_origin(origin)
if referer:
return is_valid_origin(referer)
return False
Для этого он проверяет, выполняются ли запросы от него самого, например, с app.example.com. Если я объединю это с проверкой разрешения HasAPIKey (т.е. логическое ИЛИ), то мой обычный Django-сайт сможет использовать API-Key меньше, как это было всегда. Я могу проверить это локально с помощью cURL, где cURL получает ошибку аутентификации, но анонимный пользователь, использующий сайт через браузер, может получить доступ к конечной точке просто отлично (через javascript fetch request).
Единственная альтернатива, которую я могу предложить, это перенести эту проверку в промежуточное ПО Django и вставить API-Key в заголовок запроса. Это дало бы преимущества для контролируемого дросселирования.
Или, может быть, то, что я делаю, совершенно не подходит и не является лучшей практикой. Я на правильном пути, или есть другой способ, которым я должен это делать?