如何从QOAuth2AuthorizationCodeFlow收到错误通知?

kr98yfug  于 2022-10-08  发布在  其他
关注(0)|答案(2)|浏览(163)

我正在使用QOAuth2AuthorizationCodeFlow执行OIDC身份验证。我可以连接到信号QAbstractOAuth::Grated(),并在它成功工作时收到通知。很好。

我的问题是:当错误发生时,如何得到通知?

我试图连接到QAbstractOAuth2::Error()信号,但没有收到通知。我使用:

QObject::connect(this, SIGNAL(error(const QString &, const QString &, const QUrl &)), this, SLOT(catchAll()));

在应用程序控制台中,我看到服务器报告的错误:

qt.networkauth.replyhandler: Error transferring https://idp.safenetid.com/auth/realms/2H31DFOIEQ-STA/protocol/openid-connect/token - server replied: Fake Bad request

所以Qt发现了这个问题。看起来在QHttpThreadDelegate::finishedSlot()中检测到了该问题:

if (httpReply->statusCode() >= 400) {
        // it's an error reply
        QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply",
                                                      "Error transferring %1 - server replied: %2"));
        msg = msg.arg(httpRequest.url().toString(), httpReply->reasonPhrase());
        emit error(statusCodeFromHttp(httpReply->statusCode(), httpRequest.url()), msg);
    }

发出error()信号。但它是由Qt本身捕获的,而不是上游报告给应用程序的?

注意:有时我使用的服务器会回复一个HTTP错误代码400和消息“User Not Found”。在这里,我使用mitmproxy伪造了问题,以便能够轻松地重现该问题。

PS:我认为这是Qt中缺失的一个功能。因此,我在https://bugreports.qt.io/browse/QTBUG-102279为QOAuth2AuthorizationCodeFlow::requestAccessToken()创建了一个问题

flseospp

flseospp1#

您可以通过实现您自己的OAuthHttpServerReplyHandler来获得通知。此处理程序必须使用其setReplyHandler(_)设置为您的QOAuth2AuthorizationCodeFlow

OAuthHttpServerReplyHandler必须覆盖将由Qt调用的void networkReplyFinished(QNetworkReply *reply)

kh212irz

kh212irz2#

tl;dr:连接QOAuth2AuthorizationCodeFlow::authorizationCallbackReceived,编写自己的处理程序。

看一下源代码,问题似乎是这些case语句中只有一个语句实际上会发出error信号。它们都在没有Q_EMIT error(...)调用的情况下返回。

void QOAuth2AuthorizationCodeFlowPrivate::_q_handleCallback(const QVariantMap &data)
{
    Q_Q(QOAuth2AuthorizationCodeFlow);
    using Key = QAbstractOAuth2Private::OAuth2KeyString;

    if (status != QAbstractOAuth::Status::NotAuthenticated) {
        qCWarning(loggingCategory, "Unexpected call");
        return;
    }

    Q_ASSERT(!state.isEmpty());

    const QString error = data.value(Key::error).toString();
    const QString code = data.value(Key::code).toString();
    const QString receivedState = data.value(Key::state).toString();
    if (error.size()) {
        const QString uri = data.value(Key::errorUri).toString();
        const QString description = data.value(Key::errorDescription).toString();
        qCWarning(loggingCategory, "AuthenticationError: %s(%s): %s",
                 qPrintable(error), qPrintable(uri), qPrintable(description));
        Q_EMIT q->error(error, description, uri);
        return;
    }
    if (code.isEmpty()) {
        qCWarning(loggingCategory, "AuthenticationError: Code not received");
        return;
    }
    if (receivedState.isEmpty()) {
        qCWarning(loggingCategory, "State not received");
        return;
    }
    if (state != receivedState) {
        qCWarning(loggingCategory, "State mismatch");
        return;
    }

    setStatus(QAbstractOAuth::Status::TemporaryCredentialsReceived);

    QVariantMap copy(data);
    copy.remove(Key::code);
    extraTokens = copy;
    q->requestAccessToken(code);
}

但对于error信号的用途似乎也存在误解;根据其声明中的参数,

void QAbstractOAuth2::error(const QString &error, const QString &errorDescription, const QUrl &uri)

错误信号是针对*rfc6749中描述的HTTP400“错误请求”响应的,而不是针对所有错误的信号,这就是为什么它不会针对state mismatch之类的(客户端)错误发出信号(这可能是一个非常严重的错误)。

但是,如果您仍然希望传播error信号,则可以像私有类一样连接到&QOAuth2AuthorizationCodeFlow::authorizationCallbackReceived信号,然后发出error信号。这需要一些创造力,因为代码流私有类中的成员不可用,但可以这样做。

例如,如果要在状态不匹配时传播错误,可能如下所示:

{
   static QString last_state;
   QObject::connect(
      &oauth2, &QOAuth2AuthorizationCodeFlow::stateChanged,
      [&](const QString& state) {
         last_state = state;
      });
   QObject::connect(
      &oauth2, QOAuth2AuthorizationCodeFlow::authorizationCallbackReceived,
      [&](const QVariantMap& data) {
         const QString state = data.value("state").toString();

         if(last_state != state) {
             Q_EMIT oauth2.error(
                 "The state in request does not match the state in reply!",                      
                 "State Mismatch!",
                 QUrl());
         }
       });
}

然而,由于这是对error函数的滥用,我建议将所有这些内容放在一个不同的对象或类中;一个可能具有stateUpdatedonCallbackReceived插槽并能够发出stateMismatch信号的对象或类。

相关问题