Социальная аутентификация (DRF + Djoser), "non_field_errors": ["Предоставлено неверное состояние."] после POST-запроса с состоянием и кодом

Я следую этому видеоуроку и пытаюсь реализовать социальную аутентификацию Google с помощью DRF, djoser и React.

  1. Я набираю в браузере следующее (GET-запрос):

    http://localhost:8000/auth/o/google-oauth2/?redirect_uri=http://localhost:8000
    
  2. Я получаю вот такой ответ (я не уверен, что этот url безопасен для совместного использования, но в любом случае я его немного изменил)

    {
        "authorization_url": "https://accounts.google.com/o/oauth2/auth?client_id=836198290956-fe0ilujf6e23l882oumgkufi8qm6fg3m.apps.googleusercontent.com&redirect_uri=http://localhost:8000&state=eNwMFCmEplYgbUTTP9nnrQ6MduAPxzDY&response_type=code&scope=https://www.googleapis.com/auth/userinfo.email+https://www.googleapis.com/auth/userinfo.profile+openid+openid+email+profile"
    }
    
  3. Я ввожу этот ответ в браузер, который перенаправляет меня на страницу входа в google. Затем я выбираю свою учетную запись, после чего нажимаю продолжить. Теперь я перенаправлен на localhost:8000 с таким url

    http://localhost:8000/?state=eNwMFCmEplYgbUTTP9nnrQ6MduAPxzDY&code=4%2F0AcvDMrB6f3ZQuTD563Vxriu2n0VHmLEOHnDRqC6jD5BRm068jj2tyExxfZZJDFLAtcwYLg&scope=email+profile+openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&authuser=0&hd=circle.help&prompt=consent
    
  4. Моя проблема проявляется здесь. Я беру параметры state и code из этого url, и делаю POST запрос с помощью Postman (без тела, только url, но я также установил Content-Type заголовок на application/x-www-form-urlencoded) к этому url:

localhost:8000/auth/o/google-oauth2/?state=eNwMFCmEplYgbUTTP9nnrQ6MduAPxzDY&code=4%2F0AcvDMrB6f3ZQuTD563Vxriu2n0VHmLEOHnDRqC6jD5BRm068jj2tyExxfZZJDFLAtcwYLg

Но в ответ я получаю следующее ( Вот моя проблема):

{
    "non_field_errors": [
        "Invalid state has been provided."
    ]
}

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

venv/lib/python3.11/site-packages/social_core/backends/oauth.py

class OAuthAuth(BaseAuth):
    ...
    # Other methods
    def validate_state(self):
        """Validate state value. Raises exception on error, returns state
        value if valid."""
        if not self.STATE_PARAMETER and not self.REDIRECT_STATE:
            return None
        state = self.get_session_state()
        request_state = self.get_request_state()
        if not request_state:
            raise AuthMissingParameter(self, "state")
        elif not state:
            raise AuthStateMissing(self, "state")
        elif not constant_time_compare(request_state, state):
            raise AuthStateForbidden(self)
        else:
            return state
    ...
    # Other methods

В моем случае в методе OAuthAuth.validate_state() переменная state отличается от переменной request_state, а request_state (из OAuthAuth.validate_state()) совпадает с state (из урлов)
. в обоих приведенных выше урлах, но state (из OAuthAuth.validate_state()) совершенно другой. Я не могу понять, откуда это взялось, почему

self.get_request_state()

возвращает другое состояние, чем в url? Возможно, я делаю что-то не так, и мне нужно передать какой-то cookie в Postman?

Вот список моих установленных пакетов:

asgiref==3.8.1
certifi==2024.7.4
cffi==1.16.0
charset-normalizer==3.3.2
cryptography==42.0.8
defusedxml==0.8.0rc2
Django==5.0.7
django-filter==24.2
django-templated-mail==1.1.1
djangorestframework==3.15.2
djangorestframework-simplejwt==5.3.1
djoser==2.2.3
idna==3.7
Markdown==3.6
oauthlib==3.2.2
psycopg==3.2.1
psycopg2-binary==2.9.9
pycparser==2.22
PyJWT==2.8.0
python3-openid==3.2.0
requests==2.32.3
requests-oauthlib==2.0.0
social-auth-app-django==5.4.2
social-auth-core==4.5.4
sqlparse==0.5.1
typing_extensions==4.12.2
urllib3==2.2.2

Вот мой файл settings.py

Я нашел кучу похожих вопросов, но они не полностью объясняли, что является причиной этой ошибки.

Проблема, с которой вы столкнулись, заключается в том, что состояние не сохраняется при выполнении редиректов. Состояние сохраняется в session под ключом f'_state_{self.name}_{state}'. Значение self.name не имеет значения. А вот значение state имеет. Значение state возвращается Google в качестве аргументов запроса при перенаправлении после проверки ваших учетных данных. Обычно оно хранится в oauth_token arg. Это значение сравнивается с тем, которое находится в сессии или кэше.

Так что, по сути, вы должны сохранять session при выполнении вызовов бэкенда. Если вы используете браузер, лучше всего использовать режим инкогнито. Если вы также хотите использовать postman, убедитесь, что сессия копируется в запрос перед его выполнением.

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