Django Admin创建Gmail草稿遇Google OAuth重定向不匹配错误
Django Admin Gmail草稿创建:OAuth redirect_uri_mismatch问题
问题描述
尝试实现Django Admin动作,为选中的联系人在Gmail账户中创建邮件草稿,但在Google OAuth流程中触发redirect_uri_mismatch错误,报错发生在flow.fetch_token(code=code)代码行。
相关代码
admin.py 代码
... DEBUG = os.getenv('DEBUG', 'False') == 'True' if DEBUG: os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' SCOPES = ['https://www.googleapis.com/auth/gmail.compose'] def email_contacts(modeladmin, request, queryset): flow = Flow.from_client_secrets_file( 'contacts/client_secret.json', scopes=SCOPES) flow.redirect_uri = "http://localhost:8000/callback" authorization_url, state = flow.authorization_url( access_type='offline', include_granted_scopes='true') return HttpResponseRedirect(authorization_url) def auth_callback(request): code = request.GET.get('code') flow = Flow.from_client_secrets_file( 'contacts/client_secret.json', scopes=SCOPES) flow.redirect_uri = "http://localhost:8000" flow.fetch_token(code=code) creds = flow.credentials send_email(creds) def send_email(creds): message_body = "Test content" message = MIMEMultipart() message['to'] = 'test.address@example.com' message.attach(MIMEText(message_body, "plain")) try: service = build('gmail', 'v1', credentials=creds) message = {'message': {'raw': base64.urlsafe_b64encode(message.as_bytes()).decode()}} service.users().drafts().create(userId='me', body=message).execute() except HttpError as err: print(err) ... class ContactAdmin(admin.ModelAdmin): actions = [emails_contacts]
注:目前仅测试创建邮件,暂未使用查询集数据填充内容
urls.py 代码
... from contacts.admin import auth_callback urlpatterns = [ path('callback/', auth_callback, name='oauth_callback'), path('admin/', admin.site.urls), ...
client_secret.json 配置
{"web":{"client_id":"....apps.googleusercontent.com","project_id":"...","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","...":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"...","redirect_uris":["http://localhost:8000/callback","http://localhost:8000/callback/","http://localhost/callback","http://localhost/callback/","http://localhost:8000/","http://localhost:8000","http://localhost","http://localhost/"]}}
注:已配置多个redirect_uri确保兼容性
错误信息
在/callback/路径触发CustomOAuth2Error,错误类型为redirect_uri_mismatch(Bad Request),报错位置为flow.fetch_token(code=code)行。
解决方案
1. 统一OAuth流程中的redirect_uri
OAuth授权请求和令牌获取阶段的redirect_uri必须完全一致。当前email_contacts函数中设置的是http://localhost:8000/callback,但auth_callback中错误设置为http://localhost:8000,这是核心问题。修改auth_callback:
def auth_callback(request): code = request.GET.get('code') flow = Flow.from_client_secrets_file( 'contacts/client_secret.json', scopes=SCOPES) flow.redirect_uri = "http://localhost:8000/callback" # 和授权阶段保持一致 flow.fetch_token(code=code) creds = flow.credentials send_email(creds) # 重定向回Admin页面,给用户反馈 return HttpResponseRedirect('/admin/contacts/contact/')
2. 修正函数名拼写错误
Admin动作中注册的emails_contacts与实际定义的函数名email_contacts不符,会导致动作无法加载。修改ContactAdmin:
class ContactAdmin(admin.ModelAdmin): actions = [email_contacts] # 修正函数名
3. 验证Google Cloud配置的URI一致性
确认Google Cloud Console中OAuth 2.0客户端ID的已授权重定向URI列表里,http://localhost:8000/callback(注意结尾斜杠是否一致)已正确添加,避免因细微格式差异导致不匹配。
4. 补充state参数验证(可选但推荐)
添加state参数验证可防止CSRF攻击,提升安全性:
def email_contacts(modeladmin, request, queryset): flow = Flow.from_client_secrets_file( 'contacts/client_secret.json', scopes=SCOPES) flow.redirect_uri = "http://localhost:8000/callback" authorization_url, state = flow.authorization_url( access_type='offline', include_granted_scopes='true') request.session['oauth_state'] = state # 将state存入session return HttpResponseRedirect(authorization_url) def auth_callback(request): code = request.GET.get('code') state = request.GET.get('state') # 验证state参数一致性 if state != request.session.get('oauth_state'): return HttpResponse("无效的state参数", status=400) flow = Flow.from_client_secrets_file( 'contacts/client_secret.json', scopes=SCOPES, state=state) flow.redirect_uri = "http://localhost:8000/callback" flow.fetch_token(code=code) creds = flow.credentials send_email(creds) return HttpResponseRedirect('/admin/contacts/contact/')
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

