| 340 | } |
| 341 | |
| 342 | void OAuthService::exchangeCodeForTokens(const QString &code, int retryCount) |
| 343 | { |
| 344 | auto *nam = new QNetworkAccessManager(this); |
| 345 | QPointer<OAuthService> self(this); |
| 346 | |
| 347 | QUrl tokenUrl; |
| 348 | QUrlQuery body; |
| 349 | |
| 350 | if (m_provider == "gdrive") { |
| 351 | tokenUrl.setUrl(GDRIVE_TOKEN_URL); |
| 352 | body.addQueryItem("code", code); |
| 353 | body.addQueryItem("client_id", GDRIVE_CLIENT_ID); |
| 354 | body.addQueryItem("client_secret", GDRIVE_CLIENT_SECRET); |
| 355 | body.addQueryItem("redirect_uri", m_redirectUri); |
| 356 | body.addQueryItem("grant_type", "authorization_code"); |
| 357 | body.addQueryItem("code_verifier", m_codeVerifier); |
| 358 | } else if (m_provider == "onedrive") { |
| 359 | tokenUrl.setUrl(ONEDRIVE_TOKEN_URL); |
| 360 | body.addQueryItem("code", code); |
| 361 | body.addQueryItem("client_id", ONEDRIVE_CLIENT_ID); |
| 362 | body.addQueryItem("client_secret", ONEDRIVE_CLIENT_SECRET); |
| 363 | body.addQueryItem("redirect_uri", m_redirectUri); |
| 364 | body.addQueryItem("grant_type", "authorization_code"); |
| 365 | body.addQueryItem("scope", ONEDRIVE_SCOPE); |
| 366 | body.addQueryItem("code_verifier", m_codeVerifier); |
| 367 | } |
| 368 | |
| 369 | QNetworkRequest req(tokenUrl); |
| 370 | req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); |
| 371 | |
| 372 | QNetworkReply *reply = nam->post(req, body.toString(QUrl::FullyEncoded).replace("%20", "+").toUtf8()); |
| 373 | |
| 374 | connect(reply, &QNetworkReply::finished, this, [this, self, reply, nam, code, retryCount]() { |
| 375 | reply->setParent(nullptr); |
| 376 | reply->deleteLater(); |
| 377 | nam->deleteLater(); |
| 378 | |
| 379 | if (!self) return; |
| 380 | |
| 381 | if (reply->error() != QNetworkReply::NoError) { |
| 382 | // Retry once -- broken IPv6 fails instantly, retry gives IPv4 a chance |
| 383 | if (retryCount < 2) { |
| 384 | qDebug() << "[OAuth] Token exchange failed, retrying:" << reply->errorString(); |
| 385 | emit statusMessage("Retrying token exchange..."); |
| 386 | QTimer::singleShot(500, this, [this, code, retryCount]() { |
| 387 | exchangeCodeForTokens(code, retryCount + 1); |
| 388 | }); |
| 389 | return; |
| 390 | } |
| 391 | emit authFailed(m_provider, "Token exchange failed: " + reply->errorString()); |
| 392 | cancel(); |
| 393 | return; |
| 394 | } |
| 395 | |
| 396 | QByteArray responseData = reply->readAll(); |
| 397 | QJsonDocument doc = QJsonDocument::fromJson(responseData); |
| 398 | if (!doc.isObject()) { |
| 399 | emit authFailed(m_provider, "Invalid token response"); |
nothing calls this directly
no test coverage detected