Реализация google login с помощью django

У меня есть django приложение, в котором я хочу развернуть google login auth. Моим фронтенд-фреймворком является flutter. После входа с помощью flutter в google, он отправляет auth_code, полученный от google, на бэкенд django. Вот как я реализую свой логин

google_auth_request = HttpRequestManager()


class GoogleAuth:
    def __init__(self, code):
        self.code = code

    def get_access_token(self, code: str, redirect_uri: str) -> str:
        data = {
            "code": code,
            "client_id": config.settings.constants.GOOGLE_AUTH_CONFIG["GOOGLE_OAUTH2_CLIENT_ID"],
            "client_secret": config.settings.constants.GOOGLE_AUTH_CONFIG["GOOGLE_OAUTH2_CLIENT_SECRET"],
            "redirect_uri": redirect_uri,
            "grant_type": "authorization_code",
        }

        response = google_auth_request.post(
            config.settings.constants.GOOGLE_AUTH_CONFIG["GOOGLE_ACCESS_TOKEN_OBTAIN_URL"], data=data
        )
        if not response.ok:
            log.error("Response.json() for get access_token")
            log.error(response.json())
            print(response.json(), flush=True)
            raise ValidationError("Could not get access token from Google.")

        access_token = response.json()["access_token"]

        return access_token

    def get_user_info(self, access_token: str) -> Dict[str, Any]:
        response = google_auth_request.get(
            config.settings.constants.GOOGLE_AUTH_CONFIG["GOOGLE_USER_INFO_URL"], params={"access_token": access_token}
        )

        if not response.ok:
            log.error(f"google auth response data: {response.data}")
            print(response.json(), flush=True)
            raise Exception("google service is unavailable")

        return response.json()

    def login(self):
        domain = config.settings.constants.GOOGLE_AUTH_CONFIG["GOOGLE_OAUTH2_API_URL"]
        redirect_uri = f"{domain}/api/auth/v1/login/google/"

        access_token = self.get_access_token(code=self.code, redirect_uri=redirect_uri)
        user_data = self.get_user_info(access_token)
        # Creates user in DB if first time login
        query = EtloUser.objects.filter(email=user_data["email"])
        if not query.exists():
            EtloUser.objects.create(
                email=user_data["email"],
                first_name=user_data.get("given_name"),
                last_name=user_data.get("family_name"),
            )

        profile_data = {
            "email": user_data["email"],
            "first_name": user_data.get("given_name"),
            "last_name": user_data.get("family_name"),
        }
        return profile_data

И это его вид

@extend_schema(request=GoogleAuthSerializer, responses={200: OutputCredentialSerializer})
class GoogleAuthView(APIView):
    throttle_scope = "auth"

    def post(self, request, *args, **kwargs):
        auth_serializer = GoogleAuthSerializer(data=request.data)
        auth_serializer.is_valid(raise_exception=True)
        validated_data = auth_serializer.validated_data

        if validated_data["error"] or not validated_data["code"]:
            params = urlencode({"error": validated_data["error"]})
            return redirect(f"{config.settings.constants.GOOGLE_AUTH_CONFIG['GOOGLE_OAUTH2_LOGIN_URL']}?{params}")
        google_auth_service = GoogleAuth(validated_data["code"])
        user_profile = google_auth_service.login()

        auth_backend = EtloGoogleAuthenticationBackend(user_profile["email"])
        user = auth_backend.authenticate_credentials()
        tokens = auth_backend.get_user_tokens_data(user)
        return Response(tokens, status=status.HTTP_200_OK)

Но когда flutter отправляет auth_code на django, выводится строка print(response.json(), flush=True): {'error': 'invalid_grant', 'error_description': 'Bad Request'}

Ваш curl запрос неправильно сформирован, вы не можете передать информацию oauth в качестве параметров запроса.

Согласно документации:

invalid_grant

Предоставленный код авторизации недействителен или имеет неправильный формат. Запросите новый код, перезапустив процесс OAuth, чтобы снова попросить пользователя дать согласие.

А вот правильный формат:

POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded

code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=ваш_клиент_id&
client_secret=ваш_клиентский_секрет&
redirect_uri=https%3A//oauth2.example.com/code&
grant_type=authorization_code

Что в переводе на curl выглядит следующим образом:

curl -X POST https://oauth2.googleapis.com/token \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -d "code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7" \
     -d "client_id=your_client_id" \
     -d "client_secret=your_client_secret" \
     -d "redirect_uri=https%3A//oauth2.example.com/code" \
     -d "grant_type=authorization_code"

Что касается вашего кода на Python, то, возможно, дело в отсутствии заголовка Content-Type, который указан в документации.

Предполагая, что HttpRequestManager соответствует requests:

[...]
headers = {
    'Content-Type': 'application/x-www-form-urlencoded'
}

response = google_auth_request.post(config.settings.constants.GOOGLE_AUTH_CONFIG["GOOGLE_ACCESS_TOKEN_OBTAIN_URL"], 
    data=data,
    headers=headers
)
Вернуться на верх