Python PyQt5应用程序的替代OAuth2登录

Python PyQt5应用程序的替代OAuth2登录,python,pyqt5,google-oauth,Python,Pyqt5,Google Oauth,我有一个带有Google登录的PyQt5应用程序,它是使用实现的。使用QWebEngineView在嵌入式浏览器中显示登录页面。但是,对于,我的应用程序中需要进行更改,以打开系统浏览器,然后接收来自该浏览器的授权响应。为此,我正在使用,这也在中使用 我创建了一个小型POC,它只是实现了这个工作流,但我面临着几个问题: 登录页面将在新浏览器窗口或选项卡中打开。但这可能不是一个好的用户体验,因为用户必须在登录后关闭浏览器,然后返回应用程序。在这里打开弹出式浏览器似乎是更好的用户体验。我检查了负责打开

我有一个带有Google登录的PyQt5应用程序,它是使用实现的。使用
QWebEngineView
在嵌入式浏览器中显示登录页面。但是,对于,我的应用程序中需要进行更改,以打开系统浏览器,然后接收来自该浏览器的授权响应。为此,我正在使用,这也在中使用

我创建了一个小型POC,它只是实现了这个工作流,但我面临着几个问题:

  • 登录页面将在新浏览器窗口或选项卡中打开。但这可能不是一个好的用户体验,因为用户必须在登录后关闭浏览器,然后返回应用程序。在这里打开弹出式浏览器似乎是更好的用户体验。我检查了负责打开浏览器的
    run\u local\u server
    方法的源代码,他们似乎使用了
    webbrowser
    模块,不幸的是,该模块没有打开弹出窗口的方法

  • 如果用户在未登录的情况下关闭使用
    run\u local\u server
    方法打开的浏览器,则调用它的应用程序将冻结,需要强制退出。我也没有注意到任何控制台错误。我正在使用的库是否有办法处理这个问题

  • 以下是最低限度的工作示例:

    导入系统 从PyQt5.QtCore导入QStandardPath 从PyQt5.QtWidgets导入QWidget、QPushButton、QApplication 从google_auth_oauthlib.flow导入安装的应用程序流 谷歌登录类(QWidget): 定义初始化(自): super()。\uuuu init\uuuuu() self.flow=InstalledAppFlow.from\u client\u secrets\u文件( “credentials.json”#此文件应放在正确的文件夹中 作用域=[”https://www.googleapis.com/auth/userinfo.profile“,”openid“, "https://www.googleapis.com/auth/userinfo.email"]) self.initUI() def initUI(self): self.sign\u in\u btn=QPushButton(“登录”,self) 自签名移动(135135) 自签名设置固定大小(100,40) self.sign\u-in\u-btn.clicked.connect(self.open\u-google\u-sign\u) 自我设置固定大小(350350) self.setWindowTitle('谷歌登录测试') self.show() def打开\谷歌\登录(自我): self.flow.run_local_服务器(端口=0) session=self.flow.authorized_session() profile\u info=session.get('https://www.googleapis.com/userinfo/v2/me').json() 打印(个人资料信息) def main(): app=QApplication(sys.argv) ex=谷歌签名() sys.exit(app.exec_()) 如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu': main()
    QWebEngineView可用作浏览器进行身份验证,但必须设置有效的用户代理。另一方面,google auth oauthlib请求正在阻塞,因此它们必须在不同的线程中执行,并通过信号通知结果:

    导入工具
    导入日志记录
    导入操作系统
    进口泡菜
    导入系统
    导入线程
    从PyQt5导入QtCore、QtGui、QtWidgets、QtWebEngineWidgets
    从google_auth_oauthlib.flow导入安装的应用程序流
    从google.auth.transport.requests导入请求
    从GoogleAppClient.discovery导入生成
    作用域=[
    "https://www.googleapis.com/auth/userinfo.profile",
    “openid”,
    "https://www.googleapis.com/auth/userinfo.email",
    ]
    CURRENT_DIR=os.path.dirname(os.path.realpath(uu文件_uu))
    logging.basicConfig(级别=logging.DEBUG)
    班级回复(QtCore.QObject):
    finished=QtCore.pyqtSignal()
    def(self,func,args=(),kwargs=None,parent=None):
    super()。\uuuu init\uuuu(父级)
    self.\u结果=无
    self.\u is\u finished=False
    self._error_str=“”
    穿线,穿线(
    target=self.\u execute,args=(func,args,kwargs),daemon=True
    ).start()
    @财产
    def结果(自我):
    返回self.\u结果
    @财产
    def错误_str(自身):
    返回self.\u错误\u str
    def已完成(自):
    返回自我。完成
    def有错误(自身):
    返回布尔值(自身错误)
    def_execute(self、func、args、kwargs):
    如果kwargs为无:
    kwargs={}
    尝试:
    self._results=func(*args,**kwargs)
    例外情况除外,如e:
    自我错误,自我错误=str(e)
    self.\u已完成=真
    self.finished.emit()
    def转换为应答(func):
    def包装(*args,**kwargs):
    回复=回复(func、args、kwargs)
    回覆
    返回包装器
    类后端(QtCore.QObject):
    started=QtCore.pyqtSignal(QtCore.QUrl)
    finished=QtCore.pyqtSignal()
    def uuu init uuu(self,parent=None):
    super()。\uuuu init\uuuu(父级)
    自助服务=无
    @财产
    def服务(自助):
    如果自助服务为无:
    回复=self.\u更新\u凭证()
    loop=QtCore.QEventLoop()
    reply.finished.connect(循环.退出)
    loop.exec()
    如果没有,请答复。有错误()
    self.\u service=reply.results
    其他:
    logging.debug(reply.error\u str)
    返回自助服务
    @将_转换为_回复
    def_更新_凭证(自我):
    信誉=无
    如果os.path.exists(“token.pickle”):
    以open(“token.pickle”、“rb”)作为令牌:
    creds=pickle.load(令牌)
    如果没有信用或信用无效:
    如果creds和creds.expired和creds.refresh\u令牌:
    creds.refresh(请求())
    其他:
    flow=InstalledAppFlow.from_client_secrets_文件(
    “credentials.json”,作用域
    )
    host=“localhost”
    端口=8080
    state=“default”
    QtCore.QTimer.singleShot(
    0,functools.partial(self.get_url、流、主机、端口、状态)
    )
    creds=flow.run\u本地\u服务器(
    主机=主机,端口=端口,打开浏览器=错误,状态=状态