flask - Flask Google Login with Google Drive access
问题描述
I'm trying to create a Flask app that can browse to the user's Google Drive to select a file to convert to CSV format.
I found online a code to add Google Login (see below), but I don't understand how to print all files from the logged user. I figured out I need to add https://www.googleapis.com/auth/drive.file
to the scope
but I can't understand how to list all files from an authorized user (current_user.paying==True
) in index
. I know that this could be done with the following code, but I'm not sure how to defined the credentials
.
In callback
an access token is already generated, so I tried to access the Google Drive API to just search for the user's Google sheets:
from httplib2 import Http
from apiclient import discovery
from oauth2client import file
with open("./credentials.json", 'w') as outfile:
json.dump(token_response.json(), outfile)
store = file.Storage("./credentials.json")
credentials = store.get()
drive = discovery.build("drive", "v3", http=credentials.authorize(Http()))
files = drive.files().list(q="mimeType='application/vnd.google-apps.spreadsheet'").execute()
But this gives me an "KeyError: '_module'" error that's not very informative.
This is the entire Flask code with Google Login
import os
import requests
import json
from httplib2 import Http
from apiclient import discovery
from oauthlib.oauth2 import WebApplicationClient
from flask import Flask, redirect, request, url_for
from flask_login import LoginManager, current_user, login_required, login_user, logout_user
from flask_login import UserMixin
authorized_users = ["myuser@gmail.com"]
class User(UserMixin):
def __init__(self, id_, paying):
self.id = id_
self.paying = paying
@staticmethod
def get(user_email):
if user_email not in authorized_users:
user = User(user_email, False)
else:
user = User(user_email, True)
return user
# Configuration
GOOGLE_CLIENT_ID = os.environ.get("GOOGLE_CLIENT_ID", None)
GOOGLE_CLIENT_SECRET = os.environ.get("GOOGLE_CLIENT_SECRET", None)
GOOGLE_DISCOVERY_URL = ("https://accounts.google.com/.well-known/openid-configuration")
# Flask app setup
app = Flask(__name__)
app.secret_key = os.environ.get("SECRET_KEY") or os.urandom(24)
# User session management setup
login_manager = LoginManager()
login_manager.init_app(app)
# OAuth 2 client setup
client = WebApplicationClient(GOOGLE_CLIENT_ID)
# Flask-Login helper to retrieve a user from our db
@login_manager.user_loader
def load_user(user_email):
return User.get(user_email)
def get_google_provider_cfg():
return requests.get(GOOGLE_DISCOVERY_URL).json()
google_provider_cfg = get_google_provider_cfg()
token_endpoint = google_provider_cfg["token_endpoint"]
print("Google provider cfg", google_provider_cfg)
ACCESS_TOKEN_URI = 'https://www.googleapis.com/oauth2/v4/token'
@app.route("/")
def index():
if current_user.is_authenticated:
if current_user.paying:
return f"<p>Hello, {current_user.id}! You're logged in!</p><br/><a class='button' href='/logout'>Logout</a>"
else:
return "Not authorized!<br/><a class='button' href='/logout'>Logout</a>"
else:
return f"<a class='button' href='/login'>Google Login</a>"
@app.route("/login")
def login():
# Find out what URL to hit for Google login
google_provider_cfg = get_google_provider_cfg()
authorization_endpoint = google_provider_cfg["authorization_endpoint"]
# Use library to construct the request for Google login and provide
# scopes that let you retrieve user's profile from Google
request_uri = client.prepare_request_uri(
authorization_endpoint,
redirect_uri=request.base_url + "/callback",
scope=["openid", "email", "profile", "https://www.googleapis.com/auth/drive.file"],
)
return redirect(request_uri)
@app.route("/login/callback")
def callback():
# Get authorization code Google sent back to you
code = request.args.get("code")
# Prepare and send a request to get tokens! Yay tokens!
token_url, headers, body = client.prepare_token_request(
token_endpoint,
authorization_response=request.url,
redirect_url=request.base_url,
code=code
)
token_response = requests.post(
token_url,
headers=headers,
data=body,
auth=(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET),
)
# Parse the tokens!
client.parse_request_body_response(json.dumps(token_response.json()))
# Now that you have tokens (yay) let's find and hit the URL
# from Google that gives you the user's profile information,
# including their Google profile image and email
userinfo_endpoint = google_provider_cfg["userinfo_endpoint"]
uri, headers, body = client.add_token(userinfo_endpoint)
userinfo_response = requests.get(uri, headers=headers, data=body)
if userinfo_response.json().get("email_verified"):
# unique_id = userinfo_response.json()["sub"]
users_email = userinfo_response.json()["email"]
picture = userinfo_response.json()["picture"]
users_name = userinfo_response.json()["given_name"]
else:
return "User email not available or not verified by Google.", 400
# Create a user in your db with the information provided
# by Google
user = User(users_email, False)
#
# # Doesn't exist? Add it to the database.
# if not User.get(unique_id):
# User.create(unique_id, users_name, users_email, picture)
print("Logging", users_name, users_email, picture)
# Begin user session by logging the user in
login_user(user)
# Send user back to homepage
return redirect(url_for("index"))
@app.route("/logout")
@login_required
def logout():
logout_user()
print("Logging out")
return redirect(url_for("index"))
if __name__ == "__main__":
app.run(ssl_context="adhoc", debug=True)
解决方案
获取凭据的一种简单方法是
- 访问Google Drive API 快速入门
- 点击
Enable Drive
- 记下
Client Id
andClient Secret
或单击DOWNLOAD CLIENT CONFIGURATION
并打开生成和下载的credentials.json
文件
或者,您可以
- 访问谷歌云控制台
- 选择
APIs & Services
-> 凭证 点击+CREATE CREDENTIALS
更新
KeyError: '_module'
是由于检索json
结构不正确的文件而导致的错误。
可能的原因:
- 使用需要client_secrets.json文件的 API 客户端库 - 这与
credentials.json
您从 Google Drive API 快速入门获得的文件不同 json
在文件中存储无效凭据- 将文件存储
json
在 py 文件之外的另一个文件夹中 - 使用无效范围
我了解您使用WebApplicationClient并需要为 Web Server 创建相应的客户端凭据。
json file
应该有类型的内容
{"web":{"client_id":"XXXX.apps.googleusercontent.com","project_id":"XXXX","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"XXX","redirect_uris":["https://script.google.com/oauthcallback"]}}
还:
范围https://www.googleapis.com/auth/drive.file不足以列出用户驱动器上的所有文件 - 它仅允许您访问使用您的应用程序创建/打开的文件。更多信息在这里。
我建议您出于调试目的将您的应用程序置于第一个大范围内。一旦您解决了凭据问题并且您的应用程序可以正常工作 - 看看您可以在不影响应用程序功能的情况下限制范围多远。