Может ли 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 в заголовок запроса. Это дало бы преимущества для контролируемого дросселирования.

Или, может быть, то, что я делаю, совершенно не подходит и не является лучшей практикой. Я на правильном пути, или есть другой способ, которым я должен это делать?

Вернуться на верх