python - 使用 PyQt4/5 隐式 OAuth2 授权

标签 python oauth-2.0 pyqt python-requests qwebview

我一直在开发一个使用 OAuth2 来识别用户的 python 应用程序。我似乎已经成功实现了 OAuth2 隐式授予的工作流程(通常用于已安装的应用程序和用户代理应用程序),但在接收 token 的最后一步,似乎出现了问题。

每当用户需要进行身份验证时,就会生成一个 PyQt QWebView 窗口(基于 webkit),显示登录页面。用户登录并允许我的应用程序的范围权限后,OAuth2 服务器重定向到预先指定的redirect_uri。

问题是,当使用 QWebView 浏览器时,通常出现在 # 之后的 token 字符串似乎已从 URL 中删除:QWebView 返回的 URL 只是基本的 redirect_uri。

如果我复制粘贴 OAuth 授权 URL,并在普通 Web 浏览器(例如 Chrome 或 Firefox)中执行登录和授权的相同步骤,我确实会看到包含 token 字符串的 redirect_uri,因此问题不会出现位于 OAuth2 流程中,但在我这边的实现中一定会出现问题。

这种行为是 QWebView 或 webkit 的实现所固有的吗?我读出的 QUrl 不正确吗?

为了完整起见,这是我的代码:

osf.py 模块,为开放科学框架生成 OAuth2 URL。

# Import basics
import sys
import os

# Module for easy OAuth2 usage, based on the requests library,
# which is the easiest way to perform HTTP requests.

# OAuth2Session object
from requests_oauthlib import OAuth2Session
# Mobile application client that does not need a client_secret
from oauthlib.oauth2 import MobileApplicationClient

#%%----------- Main configuration settings ----------------
client_id = "cbc4c47b711a4feab974223b255c81c1"
# TESTED, just redirecting to Google works in normal browsers
# the token string appears in the url of the address bar
redirect_uri = "https://google.nl"

# Generate correct URLs
base_url = "https://test-accounts.osf.io/oauth2/"
auth_url = base_url + "authorize"
token_url = base_url + "token"
#%%--------------------------------------------------------

mobile_app_client = MobileApplicationClient(client_id)

# Create an OAuth2 session for the OSF
osf_auth = OAuth2Session(
    client_id, 
    mobile_app_client,
    scope="osf.full_write", 
    redirect_uri=redirect_uri,
)

def get_authorization_url():
    """ Generate the URL with which one can authenticate at the OSF and allow 
    OpenSesame access to his or her account."""
    return osf_auth.authorization_url(auth_url)

def parse_token_from_url(url):
    token = osf_auth.token_from_fragment(url)
    if token:
        return token
    else:
        return osf_auth.fetch_token(url)

主程序,打开带有登录屏幕的 QWebView 浏览器窗口

# Oauth2 connection to OSF
import off
import sys
from PyQt4 import QtGui, QtCore, QtWebKit

class LoginWindow(QtWebKit.QWebView):
    """ A Login window for the OSF """

    def __init__(self):
        super(LoginWindow, self).__init__()
        self.state = None
        self.urlChanged.connect(self.check_URL)

    def set_state(self,state):
        self.state = state

    def check_URL(self, url):
        #url is a QUrl object, covert it to string for easier usage
        url_string = url.toEncoded()
        print(url_string)

        if url.hasFragment():
            print("URL CHANGED: On token page: {}".format(url))
            self.token = osf.parse_token_from_url(url_string)
            print(self.token)
        elif not osf.base_url in url_string:
            print("URL CHANGED: Unexpected url")

if __name__ == "__main__":
    """ Test if user can connect to OSF. Opens up a browser window in the form
    of a QWebView window to do so."""
    # Import QT libraries

    app = QtGui.QApplication(sys.argv)
    browser = LoginWindow()

    auth_url, state = osf.get_authorization_url()
    print("Generated authorization url: {}".format(auth_url))

    browser_url = QtCore.QUrl.fromEncoded(auth_url)
    browser.load(browser_url)
    browser.set_state(state)
    browser.show()

    exitcode = app.exec_()
    print("App exiting with code {}".format(exitcode))
    sys.exit(exitcode)

基本上,当我从 OAuth 服务器返回时,QWebView 的 url_changed 事件提供给 check_URL 函数的 url 永远不会包含 OAuth token 片段,无论我使用什么用于 redirect_uri (在本例中,我只是重定向到 google)简单)。

有人可以帮我解决这个问题吗?我已经用尽了在哪里寻找此问题的解决方案的选择。

最佳答案

这似乎是 Webkit/Safari 中的一个已知错误:

https://bugs.webkit.org/show_bug.cgi?id=24175 https://phabricator.wikimedia.org/T110976#1594914

基本上它是不固定的,因为人们对于根据 HTTP 规范所期望的行为应该是什么没有达成一致。 How do I preserve uri fragment in safari upon redirect? 描述了可能的修复。但我无法对此进行测试。

编辑

我设法找到了一个(不太优雅的)解决方法来解决这个问题。我没有使用 QWebView 中的 urlChanged 事件(它不显示 OAuth 服务器完成的 301 重定向),而是使用 QNetworkAccessManager 的 finish() 事件。 任何 http 请求完成后都会触发此事件(对于页面的所有链接内容(例如图像、样式表等)也是如此,因此您必须进行大量过滤)。

现在我的代码如下所示:

class LoginWindow(QtWebKit.QWebView):
    """ A Login window for the OSF """
    # Login event is emitted after successfull login
    logged_in = QtCore.pyqtSignal(['QString'])  

    def __init__(self):
        super(LoginWindow, self).__init__()

        # Create Network Access Manager to listen to all outgoing
        # HTTP requests. Necessary to work around the WebKit 'bug' which
        # causes it drop url fragments, and thus the access_token that the
        # OSF Oauth system returns
        self.nam = self.page().networkAccessManager()

        # Connect event that is fired if a HTTP request is completed.
        self.nam.finished.connect(self.checkResponse)

    def checkResponse(self,reply):
        request = reply.request()
        # Get the HTTP statuscode for this response
        statuscode = reply.attribute(request.HttpStatusCodeAttribute)
        # The accesstoken is given with a 302 statuscode to redirect

        if statuscode == 302:
            redirectUrl = reply.attribute(request.RedirectionTargetAttribute)
            if redirectUrl.hasFragment():
                r_url = redirectUrl.toString()
                if osf.redirect_uri in r_url:
                    print("Token URL: {}".format(r_url))
                    self.token = osf.parse_token_from_url(r_url)
                    if self.token:
                        self.logged_in.emit("login")
                        self.close()

关于python - 使用 PyQt4/5 隐式 OAuth2 授权,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35386030/

相关文章:

python通过通配符复制文件

python - 类实例可以知道它是否是一个参数吗?

python - 数据库主键正在递增,但没有添加新行

python - Sytanx错误: Invalid Sytax

QT - QGridLayout 需要不同的标题行间距

java - Spring Security + OAuth,如果访问 token 不存在则回退

python - authlib OAuth 客户端与 Flask 应用程序一起使用的更好示例?

oauth-2.0 - 发送 http post 请求以从身份验证代码获取 token 时出现 400 Bad Request?

python - opencv BGR 图像和它的反向版本 RGB 图像 [:,:,::-1] 有什么区别?

python - 将 stdout 和 stderr 从辅助线程重定向到 PyQt4 QTextEdit