Invalid_client при попытке аутентификации с client_id и client_secret с помощью django oauth toolkit и rest framework

Я запускаю Django-сервис, который выставляет конечные точки через Django REST Framework. Я хочу защитить эти конечные точки, используя Django OAuth Toolkit для аутентификации.

Когда я создаю приложение из панели администратора, я использую следующие настройки:

enter image description here

Как показано на скриншоте, я отключил хеширование client_secret. При такой конфигурации все работает отлично, и я могу получить токен доступа без каких-либо проблем.

enter image description here

* Preparing request to http://localhost:8000/o/token/
* Current time is 2024-12-19T10:47:03.573Z
* Enable automatic URL encoding
* Using default HTTP version
* Enable timeout of 30000ms
* Enable SSL validation
* Found bundle for host localhost: 0x159f21ed0 [serially]
* Can not multiplex, even if we wanted to!
* Re-using existing connection! (#106) with host localhost
* Connected to localhost (127.0.0.1) port 8000 (#106)

> POST /o/token/ HTTP/1.1
> Host: localhost:8000
> User-Agent: insomnia/0.2.2
> Content-Type: application/x-www-form-urlencoded
> Accept: application/x-www-form-urlencoded, application/json
> Authorization: Basic MkVDdzAwWkN1cm1CRFc4TEFrSmpjSGt1bnh1OW9WZlRpY09DaGU5bDpKVTl6SURzd0Zob09JMzJhRjhUMVI2WnhYZDVVTU84TWwwbHdiZldNWFNxcHVuTGdsaXBLT2xLTFBNMTBublV1TGp3WGFWOVBPR2ZxYUpURzF5Smx2VGRMWHVPRzN0SVg4bE9tQ1N6U09lbTV4Z2ExaWZrNWRUdjVOYWdFV2djQQ==
> Content-Length: 29

| grant_type=client_credentials

* Mark bundle as not supporting multiuse

< HTTP/1.1 200 OK
< Date: Thu, 19 Dec 2024 10:47:03 GMT
< Server: WSGIServer/0.2 CPython/3.12.8
< Content-Type: application/json
< Cache-Control: no-store
< Pragma: no-cache
< djdt-store-id: b377d48fbdae4db989aabb760af12619
< Server-Timing: TimerPanel_utime;dur=28.13699999999919;desc="User CPU time", TimerPanel_stime;dur=2.7200000000000557;desc="System CPU time", TimerPanel_total;dur=30.856999999999246;desc="Total CPU time", TimerPanel_total_time;dur=56.63733399705961;desc="Elapsed time", SQLPanel_sql_time;dur=5.738208987168036;desc="SQL 3 queries", CachePanel_total_time;dur=0;desc="Cache 0 Calls"
< Vary: Accept-Language, Cookie
< Content-Language: en
< X-Frame-Options: DENY
< Content-Length: 118
< X-Content-Type-Options: nosniff
< Referrer-Policy: same-origin
< Cross-Origin-Opener-Policy: same-origin


* Received 118 B chunk
* Connection #106 to host localhost left intact

| {"access_token": "C5RbvIhZIp3y5zLnC7xrezfuzTGNwe", "expires_in": 36000, "token_type": "Bearer", "scope": "read write"}

Однако, когда я включаю хеширование client_secret, я получаю ошибку: {«error»: «invalid_client"}.

enter image description here

enter image description here

* Preparing request to http://localhost:8000/o/token/
* Current time is 2024-12-19T10:50:41.587Z
* Enable automatic URL encoding
* Using default HTTP version
* Enable timeout of 30000ms
* Enable SSL validation
* Too old connection (217 seconds), disconnect it
* Connection 106 seems to be dead!
* Closing connection 106
*   Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#107)

> POST /o/token/ HTTP/1.1
> Host: localhost:8000
> User-Agent: insomnia/0.2.2
> Content-Type: application/x-www-form-urlencoded
> Accept: application/x-www-form-urlencoded, application/json
> Authorization: Basic eHp0cktWNmh1bDJjdFJNWWMxNW82ZHhWRk4wZHJTNzkyMjNldTQ3VTp6cUJwbW1zVHQ4dU1PaEk4Y29FZ3U3eW9rUUNRYnpoVE1rN2Y3bkVYclFlYnl5cEdYU1JpOWNFSGlIUXk1UnJ4blpvNTFOWVBldWhoTERkeWF3VkNrYkJGN05KVzNKZjRua1Z4OEI0a3p1V2tHRU9XdHNOOUo0NWFQRTIybHkzNw==
> Content-Length: 29

| grant_type=client_credentials

* Mark bundle as not supporting multiuse

< HTTP/1.1 401 Unauthorized
< Date: Thu, 19 Dec 2024 10:50:41 GMT
< Server: WSGIServer/0.2 CPython/3.12.8
< Content-Type: application/json
< Cache-Control: no-store
< Pragma: no-cache
< WWW-Authenticate: Bearer error="invalid_client"
< djdt-store-id: fc37d8dc93364fa1ab78cd392ddcfe20
< Server-Timing: TimerPanel_utime;dur=20.312999999998027;desc="User CPU time", TimerPanel_stime;dur=3.6159999999991754;desc="System CPU time", TimerPanel_total;dur=23.928999999997203;desc="Total CPU time", TimerPanel_total_time;dur=44.46612499305047;desc="Elapsed time", SQLPanel_sql_time;dur=2.3827080003684387;desc="SQL 2 queries", CachePanel_total_time;dur=0;desc="Cache 0 Calls"
< Vary: Accept-Language, Cookie
< Content-Language: en
< X-Frame-Options: DENY
< Content-Length: 27
< X-Content-Type-Options: nosniff
< Referrer-Policy: same-origin
< Cross-Origin-Opener-Policy: same-origin


* Received 27 B chunk
* Connection #107 to host localhost left intact

| {"error": "invalid_client"}

Вот мой пользовательский класс аутентификации, который я скопировал из https://stackoverflow.com/a/65718714/15136864:

from oauth2_provider.contrib.rest_framework import OAuth2Authentication
from oauth2_provider.models import Application


class OAuth2ClientCredentialAuthentication(OAuth2Authentication):

    """OAuth2Authentication doesn't allows credentials to belong to an application (client).
    This override authenticates server-to-server requests, using client_credential authentication.
    """

    def authenticate(self, request):
        authentication = super().authenticate(request)

        if authentication is not None:
            _, access_token = authentication
            if self._grant_type_is_client_credentials(access_token):
                authentication = access_token.application.user, access_token

        return authentication

    def _grant_type_is_client_credentials(self, access_token):
        return access_token.application.authorization_grant_type == Application.GRANT_CLIENT_CREDENTIALS

Я использовал этот класс, потому что, как упоминалось в упомянутом посте, вход в систему с использованием учетных данных клиента не поддерживается из коробки, что не указано в документации.

Вот моя конфигурация

REST_FRAMEWORK = {
    "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
    "DEFAULT_AUTHENTICATION_CLASSES": ("authentication.rest_framework.OAuth2ClientCredentialAuthentication",),
    "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
}

OAUTH2_PROVIDER_APPLICATION_MODEL = "authentication.Application"

OAUTH_2_PROVIDER = {
    "SCOPES": {"read": "Read scope", "write": "Write scope", "groups": "Access to your groups"},
}

Редактирование: Я обнаружил, что наше приложение использует собственный хэшер паролей:

PASSWORD_HASHERS = [
    "authentication.password.PBKDF2SHA256PasswordHasher",
]

Вероятно, Django OAuth Toolkit использует другой хэшер, что может быть причиной проблемы.

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