Django集成Google登录遇invalid_grant错误求助(Flutter前端)
Django + Flutter Google登录集成:invalid_grant 错误排查
问题背景
我基于Django开发后端、Flutter开发前端,在集成Google登录授权时遇到问题:Flutter端获取auth_code并发送给Django后,后端调用Google令牌接口返回错误:{'error': 'invalid_grant', 'error_description': 'Bad Request'}。即使改用URL参数传递请求参数(如下方curl示例),Postman测试仍返回相同错误。
后端核心代码
GoogleAuth服务类
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) # 首次登录时创建用户 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)
测试用curl请求
curl --location --request POST 'https://oauth2.googleapis.com/token?client_id=******-c4hrkmu0sfc1t3se6ni5g44aacg81iia.apps.googleusercontent.com&client_secret=******-0lkcEi489FxaLzUwRGyF5MmAgE3T&code=******InQyhphLF2Cjjx75fCiiTCAVRtiZ3fNrUdwNXpwTSH0_-Vbrzl4VrKSQ&redirect_uri=https%3A%2F%2Fetlo-firebase.firebaseapp.com%2F__%2Fauth%2Fhandler&grant_type=authorization_code'
返回错误
{ "error": "invalid_grant", "error_description": "Bad Request" }
排查要点
- redirect_uri 完全匹配:
确保Django中使用的redirect_uri({domain}/api/auth/v1/login/google/)与Flutter请求Google授权时的redirect_uri完全一致,包括大小写、结尾斜杠、协议(http/https)。Google OAuth对该参数的匹配要求严格,任何细微差异都会触发invalid_grant。 - auth_code 有效性检查:
auth_code是一次性凭证,只能使用一次,重复调用会直接失效;同时code有效期仅10分钟,超时也会报错。检查Flutter是否重复发送同一code,或后端是否复用了已使用过的code。 - 客户端凭证正确性:
确认Django配置的GOOGLE_OAUTH2_CLIENT_ID和GOOGLE_OAUTH2_CLIENT_SECRET与Google Cloud Console中创建的OAuth 2.0客户端凭证完全一致,注意区分Web/Android/iOS类型的客户端ID,避免混用。 - 请求内容类型验证:
Google令牌接口要求请求体为application/x-www-form-urlencoded格式,检查HttpRequestManager的post方法是否正确设置了Content-Type头。若默认使用application/json,会导致参数解析失败,触发错误。 - Google Cloud配置检查:
登录Google Cloud Console,确认对应OAuth客户端ID的「授权重定向URI」列表中,已添加Django使用的redirect_uri和测试用的https://etlo-firebase.firebaseapp.com/__/auth/handler,未添加的URI会被拒绝。 - IP/地区限制排查:
检查Google Cloud中该OAuth客户端是否设置了IP或地区限制,若后端服务器IP不在允许列表内,会导致请求被拦截。
内容的提问来源于stack exchange,提问作者Emad Helmi
相关产品推荐
相关产品推荐

