From 1d4bf6e8b1208383021e22f61d859e9b721c482d Mon Sep 17 00:00:00 2001 From: Mehmet Samet Duman Date: Thu, 2 Apr 2026 17:36:40 +0300 Subject: NOISSUE reformat MeshMC to new clang format rules and fixed macOS portability issue and java find issue Signed-off-by: Mehmet Samet Duman --- libraries/katabasis/src/DeviceFlow.cpp | 913 ++++++++++++++++++--------------- 1 file changed, 490 insertions(+), 423 deletions(-) (limited to 'libraries/katabasis/src/DeviceFlow.cpp') diff --git a/libraries/katabasis/src/DeviceFlow.cpp b/libraries/katabasis/src/DeviceFlow.cpp index c40059f44c..d03b3efd8f 100644 --- a/libraries/katabasis/src/DeviceFlow.cpp +++ b/libraries/katabasis/src/DeviceFlow.cpp @@ -42,431 +42,498 @@ #include "JsonResponse.h" -namespace { -// ref: https://tools.ietf.org/html/rfc8628#section-3.2 -// Exception: Google sign-in uses "verification_url" instead of "*_uri" - we'll accept both. -bool hasMandatoryDeviceAuthParams(const QVariantMap& params) +namespace { - if (!params.contains(Katabasis::OAUTH2_DEVICE_CODE)) - return false; - - if (!params.contains(Katabasis::OAUTH2_USER_CODE)) - return false; - - if (!(params.contains(Katabasis::OAUTH2_VERIFICATION_URI) || params.contains(Katabasis::OAUTH2_VERIFICATION_URL))) - return false; - - if (!params.contains(Katabasis::OAUTH2_EXPIRES_IN)) - return false; - - return true; -} - -QByteArray createQueryParameters(const QList ¶meters) { - QByteArray ret; - bool first = true; - for( auto & h: parameters) { - if (first) { - first = false; - } else { - ret.append("&"); - } - ret.append(QUrl::toPercentEncoding(h.name) + "=" + QUrl::toPercentEncoding(h.value)); - } - return ret; -} -} - -namespace Katabasis { - -DeviceFlow::DeviceFlow(Options & opts, Token & token, QObject *parent, QNetworkAccessManager *manager) : QObject(parent), token_(token) { - manager_ = manager ? manager : new QNetworkAccessManager(this); - qRegisterMetaType("QNetworkReply::NetworkError"); - options_ = opts; -} - -bool DeviceFlow::linked() { - return token_.validity != Validity::None; -} -void DeviceFlow::setLinked(bool v) { - qDebug() << "DeviceFlow::setLinked:" << (v? "true": "false"); - token_.validity = v ? Validity::Certain : Validity::None; -} - -void DeviceFlow::updateActivity(Activity activity) + // ref: https://tools.ietf.org/html/rfc8628#section-3.2 + // Exception: Google sign-in uses "verification_url" instead of "*_uri" - + // we'll accept both. + bool hasMandatoryDeviceAuthParams(const QVariantMap& params) + { + if (!params.contains(Katabasis::OAUTH2_DEVICE_CODE)) + return false; + + if (!params.contains(Katabasis::OAUTH2_USER_CODE)) + return false; + + if (!(params.contains(Katabasis::OAUTH2_VERIFICATION_URI) || + params.contains(Katabasis::OAUTH2_VERIFICATION_URL))) + return false; + + if (!params.contains(Katabasis::OAUTH2_EXPIRES_IN)) + return false; + + return true; + } + + QByteArray + createQueryParameters(const QList& parameters) + { + QByteArray ret; + bool first = true; + for (auto& h : parameters) { + if (first) { + first = false; + } else { + ret.append("&"); + } + ret.append(QUrl::toPercentEncoding(h.name) + "=" + + QUrl::toPercentEncoding(h.value)); + } + return ret; + } +} // namespace + +namespace Katabasis { - if(activity_ == activity) { - return; - } - - activity_ = activity; - switch(activity) { - case Katabasis::Activity::Idle: - case Katabasis::Activity::LoggingIn: - case Katabasis::Activity::LoggingOut: - case Katabasis::Activity::Refreshing: - // non-terminal states... - break; - case Katabasis::Activity::FailedSoft: - // terminal state, tokens did not change - break; - case Katabasis::Activity::FailedHard: - case Katabasis::Activity::FailedGone: - // terminal state, tokens are invalid - token_ = Token(); - break; - case Katabasis::Activity::Succeeded: - setLinked(true); - break; - } - emit activityChanged(activity_); -} - -QString DeviceFlow::token() { - return token_.token; -} -void DeviceFlow::setToken(const QString &v) { - token_.token = v; -} - -QVariantMap DeviceFlow::extraTokens() { - return token_.extra; -} - -void DeviceFlow::setExtraTokens(QVariantMap extraTokens) { - token_.extra = extraTokens; -} - -void DeviceFlow::setPollServer(PollServer *server) -{ - if (pollServer_) - pollServer_->deleteLater(); - - pollServer_ = server; -} - -PollServer *DeviceFlow::pollServer() const -{ - return pollServer_; -} - -QVariantMap DeviceFlow::extraRequestParams() -{ - return extraReqParams_; -} - -void DeviceFlow::setExtraRequestParams(const QVariantMap &value) -{ - extraReqParams_ = value; -} -QString DeviceFlow::grantType() -{ - if (!grantType_.isEmpty()) - return grantType_; - - return OAUTH2_GRANT_TYPE_DEVICE; -} - -void DeviceFlow::setGrantType(const QString &value) -{ - grantType_ = value; -} - -// First get the URL and token to display to the user -void DeviceFlow::login() { - qDebug() << "DeviceFlow::link"; - - updateActivity(Activity::LoggingIn); - setLinked(false); - setToken(""); - setExtraTokens(QVariantMap()); - setRefreshToken(QString()); - setExpires(QDateTime()); - - QList parameters; - parameters.append(RequestParameter(OAUTH2_CLIENT_ID, options_.clientIdentifier.toUtf8())); - parameters.append(RequestParameter(OAUTH2_SCOPE, options_.scope.toUtf8())); - QByteArray payload = createQueryParameters(parameters); - - QUrl url(options_.authorizationUrl); - QNetworkRequest deviceRequest(url); - deviceRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - QNetworkReply *tokenReply = manager_->post(deviceRequest, payload); - - connect(tokenReply, &QNetworkReply::finished, this, &DeviceFlow::onDeviceAuthReplyFinished, Qt::QueuedConnection); -} - -// Then, once we get them, present them to the user -void DeviceFlow::onDeviceAuthReplyFinished() -{ - qDebug() << "DeviceFlow::onDeviceAuthReplyFinished"; - QNetworkReply *tokenReply = qobject_cast(sender()); - if (!tokenReply) - { - qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: reply is null"; - return; - } - if (tokenReply->error() == QNetworkReply::NoError) { - QByteArray replyData = tokenReply->readAll(); - - // Dump replyData - // SENSITIVE DATA in RelWithDebInfo or Debug builds - //qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: replyData\n"; - //qDebug() << QString( replyData ); - - QVariantMap params = parseJsonResponse(replyData); - - // Dump tokens - qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: Tokens returned:\n"; - foreach (QString key, params.keys()) { - // SENSITIVE DATA in RelWithDebInfo or Debug builds, so it is truncated first - qDebug() << key << ": "<< params.value( key ).toString(); - } - - // Check for mandatory parameters - if (hasMandatoryDeviceAuthParams(params)) { - qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: Device auth request response"; - - const QString userCode = params.take(OAUTH2_USER_CODE).toString(); - QUrl uri = params.take(OAUTH2_VERIFICATION_URI).toUrl(); - if (uri.isEmpty()) - uri = params.take(OAUTH2_VERIFICATION_URL).toUrl(); - - if (params.contains(OAUTH2_VERIFICATION_URI_COMPLETE)) - emit openBrowser(params.take(OAUTH2_VERIFICATION_URI_COMPLETE).toUrl()); - - bool ok = false; - int expiresIn = params[OAUTH2_EXPIRES_IN].toInt(&ok); - if (!ok) { - qWarning() << "DeviceFlow::startPollServer: No expired_in parameter"; - updateActivity(Activity::FailedHard); - return; - } - - emit showVerificationUriAndCode(uri, userCode, expiresIn); - - startPollServer(params, expiresIn); - } else { - qWarning() << "DeviceFlow::onDeviceAuthReplyFinished: Mandatory parameters missing from response"; - updateActivity(Activity::FailedHard); - } - } - tokenReply->deleteLater(); -} - -// Spin up polling for the user completing the login flow out of band -void DeviceFlow::startPollServer(const QVariantMap ¶ms, int expiresIn) -{ - qDebug() << "DeviceFlow::startPollServer: device_ and user_code expires in" << expiresIn << "seconds"; - - QUrl url(options_.accessTokenUrl); - QNetworkRequest authRequest(url); - authRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - - const QString deviceCode = params[OAUTH2_DEVICE_CODE].toString(); - const QString grantType = grantType_.isEmpty() ? OAUTH2_GRANT_TYPE_DEVICE : grantType_; - - QList parameters; - parameters.append(RequestParameter(OAUTH2_CLIENT_ID, options_.clientIdentifier.toUtf8())); - if ( !options_.clientSecret.isEmpty() ) { - parameters.append(RequestParameter(OAUTH2_CLIENT_SECRET, options_.clientSecret.toUtf8())); - } - parameters.append(RequestParameter(OAUTH2_CODE, deviceCode.toUtf8())); - parameters.append(RequestParameter(OAUTH2_GRANT_TYPE, grantType.toUtf8())); - QByteArray payload = createQueryParameters(parameters); - - PollServer * pollServer = new PollServer(manager_, authRequest, payload, expiresIn, this); - if (params.contains(OAUTH2_INTERVAL)) { - bool ok = false; - int interval = params[OAUTH2_INTERVAL].toInt(&ok); - if (ok) { - pollServer->setInterval(interval); - } - } - connect(pollServer, &PollServer::verificationReceived, this, &DeviceFlow::onVerificationReceived); - connect(pollServer, &PollServer::serverClosed, this, &DeviceFlow::serverHasClosed); - setPollServer(pollServer); - pollServer->startPolling(); -} - -// Once the user completes the flow, update the internal state and report it to observers -void DeviceFlow::onVerificationReceived(const QMap response) { - qDebug() << "DeviceFlow::onVerificationReceived: Emitting closeBrowser()"; - emit closeBrowser(); - - if (response.contains("error")) { - qWarning() << "DeviceFlow::onVerificationReceived: Verification failed:" << response; - updateActivity(Activity::FailedHard); - return; - } - - // Check for mandatory tokens - if (response.contains(OAUTH2_ACCESS_TOKEN)) { - qDebug() << "DeviceFlow::onVerificationReceived: Access token returned for implicit or device flow"; - setToken(response.value(OAUTH2_ACCESS_TOKEN)); - if (response.contains(OAUTH2_EXPIRES_IN)) { - bool ok = false; - int expiresIn = response.value(OAUTH2_EXPIRES_IN).toInt(&ok); - if (ok) { - qDebug() << "DeviceFlow::onVerificationReceived: Token expires in" << expiresIn << "seconds"; - setExpires(QDateTime::currentDateTimeUtc().addSecs(expiresIn)); - } - } - if (response.contains(OAUTH2_REFRESH_TOKEN)) { - setRefreshToken(response.value(OAUTH2_REFRESH_TOKEN)); - } - updateActivity(Activity::Succeeded); - } else { - qWarning() << "DeviceFlow::onVerificationReceived: Access token missing from response for implicit or device flow"; - updateActivity(Activity::FailedHard); - } -} - -// Or if the flow fails or the polling times out, update the internal state with error and report it to observers -void DeviceFlow::serverHasClosed(bool paramsfound) -{ - if ( !paramsfound ) { - // server has probably timed out after receiving first response - updateActivity(Activity::FailedHard); - } - // poll server is not re-used for later auth requests - setPollServer(NULL); -} - -void DeviceFlow::logout() { - qDebug() << "DeviceFlow::unlink"; - updateActivity(Activity::LoggingOut); - // FIXME: implement logout flows... if they exist - token_ = Token(); - updateActivity(Activity::FailedHard); -} - -QDateTime DeviceFlow::expires() { - return token_.notAfter; -} -void DeviceFlow::setExpires(QDateTime v) { - token_.notAfter = v; -} - -QString DeviceFlow::refreshToken() { - return token_.refresh_token; -} - -void DeviceFlow::setRefreshToken(const QString &v) { + DeviceFlow::DeviceFlow(Options& opts, Token& token, QObject* parent, + QNetworkAccessManager* manager) + : QObject(parent), token_(token) + { + manager_ = manager ? manager : new QNetworkAccessManager(this); + qRegisterMetaType( + "QNetworkReply::NetworkError"); + options_ = opts; + } + + bool DeviceFlow::linked() + { + return token_.validity != Validity::None; + } + void DeviceFlow::setLinked(bool v) + { + qDebug() << "DeviceFlow::setLinked:" << (v ? "true" : "false"); + token_.validity = v ? Validity::Certain : Validity::None; + } + + void DeviceFlow::updateActivity(Activity activity) + { + if (activity_ == activity) { + return; + } + + activity_ = activity; + switch (activity) { + case Katabasis::Activity::Idle: + case Katabasis::Activity::LoggingIn: + case Katabasis::Activity::LoggingOut: + case Katabasis::Activity::Refreshing: + // non-terminal states... + break; + case Katabasis::Activity::FailedSoft: + // terminal state, tokens did not change + break; + case Katabasis::Activity::FailedHard: + case Katabasis::Activity::FailedGone: + // terminal state, tokens are invalid + token_ = Token(); + break; + case Katabasis::Activity::Succeeded: + setLinked(true); + break; + } + emit activityChanged(activity_); + } + + QString DeviceFlow::token() + { + return token_.token; + } + void DeviceFlow::setToken(const QString& v) + { + token_.token = v; + } + + QVariantMap DeviceFlow::extraTokens() + { + return token_.extra; + } + + void DeviceFlow::setExtraTokens(QVariantMap extraTokens) + { + token_.extra = extraTokens; + } + + void DeviceFlow::setPollServer(PollServer* server) + { + if (pollServer_) + pollServer_->deleteLater(); + + pollServer_ = server; + } + + PollServer* DeviceFlow::pollServer() const + { + return pollServer_; + } + + QVariantMap DeviceFlow::extraRequestParams() + { + return extraReqParams_; + } + + void DeviceFlow::setExtraRequestParams(const QVariantMap& value) + { + extraReqParams_ = value; + } + + QString DeviceFlow::grantType() + { + if (!grantType_.isEmpty()) + return grantType_; + + return OAUTH2_GRANT_TYPE_DEVICE; + } + + void DeviceFlow::setGrantType(const QString& value) + { + grantType_ = value; + } + + // First get the URL and token to display to the user + void DeviceFlow::login() + { + qDebug() << "DeviceFlow::link"; + + updateActivity(Activity::LoggingIn); + setLinked(false); + setToken(""); + setExtraTokens(QVariantMap()); + setRefreshToken(QString()); + setExpires(QDateTime()); + + QList parameters; + parameters.append(RequestParameter(OAUTH2_CLIENT_ID, + options_.clientIdentifier.toUtf8())); + parameters.append( + RequestParameter(OAUTH2_SCOPE, options_.scope.toUtf8())); + QByteArray payload = createQueryParameters(parameters); + + QUrl url(options_.authorizationUrl); + QNetworkRequest deviceRequest(url); + deviceRequest.setHeader(QNetworkRequest::ContentTypeHeader, + "application/x-www-form-urlencoded"); + QNetworkReply* tokenReply = manager_->post(deviceRequest, payload); + + connect(tokenReply, &QNetworkReply::finished, this, + &DeviceFlow::onDeviceAuthReplyFinished, Qt::QueuedConnection); + } + + // Then, once we get them, present them to the user + void DeviceFlow::onDeviceAuthReplyFinished() + { + qDebug() << "DeviceFlow::onDeviceAuthReplyFinished"; + QNetworkReply* tokenReply = qobject_cast(sender()); + if (!tokenReply) { + qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: reply is null"; + return; + } + if (tokenReply->error() == QNetworkReply::NoError) { + QByteArray replyData = tokenReply->readAll(); + + // Dump replyData + // SENSITIVE DATA in RelWithDebInfo or Debug builds + // qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: replyData\n"; + // qDebug() << QString( replyData ); + + QVariantMap params = parseJsonResponse(replyData); + + // Dump tokens + qDebug() + << "DeviceFlow::onDeviceAuthReplyFinished: Tokens returned:\n"; + foreach (QString key, params.keys()) { + // SENSITIVE DATA in RelWithDebInfo or Debug builds, so it is + // truncated first + qDebug() << key << ": " << params.value(key).toString(); + } + + // Check for mandatory parameters + if (hasMandatoryDeviceAuthParams(params)) { + qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: Device " + "auth request response"; + + const QString userCode = + params.take(OAUTH2_USER_CODE).toString(); + QUrl uri = params.take(OAUTH2_VERIFICATION_URI).toUrl(); + if (uri.isEmpty()) + uri = params.take(OAUTH2_VERIFICATION_URL).toUrl(); + + if (params.contains(OAUTH2_VERIFICATION_URI_COMPLETE)) + emit openBrowser( + params.take(OAUTH2_VERIFICATION_URI_COMPLETE).toUrl()); + + bool ok = false; + int expiresIn = params[OAUTH2_EXPIRES_IN].toInt(&ok); + if (!ok) { + qWarning() << "DeviceFlow::startPollServer: No expired_in " + "parameter"; + updateActivity(Activity::FailedHard); + return; + } + + emit showVerificationUriAndCode(uri, userCode, expiresIn); + + startPollServer(params, expiresIn); + } else { + qWarning() << "DeviceFlow::onDeviceAuthReplyFinished: " + "Mandatory parameters missing from response"; + updateActivity(Activity::FailedHard); + } + } + tokenReply->deleteLater(); + } + + // Spin up polling for the user completing the login flow out of band + void DeviceFlow::startPollServer(const QVariantMap& params, int expiresIn) + { + qDebug() + << "DeviceFlow::startPollServer: device_ and user_code expires in" + << expiresIn << "seconds"; + + QUrl url(options_.accessTokenUrl); + QNetworkRequest authRequest(url); + authRequest.setHeader(QNetworkRequest::ContentTypeHeader, + "application/x-www-form-urlencoded"); + + const QString deviceCode = params[OAUTH2_DEVICE_CODE].toString(); + const QString grantType = + grantType_.isEmpty() ? OAUTH2_GRANT_TYPE_DEVICE : grantType_; + + QList parameters; + parameters.append(RequestParameter(OAUTH2_CLIENT_ID, + options_.clientIdentifier.toUtf8())); + if (!options_.clientSecret.isEmpty()) { + parameters.append(RequestParameter(OAUTH2_CLIENT_SECRET, + options_.clientSecret.toUtf8())); + } + parameters.append(RequestParameter(OAUTH2_CODE, deviceCode.toUtf8())); + parameters.append( + RequestParameter(OAUTH2_GRANT_TYPE, grantType.toUtf8())); + QByteArray payload = createQueryParameters(parameters); + + PollServer* pollServer = + new PollServer(manager_, authRequest, payload, expiresIn, this); + if (params.contains(OAUTH2_INTERVAL)) { + bool ok = false; + int interval = params[OAUTH2_INTERVAL].toInt(&ok); + if (ok) { + pollServer->setInterval(interval); + } + } + connect(pollServer, &PollServer::verificationReceived, this, + &DeviceFlow::onVerificationReceived); + connect(pollServer, &PollServer::serverClosed, this, + &DeviceFlow::serverHasClosed); + setPollServer(pollServer); + pollServer->startPolling(); + } + + // Once the user completes the flow, update the internal state and report it + // to observers + void + DeviceFlow::onVerificationReceived(const QMap response) + { + qDebug() + << "DeviceFlow::onVerificationReceived: Emitting closeBrowser()"; + emit closeBrowser(); + + if (response.contains("error")) { + qWarning() + << "DeviceFlow::onVerificationReceived: Verification failed:" + << response; + updateActivity(Activity::FailedHard); + return; + } + + // Check for mandatory tokens + if (response.contains(OAUTH2_ACCESS_TOKEN)) { + qDebug() << "DeviceFlow::onVerificationReceived: Access token " + "returned for implicit or device flow"; + setToken(response.value(OAUTH2_ACCESS_TOKEN)); + if (response.contains(OAUTH2_EXPIRES_IN)) { + bool ok = false; + int expiresIn = response.value(OAUTH2_EXPIRES_IN).toInt(&ok); + if (ok) { + qDebug() << "DeviceFlow::onVerificationReceived: Token " + "expires in" + << expiresIn << "seconds"; + setExpires( + QDateTime::currentDateTimeUtc().addSecs(expiresIn)); + } + } + if (response.contains(OAUTH2_REFRESH_TOKEN)) { + setRefreshToken(response.value(OAUTH2_REFRESH_TOKEN)); + } + updateActivity(Activity::Succeeded); + } else { + qWarning() << "DeviceFlow::onVerificationReceived: Access token " + "missing from response for implicit or device flow"; + updateActivity(Activity::FailedHard); + } + } + + // Or if the flow fails or the polling times out, update the internal state + // with error and report it to observers + void DeviceFlow::serverHasClosed(bool paramsfound) + { + if (!paramsfound) { + // server has probably timed out after receiving first response + updateActivity(Activity::FailedHard); + } + // poll server is not re-used for later auth requests + setPollServer(NULL); + } + + void DeviceFlow::logout() + { + qDebug() << "DeviceFlow::unlink"; + updateActivity(Activity::LoggingOut); + // FIXME: implement logout flows... if they exist + token_ = Token(); + updateActivity(Activity::FailedHard); + } + + QDateTime DeviceFlow::expires() + { + return token_.notAfter; + } + void DeviceFlow::setExpires(QDateTime v) + { + token_.notAfter = v; + } + + QString DeviceFlow::refreshToken() + { + return token_.refresh_token; + } + + void DeviceFlow::setRefreshToken(const QString& v) + { #ifndef NDEBUG - qDebug() << "DeviceFlow::setRefreshToken" << v << "..."; + qDebug() << "DeviceFlow::setRefreshToken" << v << "..."; #endif - token_.refresh_token = v; -} - -namespace { -QByteArray buildRequestBody(const QMap ¶meters) { - QByteArray body; - bool first = true; - foreach (QString key, parameters.keys()) { - if (first) { - first = false; - } else { - body.append("&"); - } - QString value = parameters.value(key); - body.append(QUrl::toPercentEncoding(key) + QString("=").toUtf8() + QUrl::toPercentEncoding(value)); - } - return body; -} -} - -bool DeviceFlow::refresh() { - qDebug() << "DeviceFlow::refresh: Token: ..." << refreshToken().right(7); - - updateActivity(Activity::Refreshing); - - if (refreshToken().isEmpty()) { - qWarning() << "DeviceFlow::refresh: No refresh token"; - onRefreshError(QNetworkReply::AuthenticationRequiredError, nullptr); - return false; - } - if (options_.accessTokenUrl.isEmpty()) { - qWarning() << "DeviceFlow::refresh: Refresh token URL not set"; - onRefreshError(QNetworkReply::AuthenticationRequiredError, nullptr); - return false; - } - - QNetworkRequest refreshRequest(options_.accessTokenUrl); - refreshRequest.setHeader(QNetworkRequest::ContentTypeHeader, MIME_TYPE_XFORM); - QMap parameters; - parameters.insert(OAUTH2_CLIENT_ID, options_.clientIdentifier); - if ( !options_.clientSecret.isEmpty() ) { - parameters.insert(OAUTH2_CLIENT_SECRET, options_.clientSecret); - } - parameters.insert(OAUTH2_REFRESH_TOKEN, refreshToken()); - parameters.insert(OAUTH2_GRANT_TYPE, OAUTH2_REFRESH_TOKEN); - - QByteArray data = buildRequestBody(parameters); - QNetworkReply *refreshReply = manager_->post(refreshRequest, data); - timedReplies_.add(refreshReply); - connect(refreshReply, &QNetworkReply::finished, this, &DeviceFlow::onRefreshFinished, Qt::QueuedConnection); - return true; -} - -void DeviceFlow::onRefreshFinished() { - QNetworkReply *refreshReply = qobject_cast(sender()); - - auto networkError = refreshReply->error(); - if (networkError == QNetworkReply::NoError) { - QByteArray reply = refreshReply->readAll(); - QVariantMap tokens = parseJsonResponse(reply); - setToken(tokens.value(OAUTH2_ACCESS_TOKEN).toString()); - setExpires(QDateTime::currentDateTimeUtc().addSecs(tokens.value(OAUTH2_EXPIRES_IN).toInt())); - QString refreshToken = tokens.value(OAUTH2_REFRESH_TOKEN).toString(); - if(!refreshToken.isEmpty()) { - setRefreshToken(refreshToken); - } - else { - qDebug() << "No new refresh token. Keep the old one."; - } - timedReplies_.remove(refreshReply); - refreshReply->deleteLater(); - updateActivity(Activity::Succeeded); - qDebug() << "New token expires in" << expires() << "seconds"; - } else { - // FIXME: differentiate the error more here - onRefreshError(networkError, refreshReply); - } -} - -void DeviceFlow::onRefreshError(QNetworkReply::NetworkError error, QNetworkReply *refreshReply) { - QString errorString = "No Reply"; - if(refreshReply) { - timedReplies_.remove(refreshReply); - errorString = refreshReply->errorString(); - } - - switch (error) - { - // used for invalid credentials and similar errors. Fall through. - case QNetworkReply::AuthenticationRequiredError: - case QNetworkReply::ContentAccessDenied: - case QNetworkReply::ContentOperationNotPermittedError: - case QNetworkReply::ProtocolInvalidOperationError: - updateActivity(Activity::FailedHard); - break; - case QNetworkReply::ContentGoneError: { - updateActivity(Activity::FailedGone); - break; - } - case QNetworkReply::TimeoutError: - case QNetworkReply::OperationCanceledError: - case QNetworkReply::SslHandshakeFailedError: - default: - updateActivity(Activity::FailedSoft); - return; - } - if(refreshReply) { - refreshReply->deleteLater(); - } - qDebug() << "DeviceFlow::onRefreshFinished: Error" << (int)error << " - " << errorString; -} - -} + token_.refresh_token = v; + } + + namespace + { + QByteArray buildRequestBody(const QMap& parameters) + { + QByteArray body; + bool first = true; + foreach (QString key, parameters.keys()) { + if (first) { + first = false; + } else { + body.append("&"); + } + QString value = parameters.value(key); + body.append(QUrl::toPercentEncoding(key) + + QString("=").toUtf8() + + QUrl::toPercentEncoding(value)); + } + return body; + } + } // namespace + + bool DeviceFlow::refresh() + { + qDebug() << "DeviceFlow::refresh: Token: ..." + << refreshToken().right(7); + + updateActivity(Activity::Refreshing); + + if (refreshToken().isEmpty()) { + qWarning() << "DeviceFlow::refresh: No refresh token"; + onRefreshError(QNetworkReply::AuthenticationRequiredError, nullptr); + return false; + } + if (options_.accessTokenUrl.isEmpty()) { + qWarning() << "DeviceFlow::refresh: Refresh token URL not set"; + onRefreshError(QNetworkReply::AuthenticationRequiredError, nullptr); + return false; + } + + QNetworkRequest refreshRequest(options_.accessTokenUrl); + refreshRequest.setHeader(QNetworkRequest::ContentTypeHeader, + MIME_TYPE_XFORM); + QMap parameters; + parameters.insert(OAUTH2_CLIENT_ID, options_.clientIdentifier); + if (!options_.clientSecret.isEmpty()) { + parameters.insert(OAUTH2_CLIENT_SECRET, options_.clientSecret); + } + parameters.insert(OAUTH2_REFRESH_TOKEN, refreshToken()); + parameters.insert(OAUTH2_GRANT_TYPE, OAUTH2_REFRESH_TOKEN); + + QByteArray data = buildRequestBody(parameters); + QNetworkReply* refreshReply = manager_->post(refreshRequest, data); + timedReplies_.add(refreshReply); + connect(refreshReply, &QNetworkReply::finished, this, + &DeviceFlow::onRefreshFinished, Qt::QueuedConnection); + return true; + } + + void DeviceFlow::onRefreshFinished() + { + QNetworkReply* refreshReply = qobject_cast(sender()); + + auto networkError = refreshReply->error(); + if (networkError == QNetworkReply::NoError) { + QByteArray reply = refreshReply->readAll(); + QVariantMap tokens = parseJsonResponse(reply); + setToken(tokens.value(OAUTH2_ACCESS_TOKEN).toString()); + setExpires(QDateTime::currentDateTimeUtc().addSecs( + tokens.value(OAUTH2_EXPIRES_IN).toInt())); + QString refreshToken = + tokens.value(OAUTH2_REFRESH_TOKEN).toString(); + if (!refreshToken.isEmpty()) { + setRefreshToken(refreshToken); + } else { + qDebug() << "No new refresh token. Keep the old one."; + } + timedReplies_.remove(refreshReply); + refreshReply->deleteLater(); + updateActivity(Activity::Succeeded); + qDebug() << "New token expires in" << expires() << "seconds"; + } else { + // FIXME: differentiate the error more here + onRefreshError(networkError, refreshReply); + } + } + + void DeviceFlow::onRefreshError(QNetworkReply::NetworkError error, + QNetworkReply* refreshReply) + { + QString errorString = "No Reply"; + if (refreshReply) { + timedReplies_.remove(refreshReply); + errorString = refreshReply->errorString(); + } + + switch (error) { + // used for invalid credentials and similar errors. Fall through. + case QNetworkReply::AuthenticationRequiredError: + case QNetworkReply::ContentAccessDenied: + case QNetworkReply::ContentOperationNotPermittedError: + case QNetworkReply::ProtocolInvalidOperationError: + updateActivity(Activity::FailedHard); + break; + case QNetworkReply::ContentGoneError: { + updateActivity(Activity::FailedGone); + break; + } + case QNetworkReply::TimeoutError: + case QNetworkReply::OperationCanceledError: + case QNetworkReply::SslHandshakeFailedError: + default: + updateActivity(Activity::FailedSoft); + return; + } + if (refreshReply) { + refreshReply->deleteLater(); + } + qDebug() << "DeviceFlow::onRefreshFinished: Error" << (int)error + << " - " << errorString; + } + +} // namespace Katabasis -- cgit 0.0.5-2-1-g0f52