diff options
author | Juha Vuolle <juha.vuolle@qt.io> | 2024-10-07 11:21:05 +0300 |
---|---|---|
committer | Juha Vuolle <juha.vuolle@qt.io> | 2024-11-14 15:14:37 +0200 |
commit | 741fd2b1cef4159aabbc90a870adf7450945eeac (patch) | |
tree | 81de792376ddacc8bacdffbadde112c175891a7e /src/oauth/qoauthhttpserverreplyhandler.cpp | |
parent | ea2787daf64a37d68606ac4ed54ea100af9a0147 (diff) |
Add https support to QOAuthHttpServerReplyHandler
[ChangeLog][QOAuthHttpServerReplyHandler] Added support
for https localhost server
Fixes: QTBUG-64615
Change-Id: Ic28c74bb382642077e791ea1e6cd0b3ed9ee2264
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Diffstat (limited to 'src/oauth/qoauthhttpserverreplyhandler.cpp')
-rw-r--r-- | src/oauth/qoauthhttpserverreplyhandler.cpp | 154 |
1 files changed, 131 insertions, 23 deletions
diff --git a/src/oauth/qoauthhttpserverreplyhandler.cpp b/src/oauth/qoauthhttpserverreplyhandler.cpp index 221f3ae..4bd1c2d 100644 --- a/src/oauth/qoauthhttpserverreplyhandler.cpp +++ b/src/oauth/qoauthhttpserverreplyhandler.cpp @@ -17,6 +17,10 @@ #include <QtCore/qloggingcategory.h> #include <QtCore/private/qlocale_p.h> +#ifndef QT_NO_SSL +#include <QtNetwork/qsslserver.h> +#include <QtNetwork/qsslsocket.h> +#endif #include <QtNetwork/qtcpsocket.h> #include <QtNetwork/qnetworkreply.h> @@ -73,25 +77,64 @@ using namespace Qt::StringLiterals; \e {http://localhost:{port}/{path}}. Otherwise, for specific IP addresses, the actual IP literal is used. For instance \e {http://192.168.0.2:{port}/{path}} in the case of IPv4. + + \section1 HTTP and HTTPS Callbacks + + Since Qt 6.9 it's possible to configure the handler to use + \c {https} URI scheme instead of \c {http}. This is done by + providing an appropriate \l QSslConfiguration when calling + \l {listen(const QSslConfiguration &, const QHostAddress &, quint16)}{listen()}. + Internally the handler will then use \l QSslServer, and the callback + (redirect URL) will be of the form \e {https://localhost:{port}/{path}}. + + Following example illustrates this: + \snippet src_oauth_replyhandlers.cpp localhost-https-scheme-setup + + When possible, it is recommended to use other redirect URI + options, see \l {Choosing A Reply Handler} and + \l {Qt OAuth2 Browser Support}. + + The primary use cases for a localhost \c {https} handler + should be limited to development-time, or tightly controlled + and provisioned environments. For example, some Authorization + Servers won't allow plain \c {http} redirect URIs at all, in which + case this can add to development convenience. + + From security perspective, + while using SSL/TLS does encrypt the localhost traffic, OAuth2 + has also other security mechanisms in place such as + \l {QOAuth2AuthorizationCodeFlow::PkceMethod}{PKCE}. + Under no circumstances you should distribute private certificate + keys along with the application. + + \note Browsers will issue severe warnings + if the certificate is not trusted. This is typical with + self-signed certificates, whose use should be limited to + development-time. */ QOAuthHttpServerReplyHandlerPrivate::QOAuthHttpServerReplyHandlerPrivate( QOAuthHttpServerReplyHandler *p) : text(QObject::tr("Callback received. Feel free to close this page.")), path(u'/'), q_ptr(p) { - QObject::connect(&httpServer, &QTcpServer::newConnection, q_ptr, - [this]() { _q_clientConnected(); }); } QOAuthHttpServerReplyHandlerPrivate::~QOAuthHttpServerReplyHandlerPrivate() { - if (httpServer.isListening()) - httpServer.close(); + if (httpServer->isListening()) + httpServer->close(); } QString QOAuthHttpServerReplyHandlerPrivate::callback() const { QUrl url; +#ifndef QT_NO_SSL + if (qobject_cast<QSslServer*>(httpServer)) + url.setScheme(u"https"_s); + else + url.setScheme(u"http"_s); +#else url.setScheme(u"http"_s); +#endif url.setPort(callbackPort); url.setPath(path); @@ -109,7 +152,7 @@ QString QOAuthHttpServerReplyHandlerPrivate::callback() const void QOAuthHttpServerReplyHandlerPrivate::_q_clientConnected() { - QTcpSocket *socket = httpServer.nextPendingConnection(); + QTcpSocket *socket = httpServer->nextPendingConnection(); QObject::connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater); QObject::connect(socket, &QTcpSocket::readyRead, q_ptr, @@ -121,7 +164,7 @@ void QOAuthHttpServerReplyHandlerPrivate::_q_readData(QTcpSocket *socket) QHttpRequest *request = nullptr; if (auto it = clients.find(socket); it == clients.end()) { request = &clients[socket]; // insert it - request->port = httpServer.serverPort(); + request->port = httpServer->serverPort(); } else { request = &*it; } @@ -188,6 +231,36 @@ void QOAuthHttpServerReplyHandlerPrivate::_q_answerClient(QTcpSocket *socket, co socket->disconnectFromHost(); } +void QOAuthHttpServerReplyHandlerPrivate::initializeLocalServer() +{ + Q_Q(QOAuthHttpServerReplyHandler); + QObject::connect(httpServer, &QTcpServer::pendingConnectionAvailable, q, [this]() { + _q_clientConnected(); + }); +} + +bool QOAuthHttpServerReplyHandlerPrivate::listen(const QHostAddress &address, quint16 port) +{ + Q_ASSERT(httpServer); + bool success = false; + + if (address.isNull()) { + // try IPv4 first, for greatest compatibility + success = httpServer->listen(QHostAddress::LocalHost, port); + if (!success) + success = httpServer->listen(QHostAddress::LocalHostIPv6, port); + } + if (!success) + success = httpServer->listen(address, port); + + if (success) { + // Callback ('redirect_uri') value may be needed after this handler is closed + callbackAddress = httpServer->serverAddress(); + callbackPort = httpServer->serverPort(); + } + return success; +} + bool QOAuthHttpServerReplyHandlerPrivate::QHttpRequest::readMethod(QTcpSocket *socket) { bool finished = false; @@ -333,7 +406,10 @@ QOAuthHttpServerReplyHandler::QOAuthHttpServerReplyHandler(const QHostAddress &a QOAuthOobReplyHandler(parent), d_ptr(new QOAuthHttpServerReplyHandlerPrivate(this)) { - listen(address, port); + Q_D(QOAuthHttpServerReplyHandler); + d->httpServer = new QTcpServer(this); + d->initializeLocalServer(); + d->listen(address, port); } /*! @@ -422,7 +498,7 @@ void QOAuthHttpServerReplyHandler::setCallbackText(const QString &text) quint16 QOAuthHttpServerReplyHandler::port() const { Q_D(const QOAuthHttpServerReplyHandler); - return d->httpServer.serverPort(); + return d->httpServer->serverPort(); } /*! @@ -451,25 +527,57 @@ quint16 QOAuthHttpServerReplyHandler::port() const bool QOAuthHttpServerReplyHandler::listen(const QHostAddress &address, quint16 port) { Q_D(QOAuthHttpServerReplyHandler); - bool success = false; +#ifndef QT_NO_SSL + if (qobject_cast<QSslServer*>(d->httpServer)) { + d->httpServer->close(); + delete d->httpServer; + d->httpServer = new QTcpServer(this); + d->initializeLocalServer(); + } +#endif + return d->listen(address, port); +} - if (address.isNull()) { - // try IPv4 first, for greatest compatibility - success = d->httpServer.listen(QHostAddress::LocalHost, port); - if (!success) - success = d->httpServer.listen(QHostAddress::LocalHostIPv6, port); +#ifndef QT_NO_SSL +/*! + Tells this handler to listen for incoming \c https + connections / redirections on \a address and \a port. Returns + \c true if listening is successful, and \c false otherwise. + + See \l {HTTP and HTTPS Callbacks} for further information. + + \sa listen(const QHostAddress &, quint16), close(), + isListening(), QSslServer, QTcpServer::listen() +*/ +bool QOAuthHttpServerReplyHandler::listen(const QSslConfiguration &configuration, + const QHostAddress &address, quint16 port) +{ + Q_D(QOAuthHttpServerReplyHandler); + + if (!QSslSocket::supportsSsl()) { + qCWarning(lcReplyHandler, "SSL not supported, cannot listen"); + d->httpServer->close(); + return false; } - if (!success) - success = d->httpServer.listen(address, port); - if (success) { - // Callback ('redirect_uri') value may be needed after this handler is closed - d->callbackAddress = d->httpServer.serverAddress(); - d->callbackPort = d->httpServer.serverPort(); + if (configuration.isNull()) { + qCWarning(lcReplyHandler, "QSslConfiguration is null, cannot listen"); + d->httpServer->close(); + return false; } - return success; + if (!qobject_cast<QSslServer*>(d->httpServer)) { + d->httpServer->close(); + delete d->httpServer; + d->httpServer = new QSslServer(this); + d->initializeLocalServer(); + } + + auto sslServer = qobject_cast<QSslServer*>(d->httpServer); + sslServer->setSslConfiguration(configuration); + return d->listen(address, port); } +#endif /*! Tells this handler to stop listening for connections / redirections. @@ -479,7 +587,7 @@ bool QOAuthHttpServerReplyHandler::listen(const QHostAddress &address, quint16 p void QOAuthHttpServerReplyHandler::close() { Q_D(QOAuthHttpServerReplyHandler); - return d->httpServer.close(); + d->httpServer->close(); } /*! @@ -491,7 +599,7 @@ void QOAuthHttpServerReplyHandler::close() bool QOAuthHttpServerReplyHandler::isListening() const { Q_D(const QOAuthHttpServerReplyHandler); - return d->httpServer.isListening(); + return d->httpServer->isListening(); } QT_END_NAMESPACE |