无法在 Qt 应用程序中获取 OAuth 2.0 代码,但似乎可以在浏览器中使用

Can't get OAuth 2.0 code in Qt app, but is seems to work in browser

我正在尝试在我的 Qt 项目中配置与 Google OAuth 2.0 的通信。我使用的是 this tutorial,虽然它似乎有点过时了。我在 Google API 站点中配置了所有内容,在 Credentials/OAuth 2.0 Client IDs 页面中使用了这些数据:

头文件:

#ifndef GOOGLEAUTH_H
#define GOOGLEAUTH_H

#include <QObject>
#include <QOAuth2AuthorizationCodeFlow>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QUrl>
#include <QUrlQuery>
#include <QOAuthHttpServerReplyHandler>
#include <QDesktopServices>

class GoogleAuth : public QObject
{
    Q_OBJECT
public:
    explicit GoogleAuth(QObject *parent = nullptr);
    Q_INVOKABLE void click();    

private:
    QOAuth2AuthorizationCodeFlow google;
};

#endif // GOOGLEAUTH_H

源文件:

#include "GoogleAuthenticator.h"

GoogleAuth::GoogleAuth(QObject *parent) : QObject(parent)
{
    google.setScope("email");

    connect(&google, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, [=](QUrl url) {
        QUrlQuery query(url);

        query.addQueryItem("prompt", "consent");      
        query.addQueryItem("access_type", "offline");
        query.addQueryItem("nonce", "123456");
        url.setQuery(query);
        QDesktopServices::openUrl(url);
    });    

    google.setAuthorizationUrl(QUrl("https://accounts.google.com/o/oauth2/auth"));
    google.setAccessTokenUrl(QUrl("https://oauth2.googleapis.com/token"));
    google.setClientIdentifier("<client_id>");
    google.setClientIdentifierSharedKey("<client_secret>");

    auto replyHandler = new QOAuthHttpServerReplyHandler(5476, this);
    google.setReplyHandler(replyHandler);

    connect(&google, &QOAuth2AuthorizationCodeFlow::granted, [=]() {
        qDebug() << "Access Granted!";
    });
}

void GoogleAuth::click()
{
    google.grant();
}

当我 运行 click() 方法浏览器打开时,我可以登录 Google 帐户,然后它将我重定向到包含以下消息的页面:

Callback received. Feel free to close this page.

我什至可以在 URL 中看到我需要的代码(至少我认为是那个代码?)。

问题是我在 Qt 应用程序中没有得到正确的回调。当上面带有回调消息的页面被加载时,我在 Qt Creator 中得到了这个输出:

qt.networkauth.oauth2: Unexpected call
qt.networkauth.replyhandler: Error transferring https://oauth2.googleapis.com/token - server replied: Bad Request

在应用程序外它似乎工作正常,我在 this page 上检查过它。

如何解决这个问题?我可以获得有关该错误请求的更多详细信息吗?

我已经测试了这个例子,奇怪的是它不适用于“电子邮件”范围,在分析http请求后我发现问题是收到的“代码”的编码问题,它用于获取令牌。所以我的解决方案是更正该参数并且可以覆盖 requestAccessToken() method or use setModifyParametersFunction(),在这种情况下使用后者:

google.setModifyParametersFunction([](QAbstractOAuth::Stage stage,
                                   QVariantMap* parameters)
{
    if(stage == QAbstractOAuth::Stage::RequestingAccessToken){
        QByteArray code = parameters->value("code").toByteArray();
        (*parameters)["code"] = QUrl::fromPercentEncoding(code);
    }
});

官方教程已经过时了,它包含了几个与您面临的问题类似的问题。具体来说,需要对Google发送的登录码进行URL解码,但是需要从Qt内部截取参数,需要安装参数处理器:

googleSSO.setModifyParametersFunction([](QAbstractOAuth::Stage loginStage, QVariantMap* parameters) {
   // Percent-decode the "code" parameter so Google can match it
   if (QAbstractOAuth::Stage::RequestingAccessToken == loginStage) {
      QByteArray code = parameters->value("code").toByteArray();
      (*parameters)["code"] = QUrl::fromPercentEncoding(code);
   }
});

我们在博客中写到了 how to authenticate a Qt app with Google SSO 以及我们遇到的所有问题。

Pablo 的回答也已经过时了。目前的工作解决方案是:

void GoogleCloudAuth::modifyParametersFunction(QAbstractOAuth::Stage stage, QMultiMap<QString, QVariant>* parameters)
{
    qDebug() << "modifyParametersFunction stage=" << static_cast<int>(stage);
    if (stage == QAbstractOAuth::Stage::RequestingAuthorization)
    {
        // The only way to get refresh_token from Google Cloud
        parameters->insert("access_type", "offline");
        parameters->insert("prompt", "consent");
    }
    else if (stage == QAbstractOAuth::Stage::RequestingAccessToken)
    {
        // Percent-decode the "code" parameter so Google can match it
        QByteArray code = parameters->value("code").toByteArray();
        parameters->replace("code", QUrl::fromPercentEncoding(code));
    }
}