mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Withings/Nokia use OAth2 for authorisation
This commit is contained in:
@@ -111,7 +111,9 @@ BodyMeasuresDownload::BodyMeasuresDownload(Context *context) : context(context)
|
||||
QString strToken =appsettings->cvalue(context->athlete->cyclist, GC_WITHINGS_TOKEN, "").toString();
|
||||
QString strSecret= appsettings->cvalue(context->athlete->cyclist, GC_WITHINGS_SECRET, "").toString();
|
||||
|
||||
if (strToken == "" && strSecret == "") {
|
||||
QString strToken2 =appsettings->cvalue(context->athlete->cyclist, GC_NOKIA_TOKEN, "").toString();
|
||||
|
||||
if (strToken2 =="" && strToken == "" && strSecret == "") {
|
||||
downloadWithings->setEnabled(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -180,29 +180,12 @@ OAuthDialog::OAuthDialog(Context *context, OAuthSite site, CloudService *service
|
||||
|
||||
} else if (site == WITHINGS) {
|
||||
|
||||
// Withings is the only service that uses KQOauth for now.
|
||||
|
||||
#ifdef GC_HAVE_KQOAUTH
|
||||
oauthRequest = new KQOAuthRequest;
|
||||
oauthManager = new KQOAuthManager(this);
|
||||
|
||||
connect(oauthManager, SIGNAL(temporaryTokenReceived(QString,QString)), this, SLOT(onTemporaryTokenReceived(QString, QString)));
|
||||
connect(oauthManager, SIGNAL(authorizationReceived(QString,QString)), this, SLOT( onAuthorizationReceived(QString, QString)));
|
||||
connect(oauthManager, SIGNAL(accessTokenReceived(QString,QString)), this, SLOT(onAccessTokenReceived(QString,QString)));
|
||||
connect(oauthManager, SIGNAL(requestReady(QByteArray)), this, SLOT(onRequestReady(QByteArray)));
|
||||
connect(oauthManager, SIGNAL(authorizationPageRequested(QUrl)), this, SLOT(onAuthorizationPageRequested(QUrl)));
|
||||
|
||||
oauthRequest->initRequest(KQOAuthRequest::TemporaryCredentials, QUrl("https://oauth.withings.com/account/request_token"));
|
||||
//oauthRequest->setEnableDebugOutput(true);
|
||||
oauthRequest->setHttpMethod(KQOAuthRequest::GET);
|
||||
oauthRequest->setConsumerKey(GC_WITHINGS_CONSUMER_KEY);
|
||||
oauthRequest->setConsumerSecretKey(GC_WITHINGS_CONSUMER_SECRET);
|
||||
//oauthRequest->setCallbackUrl(QUrl("http://www.goldencheetah.org"));
|
||||
oauthManager->setHandleUserAuthorization(true); // false to use callback
|
||||
oauthManager->setHandleAuthorizationPageOpening(false);
|
||||
|
||||
oauthManager->executeRequest(oauthRequest);
|
||||
#endif
|
||||
urlstr = QString("https://account.health.nokia.com/oauth2_user/authorize2?");
|
||||
urlstr.append("redirect_uri=http://www.goldencheetah.org&");
|
||||
urlstr.append("scope=user.info,user.metrics&");
|
||||
urlstr.append("response_type=code&");
|
||||
urlstr.append("state=xyzzy&");
|
||||
urlstr.append("client_id=").append(GC_NOKIA_CLIENT_ID);
|
||||
|
||||
} else if (site == XERT) {
|
||||
urlChanged(QUrl("http://www.goldencheetah.org/?code=0"));
|
||||
@@ -211,7 +194,7 @@ OAuthDialog::OAuthDialog(Context *context, OAuthSite site, CloudService *service
|
||||
//
|
||||
// STEP 1: LOGIN AND AUTHORISE THE APPLICATION
|
||||
//
|
||||
if (site == DROPBOX || site == STRAVA || site == CYCLING_ANALYTICS || site == POLAR || site == SPORTTRACKS || site == GOOGLE_DRIVE || site == KENTUNI || site == TODAYSPLAN) {
|
||||
if (site == DROPBOX || site == STRAVA || site == CYCLING_ANALYTICS || site == POLAR || site == SPORTTRACKS || site == GOOGLE_DRIVE || site == KENTUNI || site == TODAYSPLAN || site == WITHINGS) {
|
||||
|
||||
url = QUrl(urlstr);
|
||||
view->setUrl(url);
|
||||
@@ -230,113 +213,6 @@ OAuthDialog::onSslErrors(QNetworkReply *reply, const QList<QSslError>&)
|
||||
}
|
||||
|
||||
|
||||
#ifdef GC_HAVE_KQOAUTH
|
||||
|
||||
//
|
||||
// KQOauth call backs
|
||||
//
|
||||
void
|
||||
OAuthDialog::onTemporaryTokenReceived(QString, QString)
|
||||
{
|
||||
//qDebug() << "onTemporaryTokenReceived";
|
||||
QUrl userAuthURL;
|
||||
|
||||
if (site == WITHINGS) {
|
||||
userAuthURL = "https://oauth.withings.com/account/authorize";
|
||||
}
|
||||
|
||||
if(oauthManager->lastError() == KQOAuthManager::NoError) {
|
||||
oauthManager->getUserAuthorization(userAuthURL);
|
||||
} else
|
||||
qDebug() << "error" << oauthManager->lastError();
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
OAuthDialog::onAuthorizationReceived(QString, QString)
|
||||
{
|
||||
//qDebug() << "Authorization token received: " << token << verifier;
|
||||
|
||||
if (site == WITHINGS) {
|
||||
oauthManager->getUserAccessTokens(QUrl("https://oauth.withings.com/account/access_token"));
|
||||
}
|
||||
|
||||
if(oauthManager->lastError() != KQOAuthManager::NoError) {
|
||||
QString error = QString(tr("Error fetching OAuth credentials - Endpoint: /oauth/access_token"));
|
||||
QMessageBox oautherr(QMessageBox::Critical, tr("Authorization Error"), error);
|
||||
oautherr.exec();
|
||||
accept();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OAuthDialog::onAccessTokenReceived(QString token, QString tokenSecret)
|
||||
{
|
||||
//qDebug() << "Access token received: " << token << tokenSecret;
|
||||
|
||||
QString info;
|
||||
if (site == WITHINGS) {
|
||||
service->setSetting(GC_WITHINGS_TOKEN, token);
|
||||
service->setSetting(GC_WITHINGS_SECRET, tokenSecret);
|
||||
appsettings->setCValue(context->athlete->cyclist, GC_NOKIA_REFRESH_TOKEN, "");
|
||||
|
||||
info = QString(tr("Nokia Health (Withings) authorization was successful."));
|
||||
}
|
||||
|
||||
|
||||
QMessageBox information(QMessageBox::Information, tr("Information"), info);
|
||||
information.exec();
|
||||
accept();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
OAuthDialog::onAuthorizedRequestDone() {} // request sent - do nothing
|
||||
|
||||
void
|
||||
OAuthDialog::onRequestReady(QByteArray response)
|
||||
{
|
||||
//qDebug() << "Response received: " << response;
|
||||
|
||||
QString r = response;
|
||||
if (r.contains("\"errors\"", Qt::CaseInsensitive)) {
|
||||
|
||||
QMessageBox oautherr(QMessageBox::Critical, tr("Error in authorization"),
|
||||
tr("There was an error during authorization. Please check the error description."));
|
||||
oautherr.setDetailedText(r); // probably blank
|
||||
oautherr.exec();
|
||||
|
||||
} else {
|
||||
|
||||
if (site == WITHINGS) {
|
||||
|
||||
QString userid;
|
||||
|
||||
#if QT_VERSION > 0x050000
|
||||
QUrlQuery params;
|
||||
params.setQuery(response);
|
||||
#else
|
||||
QUrl params;
|
||||
params.setEncodedQuery(response);
|
||||
#endif
|
||||
userid = params.queryItemValue("userid");
|
||||
|
||||
if (userid.isEmpty() == false) {
|
||||
service->setSetting(GC_WIUSER, userid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OAuthDialog::onAuthorizationPageRequested(QUrl url) {
|
||||
// open Authorization page in view
|
||||
view->setUrl(url);
|
||||
|
||||
}
|
||||
#endif // KQOAuth callbacks used by Withings only
|
||||
|
||||
|
||||
//
|
||||
// STEP 2: AUTHORISATION REDIRECT WITH TEMPORARY CODE
|
||||
//
|
||||
@@ -351,7 +227,7 @@ OAuthDialog::urlChanged(const QUrl &url)
|
||||
QString authheader;
|
||||
|
||||
// sites that use this scheme
|
||||
if (site == DROPBOX || site == STRAVA || site == CYCLING_ANALYTICS || site == TODAYSPLAN || site == POLAR || site == SPORTTRACKS || site == XERT) {
|
||||
if (site == DROPBOX || site == STRAVA || site == CYCLING_ANALYTICS || site == TODAYSPLAN || site == POLAR || site == SPORTTRACKS || site == XERT || site == WITHINGS) {
|
||||
|
||||
if (url.toString().startsWith("http://www.goldencheetah.org/?state=&code=") ||
|
||||
url.toString().contains("blank.html?code=") ||
|
||||
@@ -442,6 +318,14 @@ OAuthDialog::urlChanged(const QUrl &url)
|
||||
params.addQueryItem("grant_type", "password");
|
||||
|
||||
authheader = QString("%1:%1").arg("xert_public");
|
||||
} else if (site == WITHINGS) {
|
||||
|
||||
urlstr = QString("https://account.health.nokia.com/oauth2/token?");
|
||||
params.addQueryItem("client_id", GC_NOKIA_CLIENT_ID);
|
||||
params.addQueryItem("client_secret", GC_NOKIA_CLIENT_SECRET);
|
||||
params.addQueryItem("redirect_uri","http://www.goldencheetah.org");
|
||||
params.addQueryItem("grant_type", "authorization_code");
|
||||
|
||||
}
|
||||
|
||||
// all services will need us to send the temporary code received
|
||||
@@ -713,6 +597,19 @@ OAuthDialog::networkRequestFinished(QNetworkReply *reply)
|
||||
|
||||
service->message = "Xert authorization was successful.";
|
||||
|
||||
} else if (site == WITHINGS) {
|
||||
|
||||
service->setSetting(GC_NOKIA_TOKEN, access_token);
|
||||
service->setSetting(GC_NOKIA_REFRESH_TOKEN, refresh_token);
|
||||
|
||||
// We have to ask for userid ?
|
||||
|
||||
|
||||
|
||||
QString info = QString(tr("Withings/Nokia authorization was successful."));
|
||||
QMessageBox information(QMessageBox::Information, tr("Information"), info);
|
||||
information.exec();
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
@@ -85,15 +85,6 @@ private slots:
|
||||
void networkRequestFinished(QNetworkReply *reply);
|
||||
void onSslErrors(QNetworkReply *reply, const QList<QSslError>&error);
|
||||
|
||||
#ifdef GC_HAVE_KQOAUTH
|
||||
void onTemporaryTokenReceived(QString, QString);
|
||||
void onAuthorizationReceived(QString, QString);
|
||||
void onAccessTokenReceived(QString token, QString tokenSecret);
|
||||
void onAuthorizedRequestDone();
|
||||
void onRequestReady(QByteArray response);
|
||||
void onAuthorizationPageRequested (QUrl pageUrl);
|
||||
#endif
|
||||
|
||||
|
||||
private:
|
||||
Context *context;
|
||||
|
||||
@@ -24,9 +24,8 @@
|
||||
Withings::Withings(Context *context) : CloudService(context), context(context) {
|
||||
|
||||
// config
|
||||
settings.insert(OAuthToken, GC_WITHINGS_TOKEN);
|
||||
settings.insert(Local1, GC_WITHINGS_SECRET);
|
||||
settings.insert(Local2, GC_WIUSER);
|
||||
settings.insert(OAuthToken, GC_NOKIA_TOKEN);
|
||||
settings.insert(Local1, GC_NOKIA_REFRESH_TOKEN);
|
||||
}
|
||||
|
||||
static bool addWithings() {
|
||||
|
||||
@@ -30,6 +30,28 @@
|
||||
#include <kqoauthrequest.h>
|
||||
#endif
|
||||
|
||||
#ifndef WITHINGS_DEBUG
|
||||
#define WITHINGS_DEBUG true
|
||||
#endif
|
||||
#ifdef Q_CC_MSVC
|
||||
#define printd(fmt, ...) do { \
|
||||
if (WITHINGS_DEBUG) { \
|
||||
printf("[%s:%d %s] " fmt , __FILE__, __LINE__, \
|
||||
__FUNCTION__, __VA_ARGS__); \
|
||||
fflush(stdout); \
|
||||
} \
|
||||
} while(0)
|
||||
#else
|
||||
#define printd(fmt, args...) \
|
||||
do { \
|
||||
if (WITHINGS_DEBUG) { \
|
||||
printf("[%s:%d %s] " fmt , __FILE__, __LINE__, \
|
||||
__FUNCTION__, ##args); \
|
||||
fflush(stdout); \
|
||||
} \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
WithingsDownload::WithingsDownload(Context *context) : context(context)
|
||||
{
|
||||
nam = new QNetworkAccessManager(this);
|
||||
@@ -75,7 +97,8 @@ WithingsDownload::getBodyMeasures(QString &error, QDateTime from, QDateTime to,
|
||||
if((strToken.isEmpty() || strSecret.isEmpty() ||
|
||||
strToken == "" || strToken == "0" ||
|
||||
strSecret == "" || strSecret == "0" ) &&
|
||||
(strOldKey.isEmpty() || strOldKey == "" || strOldKey == "0" ))
|
||||
(strOldKey.isEmpty() || strOldKey == "" || strOldKey == "0" ) &&
|
||||
(strNokiaRefreshToken.isEmpty() || strNokiaRefreshToken == "" || strNokiaRefreshToken == "0" ))
|
||||
{
|
||||
#ifdef Q_OS_MACX
|
||||
#define GC_PREF tr("Golden Cheetah->Preferences")
|
||||
@@ -88,10 +111,11 @@ WithingsDownload::getBodyMeasures(QString &error, QDateTime from, QDateTime to,
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!strToken.isEmpty() &&! strSecret.isEmpty() &&
|
||||
if(!strNokiaRefreshToken.isEmpty() ||
|
||||
(!strToken.isEmpty() &&! strSecret.isEmpty() &&
|
||||
strToken != "" && strToken != "0" &&
|
||||
strSecret != "" && strSecret != "0" ) {
|
||||
qDebug() << "OAuth 2.0 API";
|
||||
strSecret != "" && strSecret != "0" )) {
|
||||
printd("OAuth 2.0 API");
|
||||
|
||||
#if QT_VERSION > 0x050000
|
||||
QUrlQuery postData;
|
||||
@@ -99,21 +123,15 @@ WithingsDownload::getBodyMeasures(QString &error, QDateTime from, QDateTime to,
|
||||
QUrl postData;
|
||||
#endif
|
||||
|
||||
//appsettings->setCValue(context->athlete->cyclist, GC_NOKIA_REFRESH_TOKEN, "");
|
||||
qDebug() << "refresh_token" << appsettings->cvalue(context->athlete->cyclist, GC_NOKIA_REFRESH_TOKEN, QString("%1:%2").arg(strToken).arg(strSecret));
|
||||
|
||||
QString refresh_token = appsettings->cvalue(context->athlete->cyclist, GC_NOKIA_REFRESH_TOKEN).toString();
|
||||
if (refresh_token.isEmpty())
|
||||
refresh_token = QString("%1:%2").arg(strToken).arg(strSecret);
|
||||
qDebug() << "refresh_token" << refresh_token;
|
||||
|
||||
postData.addQueryItem("grant_type", "refresh_token");
|
||||
postData.addQueryItem("client_id", GC_NOKIA_CLIENT_ID );
|
||||
postData.addQueryItem("client_secret", GC_NOKIA_CLIENT_SECRET );
|
||||
postData.addQueryItem("refresh_token", refresh_token );
|
||||
|
||||
|
||||
qDebug() << appsettings->cvalue(context->athlete->cyclist, GC_NOKIA_REFRESH_TOKEN, QString("%1:%2").arg(strToken).arg(strSecret)).toString();
|
||||
QUrl url = QUrl( "https://account.withings.com/oauth2/token" );
|
||||
|
||||
emit downloadStarted(100);
|
||||
@@ -122,11 +140,13 @@ WithingsDownload::getBodyMeasures(QString &error, QDateTime from, QDateTime to,
|
||||
QNetworkRequest request(url);
|
||||
request.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||
nam->post(request, postData.toString(QUrl::FullyEncoded).toUtf8());
|
||||
printd("url %s %s", url.toString().toStdString().c_str(), postData.toString().toStdString().c_str());
|
||||
|
||||
// blocking request
|
||||
loop.exec(); // we go on after receiving the data in SLOT(onRequestReady(QByteArray))
|
||||
|
||||
qDebug() << "response" << response;
|
||||
printd("response: %s", response.toStdString().c_str());
|
||||
|
||||
|
||||
if (response.contains("\"access_token\"", Qt::CaseInsensitive))
|
||||
{
|
||||
@@ -135,9 +155,13 @@ WithingsDownload::getBodyMeasures(QString &error, QDateTime from, QDateTime to,
|
||||
|
||||
access_token = migrateJson.object()["access_token"].toString();
|
||||
QString refresh_token = migrateJson.object()["refresh_token"].toString();
|
||||
QString userid = QString("%1").arg(migrateJson.object()["userid"].toInt());
|
||||
|
||||
|
||||
if (access_token != "") appsettings->setCValue(context->athlete->cyclist, GC_NOKIA_TOKEN, access_token);
|
||||
if (refresh_token != "") appsettings->setCValue(context->athlete->cyclist, GC_NOKIA_REFRESH_TOKEN, refresh_token);
|
||||
if (userid != "") appsettings->setCValue(context->athlete->cyclist, GC_WIUSER, userid);
|
||||
|
||||
|
||||
#if QT_VERSION > 0x050000
|
||||
QUrlQuery params;
|
||||
@@ -147,14 +171,20 @@ WithingsDownload::getBodyMeasures(QString &error, QDateTime from, QDateTime to,
|
||||
|
||||
emit downloadStarted(100);
|
||||
|
||||
params.addQueryItem("userid", appsettings->cvalue(context->athlete->cyclist, GC_WIUSER, "").toString());
|
||||
params.addQueryItem("action", "getmeas");
|
||||
//params.addQueryItem("userid", userid);
|
||||
params.addQueryItem("access_token", access_token);
|
||||
params.addQueryItem("startdate", QString::number(from.toMSecsSinceEpoch()/1000));
|
||||
params.addQueryItem("enddate", QString::number(to.toMSecsSinceEpoch()/1000));
|
||||
|
||||
|
||||
QUrl url = QUrl( "https://api.health.nokia.com/measure?" + params.toString() );
|
||||
|
||||
qDebug() << "URL used: " << url.url();
|
||||
printd("URL: %s", url.url().toStdString().c_str());
|
||||
|
||||
QNetworkRequest request(url);
|
||||
//request.setRawHeader("Authorization", QString("Bearer %1").arg(access_token).toLatin1());
|
||||
|
||||
nam->get(request);
|
||||
|
||||
emit downloadProgress(50);
|
||||
@@ -171,7 +201,7 @@ WithingsDownload::getBodyMeasures(QString &error, QDateTime from, QDateTime to,
|
||||
if(access_token.isEmpty() && !strToken.isEmpty() &&! strSecret.isEmpty() &&
|
||||
strToken != "" && strToken != "0" &&
|
||||
strSecret != "" && strSecret != "0" ) {
|
||||
qDebug() << "OAuth 1.0 API";
|
||||
printd("OAuth 1.0 API");
|
||||
|
||||
#ifdef GC_HAVE_KQOAUTH
|
||||
oauthRequest->initRequest(KQOAuthRequest::AuthorizedRequest, QUrl("http://wbsapi.withings.net/measure"));
|
||||
@@ -219,7 +249,7 @@ WithingsDownload::getBodyMeasures(QString &error, QDateTime from, QDateTime to,
|
||||
params2.addQueryItem("enddate", QString::number(to.toMSecsSinceEpoch()/1000));
|
||||
|
||||
QUrl url = QUrl( "https://wbsapi.withings.net/measure?" + params2.toString() );
|
||||
//qDebug() << "URL used: " << url.url();
|
||||
printd("URL : ", url.url().toStdString().c_str());
|
||||
|
||||
emit downloadStarted(100);
|
||||
|
||||
@@ -234,7 +264,8 @@ WithingsDownload::getBodyMeasures(QString &error, QDateTime from, QDateTime to,
|
||||
|
||||
emit downloadEnded(100);
|
||||
#endif
|
||||
} else {
|
||||
} else if (access_token.isEmpty()) {
|
||||
printd("Withings password API");
|
||||
|
||||
// account for trailing slash, remove it if it is there (it was the default in preferences)
|
||||
QString server = appsettings->cvalue(context->athlete->cyclist, GC_WIURL, "http://wbsapi.withings.net").toString();
|
||||
@@ -261,7 +292,7 @@ WithingsDownload::getBodyMeasures(QString &error, QDateTime from, QDateTime to,
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "response:" << response;
|
||||
printd("response: %s", response.toStdString().c_str());
|
||||
|
||||
QJsonParseError parseResult;
|
||||
if (response.contains("\"status\":0", Qt::CaseInsensitive))
|
||||
@@ -384,12 +415,12 @@ WithingsDownload::downloadFinished(QNetworkReply *reply)
|
||||
#ifdef GC_HAVE_KQOAUTH
|
||||
void
|
||||
WithingsDownload::onAuthorizedRequestDone() {
|
||||
// qDebug() << "Request sent to Withings!";
|
||||
// printd("Request sent to Withings!");
|
||||
}
|
||||
|
||||
void
|
||||
WithingsDownload::onRequestReady(QByteArray r) {
|
||||
//qDebug() << "Response from the Withings's service: " << response;
|
||||
//printd("Response from the Withings's service: %s", response..toStdString().c_str());
|
||||
|
||||
response = r;
|
||||
loop.exit(0);
|
||||
|
||||
Reference in New Issue
Block a user