diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 9303bed2b5..bf7a8e2f1e 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1959,7 +1959,8 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url return _settingsManager.handleAuthenticatedHTTPRequest(connection, url); } -const QString HIFI_SESSION_COOKIE_KEY = "DS_WEB_SESSION_UUID"; +static const QString HIFI_SESSION_COOKIE_KEY = "DS_WEB_SESSION_UUID"; +static const QString STATE_QUERY_KEY = "state"; bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &url, bool skipSubHandler) { qDebug() << "HTTPS request received at" << url.toString(); @@ -1970,10 +1971,9 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u const QString CODE_QUERY_KEY = "code"; QString authorizationCode = codeURLQuery.queryItemValue(CODE_QUERY_KEY); - const QString STATE_QUERY_KEY = "state"; QUuid stateUUID = QUuid(codeURLQuery.queryItemValue(STATE_QUERY_KEY)); - if (!authorizationCode.isEmpty() && !stateUUID.isNull()) { + if (!authorizationCode.isEmpty() && !stateUUID.isNull() && _webAuthenticationStateSet.remove(stateUUID)) { // fire off a request with this code and state to get an access token for the user const QString OAUTH_TOKEN_REQUEST_PATH = "/oauth/token"; @@ -1991,47 +1991,83 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); QNetworkReply* tokenReply = NetworkAccessManager::getInstance().post(tokenRequest, tokenPostBody.toLocal8Bit()); + connect(tokenReply, &QNetworkReply::finished, this, &DomainServer::tokenGrantFinished); - if (_webAuthenticationStateSet.remove(stateUUID)) { - // this is a web user who wants to auth to access web interface - // we hold the response back to them until we get their profile information - // and can decide if they are let in or not + // add this connection to our list of pending connections so that we can hold the response + _pendingOAuthConnections.insert(stateUUID, connection); - QEventLoop loop; - connect(tokenReply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + // set the state UUID on the reply so that we can associate the response with the connection later + tokenReply->setProperty(STATE_QUERY_KEY.toLocal8Bit(), stateUUID); - // start the loop for the token request - loop.exec(); + return true; + } else { + connection->respond(HTTPConnection::StatusCode400); - QNetworkReply* profileReply = profileRequestGivenTokenReply(tokenReply); + return true; + } + } else { + return false; + } +} - // stop the loop once the profileReply is complete - connect(profileReply, &QNetworkReply::finished, &loop, &QEventLoop::quit); +HTTPSConnection* DomainServer::connectionFromReplyWithState(QNetworkReply* reply) { + // grab the UUID state property from the reply + QUuid stateUUID = reply->property(STATE_QUERY_KEY.toLocal8Bit()).toUuid(); - // restart the loop for the profile request - loop.exec(); + if (!stateUUID.isNull()) { + return _pendingOAuthConnections.take(stateUUID); + } else { + return nullptr; + } +} +void DomainServer::tokenGrantFinished() { + auto tokenReply = qobject_cast(sender()); + + if (tokenReply) { + if (tokenReply->error() == QNetworkReply::NoError) { + // now that we have a token for this profile, send off a profile request + QNetworkReply* profileReply = profileRequestGivenTokenReply(tokenReply); + + // forward along the state UUID that we kept with the token request + profileReply->setProperty(STATE_QUERY_KEY.toLocal8Bit(), tokenReply->property(STATE_QUERY_KEY.toLocal8Bit())); + + connect(profileReply, &QNetworkReply::finished, this, &DomainServer::profileRequestFinished); + } else { + // the token grant failed, send back a 500 (assuming the connection is still around) + auto connection = connectionFromReplyWithState(tokenReply); + + if (connection) { + connection->respond(HTTPConnection::StatusCode500); + } + } + + tokenReply->deleteLater(); + } +} + +void DomainServer::profileRequestFinished() { + + auto profileReply = qobject_cast(sender()); + + if (profileReply) { + auto connection = connectionFromReplyWithState(profileReply); + + if (connection) { + if (profileReply->error() == QNetworkReply::NoError) { // call helper method to get cookieHeaders Headers cookieHeaders = setupCookieHeadersFromProfileReply(profileReply); connection->respond(HTTPConnection::StatusCode302, QByteArray(), HTTPConnection::DefaultContentType, cookieHeaders); - delete tokenReply; - delete profileReply; - - // we've redirected the user back to our homepage - return true; - + } else { + // the profile request failed, send back a 500 (assuming the connection is still around) + connection->respond(HTTPConnection::StatusCode500); } } - // respond with a 200 code indicating that login is complete - connection->respond(HTTPConnection::StatusCode200); - - return true; - } else { - return false; + profileReply->deleteLater(); } } diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 8851e3380b..4808297c89 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -111,6 +111,9 @@ private slots: void updateDownstreamNodes(); void updateUpstreamNodes(); + void tokenGrantFinished(); + void profileRequestFinished(); + signals: void iceServerChanged(); void userConnected(); @@ -178,6 +181,8 @@ private: void updateReplicationNodes(ReplicationServerDirection direction); + HTTPSConnection* connectionFromReplyWithState(QNetworkReply* reply); + SubnetList _acSubnetWhitelist; std::vector _replicatedUsernames; @@ -235,6 +240,8 @@ private: bool _sendICEServerAddressToMetaverseAPIInProgress { false }; bool _sendICEServerAddressToMetaverseAPIRedo { false }; + + QHash> _pendingOAuthConnections; };