如何解决 InvalidClientError (oauthlib.oauth2)
How to resolve InvalidClientError (oauthlib.oauth2)
我目前正在尝试使用 OAuthlib 将 Google 登录功能集成到我的本地 Flask Web 应用程序中。在尝试 运行 Flask Google-登录功能的完成实现时 here at RealPython,我 运行 遇到了这个错误:
oauthlib.oauth2.rfc6749.errors.InvalidClientError: (invalid_client) Unauthorized
它指向我的 routes.py
文件中的这一行代码:
client.parse_request_body_response(json.dumps(token_response.json()))
我不知道为什么会收到此错误,我该如何解决?
以下是我的 app/__init__.py
文件和 routes.py
文件的示例:
app/__init__.py
import os
from flask import Flask
from config import Development
from flask_caching import Cache
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
from oauthlib.oauth2 import WebApplicationClient
app = Flask(__name__)
app.config.from_object(Development)
cache = Cache(app)
db = SQLAlchemy(app)
login_manager = LoginManager()
login_manager.init_app(app)
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
client = WebApplicationClient(GOOGLE_CLIENT_ID)
from app import routes, forms, login, models
app/routes.py
import os
...
GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
GOOGLE_CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET")
GOOGLE_DISCOVERY_URL = (
"https://accounts.google.com/.well-known/openid-configuration"
)
def get_google_provider_cfg():
return requests.get(GOOGLE_DISCOVERY_URL).json()
...
@app.route("/login-google")
def login_google():
google_provider_cfg = get_google_provider_cfg()
authorization_endpoint = google_provider_cfg["authorization_endpoint"]
request_uri = client.prepare_request_uri(
authorization_endpoint,
redirect_uri=flask.request.base_url + '/callback',
scope=["openid", "email", "profile"],
)
return flask.redirect(request_uri)
@app.route("/login-google/callback")
def callback():
code = flask.request.args.get("code")
google_provider_cfg = get_google_provider_cfg()
token_endpoint = google_provider_cfg["token_endpoint"]
token_url, headers, body = client.prepare_token_request(
token_endpoint, authorization_response=flask.request.url,
redirect_url=flask.request.base_url, code=code
)
token_response = requests.post(
token_url, headers=headers,
data=body, auth=(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET),
)
client.parse_request_body_response(json.dumps(token_response.json())) ## <--- Error Occurs Here!
userinfo_endpoint = google_provider_cfg["userinfo_endpoint"]
uri, headers, body = client.add_token(userinfo_endpoint)
userinfo_response = requests.get(uri, headers=headers, data=body)
flags = userinfo_response.json().get("email_verified")
...
你是说你想制作 google - 登录到你的烧瓶网站?
我有自己的代码,它运行良好
首先,init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
import dir
from flask_mail import Mail
# init SQLAlchemy so we can use it later in our models
db = SQLAlchemy()
mail = Mail()
GOOGLE_LOGIN_CLIENT_ID = "xxxxxxxx-xxxxxxxxx.apps.googleusercontent.com"
GOOGLE_LOGIN_CLIENT_SECRET = "xxxxxxxxx"
def create_app():
app = Flask(__name__)
app.debug = False
app.config['SECRET_KEY'] = 'secret-key-goes-here'
# app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////' + str(dir.dir) + '/admin.sqlite' #this is dabase, you can change it.
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
app.config['MAIL_PORT'] = 465
app.config['MAIL_USERNAME'] = 'youremail@gmail.com'
app.config['MAIL_PASSWORD'] = 'yourpass'
app.config['MAIL_USE_TLS'] = False
app.config['MAIL_USE_SSL'] = True
app.config['OAUTH_CREDENTIALS'] = {
'google': {
'id': GOOGLE_LOGIN_CLIENT_ID,
'secret': GOOGLE_LOGIN_CLIENT_SECRET
}
}
mail.init_app(app)
db.init_app(app)
with app.app_context():
db.create_all()
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
login_manager.init_app(app)
from .models import User
@login_manager.user_loader
def load_user(user_id):
# since the user_id is just the primary key of our user table, use it in the query for the user
return User.query.get(int(user_id))
# blueprint for auth routes in our app
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint)
from .serv import serv as serv_blueprint
app.register_blueprint(serv_blueprint)
# blueprint for non-auth parts of app
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
from .search import search as sear_blueprint
app.register_blueprint(sear_blueprint)
from .dash import dash as dash_blueprint
app.register_blueprint(dash_blueprint)
from .dlf import dlf as dlf_blueprint
app.register_blueprint(dlf_blueprint)
from .contact import contact as contact_blueprint
app.register_blueprint(contact_blueprint)
return app
其次,在main.py中,您可以更改为app.py,为了以后的代码简单,我认为您应该使用Blueprint
from __future__ import print_function
from flask import Blueprint, render_template, request, redirect, flash, url_for, current_app
from flask_login import login_required, current_user, login_user
from project.function import cmsHaravan as hara, cmsCalendar as cal
from .function import config as cf, cmsContacts as ct
from datetime import datetime, timedelta, date
from .models import User, Orders, Shop, Services
from . import db, mail
from flask_mail import Message
import random
import string
from werkzeug.security import generate_password_hash
import json
from requests_oauthlib import OAuth2Session
from urllib.request import urlopen
import dateutil
from oauth2client.client import OAuth2WebServerFlow
main = Blueprint('main', __name__)
class OAuthSignIn(object):
providers = None
def __init__(self, provider_name):
self.provider_name = provider_name
credentials = current_app.config['OAUTH_CREDENTIALS'][provider_name]
self.consumer_id = credentials['id']
self.consumer_secret = credentials['secret']
def authorize(self):
pass
def callback(self):
pass
def get_callback_url(self):
return url_for('main.oauth_callback', provider=self.provider_name,
_external=True)
@classmethod
def get_provider(self, provider_name):
if self.providers is None:
self.providers = {}
for provider_class in self.__subclasses__():
provider = provider_class()
self.providers[provider.provider_name] = provider
return self.providers[provider_name]
class GoogleSignIn(OAuthSignIn):
openid_url = "https://accounts.google.com/.well-known/openid-configuration"
def __init__(self):
super(GoogleSignIn, self).__init__("google")
self.openid_config = json.load(urlopen(self.openid_url))
self.session = OAuth2Session(
client_id=self.consumer_id,
redirect_uri=self.get_callback_url(),
scope='https://www.googleapis.com/auth/userinfo.profile openid https://www.googleapis.com/auth/userinfo.email '
)
def authorize(self):
auth_url, _ = self.session.authorization_url(
self.openid_config["authorization_endpoint"])
return redirect(auth_url)
def callback(self):
if "code" not in request.args:
return None, None
self.session.fetch_token(
token_url=self.openid_config["token_endpoint"],
code=request.args["code"],
client_secret=self.consumer_secret,
)
me = self.session.get(self.openid_config["userinfo_endpoint"]).json()
print(me)
return me["family_name"], me["given_name"], me["email"], me["picture"]
@main.route('/authorize/<provider>')
def oauth_authorize(provider):
# Flask-Login function
if not current_user.is_anonymous:
return redirect(url_for('main.index'))
oauth = OAuthSignIn.get_provider(provider)
return oauth.authorize()
@main.route('/callback/<provider>')
def oauth_callback(provider):
if not current_user.is_anonymous:
return redirect(url_for('main.index'))
oauth = OAuthSignIn.get_provider(provider)
flamily_name, given_name, email, avatar = oauth.callback()
print("Successfull to get email", email)
if email is None:
# I need a valid email address for my user identification
flash('Invalid')
return redirect(url_for('auth.login'))
# Look if the user already exists
user = User.query.filter_by(email=email).first()
if not user:
# Create the user. Try and use their name returned by Google,
# but if it is not set, split the email address at the @.
name = given_name
if name is None or name == "":
name = email.split('@')[0]
# We can do more work here to ensure a unique nickname, if you
# require that.
user = User(firstname=given_name, lastname=name, email=email, avatar=avatar)
db.session.add(user)
db.session.commit()
# Log in the user, by default remembering them for their next visit
# unless they log out.
user.firstname = flamily_name
user.lastname = given_name
user.avatar = avatar
db.session.commit()
login_user(user, remember=True)
return redirect(url_for('main.profile'))
三、在html
<div class="row">
<div class="col "><span class="label-input-group"><br></span>
<a href="{{ url_for('main.oauth_authorize', provider='google') }}" class="btn btn-light btn-lg btn-block border-primary text-primary">
Login with google with out password
</a>
</div>
</div>
对于我的代码,您必须在 models.py
中制作模型
from flask_login import UserMixin
from . import db
from datetime import datetime, timedelta, date
class User(UserMixin, db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True) # primary keys are required by SQLAlchemy
firstname = db.Column(db.String(30))
lastname = db.Column(db.String(30))
email = db.Column(db.String(100), unique=True)
phone = db.Column(db.String(10))
password = db.Column(db.String(20))
要使用模型,我使用 Flask-Login
我目前正在尝试使用 OAuthlib 将 Google 登录功能集成到我的本地 Flask Web 应用程序中。在尝试 运行 Flask Google-登录功能的完成实现时 here at RealPython,我 运行 遇到了这个错误:
oauthlib.oauth2.rfc6749.errors.InvalidClientError: (invalid_client) Unauthorized
它指向我的 routes.py
文件中的这一行代码:
client.parse_request_body_response(json.dumps(token_response.json()))
我不知道为什么会收到此错误,我该如何解决?
以下是我的 app/__init__.py
文件和 routes.py
文件的示例:
app/__init__.py
import os
from flask import Flask
from config import Development
from flask_caching import Cache
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
from oauthlib.oauth2 import WebApplicationClient
app = Flask(__name__)
app.config.from_object(Development)
cache = Cache(app)
db = SQLAlchemy(app)
login_manager = LoginManager()
login_manager.init_app(app)
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
client = WebApplicationClient(GOOGLE_CLIENT_ID)
from app import routes, forms, login, models
app/routes.py
import os
...
GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
GOOGLE_CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET")
GOOGLE_DISCOVERY_URL = (
"https://accounts.google.com/.well-known/openid-configuration"
)
def get_google_provider_cfg():
return requests.get(GOOGLE_DISCOVERY_URL).json()
...
@app.route("/login-google")
def login_google():
google_provider_cfg = get_google_provider_cfg()
authorization_endpoint = google_provider_cfg["authorization_endpoint"]
request_uri = client.prepare_request_uri(
authorization_endpoint,
redirect_uri=flask.request.base_url + '/callback',
scope=["openid", "email", "profile"],
)
return flask.redirect(request_uri)
@app.route("/login-google/callback")
def callback():
code = flask.request.args.get("code")
google_provider_cfg = get_google_provider_cfg()
token_endpoint = google_provider_cfg["token_endpoint"]
token_url, headers, body = client.prepare_token_request(
token_endpoint, authorization_response=flask.request.url,
redirect_url=flask.request.base_url, code=code
)
token_response = requests.post(
token_url, headers=headers,
data=body, auth=(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET),
)
client.parse_request_body_response(json.dumps(token_response.json())) ## <--- Error Occurs Here!
userinfo_endpoint = google_provider_cfg["userinfo_endpoint"]
uri, headers, body = client.add_token(userinfo_endpoint)
userinfo_response = requests.get(uri, headers=headers, data=body)
flags = userinfo_response.json().get("email_verified")
...
你是说你想制作 google - 登录到你的烧瓶网站? 我有自己的代码,它运行良好 首先,init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
import dir
from flask_mail import Mail
# init SQLAlchemy so we can use it later in our models
db = SQLAlchemy()
mail = Mail()
GOOGLE_LOGIN_CLIENT_ID = "xxxxxxxx-xxxxxxxxx.apps.googleusercontent.com"
GOOGLE_LOGIN_CLIENT_SECRET = "xxxxxxxxx"
def create_app():
app = Flask(__name__)
app.debug = False
app.config['SECRET_KEY'] = 'secret-key-goes-here'
# app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////' + str(dir.dir) + '/admin.sqlite' #this is dabase, you can change it.
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
app.config['MAIL_PORT'] = 465
app.config['MAIL_USERNAME'] = 'youremail@gmail.com'
app.config['MAIL_PASSWORD'] = 'yourpass'
app.config['MAIL_USE_TLS'] = False
app.config['MAIL_USE_SSL'] = True
app.config['OAUTH_CREDENTIALS'] = {
'google': {
'id': GOOGLE_LOGIN_CLIENT_ID,
'secret': GOOGLE_LOGIN_CLIENT_SECRET
}
}
mail.init_app(app)
db.init_app(app)
with app.app_context():
db.create_all()
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
login_manager.init_app(app)
from .models import User
@login_manager.user_loader
def load_user(user_id):
# since the user_id is just the primary key of our user table, use it in the query for the user
return User.query.get(int(user_id))
# blueprint for auth routes in our app
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint)
from .serv import serv as serv_blueprint
app.register_blueprint(serv_blueprint)
# blueprint for non-auth parts of app
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
from .search import search as sear_blueprint
app.register_blueprint(sear_blueprint)
from .dash import dash as dash_blueprint
app.register_blueprint(dash_blueprint)
from .dlf import dlf as dlf_blueprint
app.register_blueprint(dlf_blueprint)
from .contact import contact as contact_blueprint
app.register_blueprint(contact_blueprint)
return app
其次,在main.py中,您可以更改为app.py,为了以后的代码简单,我认为您应该使用Blueprint
from __future__ import print_function
from flask import Blueprint, render_template, request, redirect, flash, url_for, current_app
from flask_login import login_required, current_user, login_user
from project.function import cmsHaravan as hara, cmsCalendar as cal
from .function import config as cf, cmsContacts as ct
from datetime import datetime, timedelta, date
from .models import User, Orders, Shop, Services
from . import db, mail
from flask_mail import Message
import random
import string
from werkzeug.security import generate_password_hash
import json
from requests_oauthlib import OAuth2Session
from urllib.request import urlopen
import dateutil
from oauth2client.client import OAuth2WebServerFlow
main = Blueprint('main', __name__)
class OAuthSignIn(object):
providers = None
def __init__(self, provider_name):
self.provider_name = provider_name
credentials = current_app.config['OAUTH_CREDENTIALS'][provider_name]
self.consumer_id = credentials['id']
self.consumer_secret = credentials['secret']
def authorize(self):
pass
def callback(self):
pass
def get_callback_url(self):
return url_for('main.oauth_callback', provider=self.provider_name,
_external=True)
@classmethod
def get_provider(self, provider_name):
if self.providers is None:
self.providers = {}
for provider_class in self.__subclasses__():
provider = provider_class()
self.providers[provider.provider_name] = provider
return self.providers[provider_name]
class GoogleSignIn(OAuthSignIn):
openid_url = "https://accounts.google.com/.well-known/openid-configuration"
def __init__(self):
super(GoogleSignIn, self).__init__("google")
self.openid_config = json.load(urlopen(self.openid_url))
self.session = OAuth2Session(
client_id=self.consumer_id,
redirect_uri=self.get_callback_url(),
scope='https://www.googleapis.com/auth/userinfo.profile openid https://www.googleapis.com/auth/userinfo.email '
)
def authorize(self):
auth_url, _ = self.session.authorization_url(
self.openid_config["authorization_endpoint"])
return redirect(auth_url)
def callback(self):
if "code" not in request.args:
return None, None
self.session.fetch_token(
token_url=self.openid_config["token_endpoint"],
code=request.args["code"],
client_secret=self.consumer_secret,
)
me = self.session.get(self.openid_config["userinfo_endpoint"]).json()
print(me)
return me["family_name"], me["given_name"], me["email"], me["picture"]
@main.route('/authorize/<provider>')
def oauth_authorize(provider):
# Flask-Login function
if not current_user.is_anonymous:
return redirect(url_for('main.index'))
oauth = OAuthSignIn.get_provider(provider)
return oauth.authorize()
@main.route('/callback/<provider>')
def oauth_callback(provider):
if not current_user.is_anonymous:
return redirect(url_for('main.index'))
oauth = OAuthSignIn.get_provider(provider)
flamily_name, given_name, email, avatar = oauth.callback()
print("Successfull to get email", email)
if email is None:
# I need a valid email address for my user identification
flash('Invalid')
return redirect(url_for('auth.login'))
# Look if the user already exists
user = User.query.filter_by(email=email).first()
if not user:
# Create the user. Try and use their name returned by Google,
# but if it is not set, split the email address at the @.
name = given_name
if name is None or name == "":
name = email.split('@')[0]
# We can do more work here to ensure a unique nickname, if you
# require that.
user = User(firstname=given_name, lastname=name, email=email, avatar=avatar)
db.session.add(user)
db.session.commit()
# Log in the user, by default remembering them for their next visit
# unless they log out.
user.firstname = flamily_name
user.lastname = given_name
user.avatar = avatar
db.session.commit()
login_user(user, remember=True)
return redirect(url_for('main.profile'))
三、在html
<div class="row">
<div class="col "><span class="label-input-group"><br></span>
<a href="{{ url_for('main.oauth_authorize', provider='google') }}" class="btn btn-light btn-lg btn-block border-primary text-primary">
Login with google with out password
</a>
</div>
</div>
对于我的代码,您必须在 models.py
中制作模型from flask_login import UserMixin
from . import db
from datetime import datetime, timedelta, date
class User(UserMixin, db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True) # primary keys are required by SQLAlchemy
firstname = db.Column(db.String(30))
lastname = db.Column(db.String(30))
email = db.Column(db.String(100), unique=True)
phone = db.Column(db.String(10))
password = db.Column(db.String(20))
要使用模型,我使用 Flask-Login