Withings/Nokia use OAth2 for authorisation

This commit is contained in:
grauser
2018-03-05 21:31:02 +01:00
parent 85cad4d2e0
commit a57766fdcd
5 changed files with 84 additions and 164 deletions

View File

@@ -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);
}

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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() {

View File

@@ -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);