summaryrefslogtreecommitdiffstats
path: root/src/oauth/qoauthhttpserverreplyhandler.cpp
diff options
context:
space:
mode:
authorJuha Vuolle <juha.vuolle@qt.io>2024-10-07 11:21:05 +0300
committerJuha Vuolle <juha.vuolle@qt.io>2024-11-14 15:14:37 +0200
commit741fd2b1cef4159aabbc90a870adf7450945eeac (patch)
tree81de792376ddacc8bacdffbadde112c175891a7e /src/oauth/qoauthhttpserverreplyhandler.cpp
parentea2787daf64a37d68606ac4ed54ea100af9a0147 (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.cpp154
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