Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

OAuth2 QOAuthHttpServerReplyHandler HTTPS/SSL support?



  • Hi,

    I am integrating with a provider that does NOT accept regular HTTP URIs for Callback URLs. It requires everything to be HTTPS.

    So the OAuth2 code looks like this:

    auto replyHandler = new QOAuthHttpServerReplyHandler(1337, this);
    replyHandler->setCallbackPath(QString("/"));
    oauth2.setReplyHandler(replyHandler);
    

    But this creates a TCP/IP server port that is HTTP and not HTTPS and things just fall apart. Is there a way to make it work with HTTPS?

    Thank you!





  • Ahh, thank you. I looked at the bug and tried to register the app just with localhost:1337 but the site didn't like it. It want the http(s) in front of it, and then replaces that with https anyway.

    Assuming that I open the browser in WebEngineView, can I intercept the redirect and change https with http? If so, do you have any pointers for that?

    thank you,
    alex



  • Ok, I found a working solution for this, but it's really another "hack" :-)
    I hope there would be a better way to get this OAuth2 to work the way it should. Also, on a different note, I emailed the provider about this issue and they said they are going to change it for localhost and leave it HTTP if it was defined HTTP.

    I was able to use concepts from this page, by leveraging the RequestInterceptor:

    https://forum.qt.io/topic/69135/how-to-send-network-requests-in-qwebengine/2

    then in my code, I did something like this to alter the URL (i did a redirect):

    void WebUrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) {
    QString rsrct = "";
    switch(info.resourceType()){
    case 0:rsrct="ResourceTypeMainFrame = 0, // top level page";break;
    // case 1:rsrct="ResourceTypeSubFrame, // frame or iframe";break;
    // case 2:rsrct="ResourceTypeStylesheet, // a CSS stylesheet";break;
    // case 3:rsrct="ResourceTypeScript, // an external script";break;
    // case 4:rsrct="ResourceTypeImage, // an image (jpg/gif/png/etc)";break;
    // case 5:rsrct="ResourceTypeFontResource, // a font";break;
    // case 6:rsrct="ResourceTypeSubResource, // an other subresource.";break;
    // case 7:rsrct="ResourceTypeObject, // an object (or embed) tag for a plugin,";break;
    // case 8:rsrct="ResourceTypeMedia, // a media resource.";break;
    // case 9:rsrct="ResourceTypeWorker, // the main resource of a dedicated worker.";break;
    // case 10:rsrct="ResourceTypeSharedWorker, // the main resource of a shared worker.";break;
    // case 11:rsrct="ResourceTypePrefetch, // an explicitly requested prefetch";break;
    // case 12:rsrct="ResourceTypeFavicon, // a favicon";break;
    // case 13:rsrct="ResourceTypeXhr, // a XMLHttpRequest";break;
    // case 14:rsrct="ResourceTypePing, // a ping request for <a ping>";break;
    // case 15:rsrct="ResourceTypeServiceWorker, // the main resource of a service worker.";break;
    // case 16:rsrct="ResourceTypeUnknown";break;

    default : rsrct="Else";return;
    }
    

    qDebug()<<"\t"<<Q_FUNC_INFO<<":\n\t\t" << "WebUrlRequestInterceptor::interceptRequest " <<info.requestMethod()
    <<"\r\n "<<info.requestUrl()<<" "<<rsrct <<"\r\n";
    if (info.requestUrl().toString() == "https://localhost/") {
    qDebug()<<"*** REDIRECTING";
    const QUrl u("http://localhost/");
    info.redirect(u);
    }



  • @Alex94102 I suspect the OAuth2 library is broken. It doesn't URL Encode the POST request body, I verified that. The technique used doesn't work properly.


  • Lifetime Qt Champion

    Hi,

    Would it be possible for you to provide a minimal compilable example that reproduces that behaviour ?



  • Hi,

    so essentially I went down that path and the service call for retrieving the refresh_token was failing and I couldn't figure out why. To diagnose that I tested that with an echo server to print what was being sent and compare that with a working CURL call. What I noticed is that the parameters that were placed in the body were not URL encoded but they were left as is. Then I looked at the source code (see below) and this is the method where he's doing the POST (the body of the request is created by this line const QString data = query.toString(QUrl::FullyEncoded);

    By replicating this functionality and properly encoding the parameter values only with QUrl::toPercentEncoding() everything works as it should. Let me know if you need more details...:

    void QOAuth2AuthorizationCodeFlow::requestAccessToken(const QString &code)
    {
    Q_D(QOAuth2AuthorizationCodeFlow);
    using Key = QAbstractOAuth2Private::OAuth2KeyString;

    QVariantMap parameters;
    QNetworkRequest request(d->accessTokenUrl);
    QUrlQuery query;
    parameters.insert(Key::grantType, QStringLiteral("authorization_code"));
    parameters.insert(Key::code, QUrl::toPercentEncoding(code));
    parameters.insert(Key::redirectUri, QUrl::toPercentEncoding(callback()));
    parameters.insert(Key::clientIdentifier, QUrl::toPercentEncoding(d->clientIdentifier));
    if (!d->clientIdentifierSharedKey.isEmpty())
        parameters.insert(Key::clientSharedSecret, d->clientIdentifierSharedKey);
    if (d->modifyParametersFunction)
        d->modifyParametersFunction(Stage::RequestingAccessToken, &parameters);
    query = QAbstractOAuthPrivate::createQuery(parameters);
    request.setHeader(QNetworkRequest::ContentTypeHeader,
                      QStringLiteral("application/x-www-form-urlencoded"));
    
    const QString data = query.toString(QUrl::FullyEncoded);
    QNetworkReply *reply = d->networkAccessManager()->post(request, data.toUtf8());
    d->currentReply = reply;
    QAbstractOAuthReplyHandler *handler = replyHandler();
    QObject::connect(reply, &QNetworkReply::finished,
                     [handler, reply] { handler->networkReplyFinished(reply); });
    QObjectPrivate::connect(d->replyHandler.data(), &QAbstractOAuthReplyHandler::tokensReceived, d,
                            &QOAuth2AuthorizationCodeFlowPrivate::_q_accessTokenRequestFinished,
                            Qt::UniqueConnection);
    QObjectPrivate::connect(d->networkAccessManager(),
                            &QNetworkAccessManager::authenticationRequired,
                            d, &QOAuth2AuthorizationCodeFlowPrivate::_q_authenticate,
                            Qt::UniqueConnection);
    

    }


  • Lifetime Qt Champion

    It looks like you found a bug :)

    Did you already check the bug report system to see if there was something related ?

    If not, please consider opening a new report providing all the information you have here (do not just link this thread, it's better to have all there).

    Since you also find a solution, would you consider submitting a patch directly ?



  • Hi,

    to be honest, I don't know how to submit a patch, I've never done it before. Would that be a patch file? I am a little lost. But essentially the code above where the "data" variable is populated by that statement needs to be replaced by something like this (just to get the idea). In my case, I used a QVariantMap for storing my parameter values, and then I build the QByteArray for the request using the method below. :

    QByteArray MyOwnOAuth2::buildBodyPayload()
    {
    bool first=true;
    QByteArray body;
    for(QVariantMap::const_iterator iter = mBodyParameters.begin(); iter != mBodyParameters.end(); ++iter) {
    qDebug() << iter.key() << iter.value();
    if (!first) {
    body.append(QString("&").toUtf8());
    }
    QString name = iter.key();
    QString value = QUrl::toPercentEncoding(iter.value().toString());
    body.append(name.toUtf8());
    body.append(QString("=").toUtf8());
    body.append(value.toUtf8());
    first = false;
    }
    return body;
    }


  • Lifetime Qt Champion

    @Alex94102 said in OAuth2 QOAuthHttpServerReplyHandler HTTPS/SSL support?:

    to be honest, I don't know how to submit a patch, I've never done it before. Would that be a patch file?

    A patch is the difference in code between the original and your new version: https://en.wikipedia.org/wiki/Patch_(computing)

    The preferred way to get patches into Qt is through Gerrit, however small patches may also be attached to the bug report. In any case, we can help you with that, preferred in a separate discussion.

    body.append(QString("&").toUtf8());

    That (and the similar line) should be simplified to: body.append("&");

    PS: Please add a link to the bug report here, so we can follow. Thanks!



  • Sorry, I was out of town. I think I am familiar with the patch utility, my question is more around the whole process. Maybe someone can get back to me directly so that we are not polluting this topic with these kind of general questions. At the present time the code that I put together is not using this library anymore as it was defective, so for me to properly test I would have to make some changes to my code and to make it use this patched library instead.

    What I am looking for is some pointers (not c++) on how to get this done (example):

    1. File a JIRA bug report
    2. Prepare the patch if bug confirmed
    3. This is where I am struggling, would I send the simple patch file to the maintainer? I guess so, submit that in JIRA?
    4. Would I need also submit unit tests for this?
    5. Anythig else?

    Thanks a lot,
    alex


  • Lifetime Qt Champion

    1. Yes if not already existing
    2. Since you are experiencing this and have a solution, say it in the report.
    3. The nicest and fastest would be to submit the patch yourself for review. See the gerrit introcution.
    4. Since you are solving a specific problem, it would be best since it's not already covered by a test
    5. Be patient, sometime patches can get reviewed quickly while some needs more time. It has nothing to do with the fact that its your first submission.


  • @SGaist Ok, I have enough info to get started. Thank you.


  • Lifetime Qt Champion

    You're welcome !

    One last thing: if you open a new report, please post a link to it here so people can more easily find it :)


Log in to reply