Социальная аутентификация (DRF + Djoser), "non_field_errors": ["Предоставлено неверное состояние."] после POST-запроса с состоянием и кодом
Я следую этому видеоуроку и пытаюсь реализовать социальную аутентификацию Google с помощью DRF, djoser и React.
Я набираю в браузере следующее (GET-запрос):
http://localhost:8000/auth/o/google-oauth2/?redirect_uri=http://localhost:8000Я получаю вот такой ответ (я не уверен, что этот 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" }Я ввожу этот ответ в браузер, который перенаправляет меня на страницу входа в 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Моя проблема проявляется здесь. Я беру параметры 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, убедитесь, что сессия копируется в запрос перед его выполнением.