Flask程序部署至Google Cloud Shell时遭遇Google OAuth访问被阻止问题
本地运行基于Flask的Google OAuth认证程序时,能正常弹出浏览器完成Google账号认证;但部署到Google Cloud Shell后,点击登录链接出现「Access blocked: This app’s request is invalid」错误,无法完成认证并展示用户信息。
程序代码(app.py)
# copied from https://github.com/piyush-singhal/oauth-python/blob/main/app.py # this seems the simplest way to authenicate with Google # developed on local box and works well. It pops a browser window open and lets me pick my Google account on my browser # # deploy to Google Shell in the GCP # I get the expected screen # Example of Google Login in Python Flask Application # You are not logged in, Click on the below link to sign in with google. # I click the link and then i get the dreaded # "Access blocked: This app’s request is invalid" # Any help you can offer would be appreciated. # # here is my debug log (with my client-id channged to protect me) # start the logging.debug # Hello line 74 # Hello line 76 # * Debugger is active! # * Debugger PIN: 127-742-122 # Hello line 78 # Hello line 80 # Hello line 81 Google.base_url == https://www.googleapis.com/ # Hello line 82 user_info_endpoint == /oauth2/v2/userinfo # 127.0.0.1 - - [15/Jul/2024 11:41:41] "GET /?authuser=0 HTTP/1.1" 200 - # Hello line 89 # Hello line 91 my_url_for-->/login/google # 127.0.0.1 - - [15/Jul/2024 11:42:30] "GET /login HTTP/1.1" 302 - # client_id = 0000000000000-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com # Generated new state V18oHjswxgqJML9VgmF7ujpaiLi6KD. # state = V18oHjswxgqJML9VgmF7ujpaiLi6KD # redirect URL = https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=0000000000000-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com&redirect_uri=http%3A%2F%2F127.0.0.1%3A5000%2Flogin%2Fgoogle%2Fauthorized&scope=profile+email&state=V18oHjswxgqJML9VgmF7ujpaiLi6KD&prompt=consent import os import sys from flask import Flask, render_template, redirect, url_for from flask_dance.contrib.google import make_google_blueprint, google import logging import json import json as json_module app = Flask(__name__) # Configuration with open('./credentials.json', 'r') as file: json = json.load(file) client_id = json['installed']['client_id'] client_secret = json['installed']['client_secret'] secret_key = os.environ.get("SECRET_KEY") or os.urandom(24) logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, format='%(message)s') print("start the logging.debug") logging.debug("Hello line 52") app.config['DEBUG'] = True app.config['SECRET_KEY'] = secret_key logging.debug("Hello line 57") os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] = '1' blueprint = make_google_blueprint( client_id=client_id, client_secret=client_secret, reprompt_consent=True, scope=["profile", "email"] ) app.register_blueprint(blueprint, url_prefix="/login") @app.route("/") def index(): google_data = None user_info_endpoint = '/oauth2/v2/userinfo' logging.debug("Hello line 74") if google.authorized: logging.debug("Hello line 76") google_data = google.get(user_info_endpoint).json() logging.debug("Hello line 78") logging.debug("Hello line 80") logging.debug("Hello line 81 Google.base_url == " + google.base_url) logging.debug("Hello line 82 user_info_endpoint == " + user_info_endpoint) return render_template('index.j2', google_data=google_data, fetch_url=google.base_url + user_info_endpoint) @app.route('/login') def login(): logging.debug("Hello line 89") my_url_for=url_for('google.login') logging.debug("Hello line 91 my_url_for-->" + my_url_for ) return redirect(my_url_for) #return redirect('/login/google') #this works if __name__ == "__main__": app.run()
模板文件(index.j2)
<!doctype html> <html class="no-js" lang=""> <head> <title>Google Oauth 2.0 Login for python Flask application</title> <link rel="icon" href="/favicon.ico" type="image/x-icon"> </head> <body> <h3>Example of Google Login in Python Flask Application</h3> {% if not google_data %} <p style="color:#333;">You are not logged in, Click on the below link to sign in with google.</p> <a href="/login" class="google-btn"> <div class="google-icon-wrapper"> <img class="google-icon" src="https://upload.wikimedia.org/wikipedia/commons/c/c1/Google_%22G%22_logo.svg"/> </div> <p class="btn-text"><b>Sign in with google</b></p> </a> {% else %} <p>Hi {{ google_data.name }}, [<strong>{{ google_data.email }}</strong>]. You have logged in successfully from your Google Account. Check your below details.</p> {% if google_data is not none %} <div class="detail"> <p style="font-size: 15px; padding-bottom: 10px;">User info fetched from <strong>{{ fetch_url }}</strong></p> <table border='1'> {% for key, value in google_data.items() %} <tr><td>{{ key }}</td> <td><strong>{{ value }}</strong></td></tr> {% endfor %} </table> </div> <div> <img src="{{ google_data.picture }}" alt="User Picture"> </div> {% endif %} {% endif %} </html>
调试日志
start the logging.debug Hello line 74 Hello line 76 * Debugger is active! * Debugger PIN: 127-742-122 Hello line 78 Hello line 80 Hello line 81 Google.base_url == https://www.googleapis.com/ Hello line 82 user_info_endpoint == /oauth2/v2/userinfo 127.0.0.1 - - [15/Jul/2024 11:41:41] "GET /?authuser=0 HTTP/1.1" 200 - Hello line 89 Hello line 91 my_url_for-->/login/google 127.0.0.1 - - [15/Jul/2024 11:42:30] "GET /login HTTP/1.1" 302 - client_id = 0000000000000-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com Generated new state V18oHjswxgqJML9VgmF7ujpaiLi6KD. state = V18oHjswxgqJML9VgmF7ujpaiLi6KD redirect URL = https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=0000000000000-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com&redirect_uri=http%3A%2F%2F127.0.0.1%3A5000%2Flogin%2Fgoogle%2Fauthorized&scope=profile+email&state=V18oHjswxgqJML9VgmF7ujpaiLi6KD&prompt=consent
问题原因及解决方案
核心问题分析
从调试日志里的redirect_uri=http%3A%2F%2F127.0.0.1%3A5000%2Flogin%2Fgoogle%2Fauthorized可以看出,程序在Cloud Shell中仍使用本地回调地址,但Cloud Shell提供的是公网可访问的URL(类似https://xxxx-xxxx-xxxx-xxxx.cloudshell.dev),Google OAuth不允许未在凭据中配置的回调地址,因此触发错误。另外,当前使用的是installed类型的OAuth凭据,这类凭据仅适用于本地桌面应用,部署到Web环境需要使用Web应用类型的凭据。
解决方案步骤
创建Web类型的OAuth凭据
- 进入Google Cloud控制台的API和服务→凭据页面
- 删除原有的「桌面应用」类型凭据,新建「Web应用」类型凭据
- 在「已获授权的重定向URI」中添加Cloud Shell的公网回调地址,格式为
https://[你的Cloud Shell公网域名]/login/google/authorized - 下载新的
credentials.json,替换Cloud Shell中的文件
修改Flask应用配置,适配Cloud Shell的公网地址
Cloud Shell运行Flask时,需要绑定0.0.0.0并指定端口,同时设置正确的回调地址。修改make_google_blueprint的配置,手动指定redirect_uri:# 获取Cloud Shell的公网域名,Cloud Shell会自动设置该环境变量 import os cloud_shell_domain = os.environ.get("CLOUDSHELL_HOST") redirect_uri = f"https://{cloud_shell_domain}/login/google/authorized" blueprint = make_google_blueprint( client_id=client_id, client_secret=client_secret, reprompt_consent=True, scope=["profile", "email"], redirect_uri=redirect_uri )同时修改启动命令,让Flask监听所有地址:
if __name__ == "__main__": app.run(host='0.0.0.0', port=5000)确保环境变量配置正确
保留OAUTHLIB_INSECURE_TRANSPORT=1(Cloud Shell的公网地址是HTTPS,可按需去掉),OAUTHLIB_RELAX_TOKEN_SCOPE=1可保留。
验证步骤
- 在Cloud Shell中启动应用,访问提供的公网URL
- 点击登录链接,此时回调地址已配置为Cloud Shell的公网地址,Google OAuth会正常跳转并完成认证
内容的提问来源于stack exchange,提问作者user23991531

