CLOUD: Add OneDrive refresh_token support

It might be not that easy to restart the request after new token
received, though.
This commit is contained in:
Alexander Tkachev
2016-08-24 16:07:55 +06:00
parent 5f4bbe6e9e
commit 5e346ea6fb
3 changed files with 120 additions and 53 deletions
+98 -43
View File
@@ -38,50 +38,111 @@ namespace OneDrive {
Common::String OneDriveStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth
Common::String OneDriveStorage::SECRET; //TODO: hide these secrets somehow
static void saveAccessTokenCallback(void *ptr) {
Common::JSONValue *json = (Common::JSONValue *)ptr;
if (json) {
debug("saveAccessTokenCallback:");
debug("%s", json->stringify(true).c_str());
OneDriveStorage::OneDriveStorage(Common::String accessToken, Common::String userId, Common::String refreshToken):
_token(accessToken), _uid(userId), _refreshToken(refreshToken) {}
//TODO: do something about refresh token
Common::JSONObject result = json->asObject();
if (!result.contains("access_token") || !result.contains("user_id")) {
warning("Bad response, no token/user_id passed");
} else {
OneDriveStorage::addStorage(result.getVal("access_token")->asString(), result.getVal("user_id")->asString());
ConfMan.removeKey("onedrive_code", "cloud");
debug("Done! You can use OneDrive now! Look:");
g_system->getCloudManager()->syncSaves();
}
OneDriveStorage::OneDriveStorage(Common::String code) {
getAccessToken(new Common::Callback<OneDriveStorage, bool>(this, &OneDriveStorage::codeFlowComplete), code);
}
delete json;
} else {
debug("saveAccessTokenCallback: got NULL instead of JSON!");
OneDriveStorage::~OneDriveStorage() {}
void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code) {
bool codeFlow = (code != "");
if (!codeFlow && _refreshToken == "") {
warning("OneDriveStorage: no refresh token available to get new access token.");
if (callback) (*callback)(false);
return;
}
Common::BaseCallback<> *innerCallback = new Common::CallbackBridge<OneDriveStorage, bool>(this, &OneDriveStorage::tokenRefreshed, callback);
Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://login.live.com/oauth20_token.srf");
if (codeFlow) {
request->addPostField("code=" + code);
request->addPostField("grant_type=authorization_code");
} else {
request->addPostField("refresh_token=" + _refreshToken);
request->addPostField("grant_type=refresh_token");
}
request->addPostField("client_id=" + KEY);
request->addPostField("client_secret=" + SECRET);
request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");
ConnMan.addRequest(request);
}
OneDriveStorage::OneDriveStorage(Common::String accessToken, Common::String userId): _token(accessToken), _uid(userId) {
curl_global_init(CURL_GLOBAL_ALL);
void OneDriveStorage::tokenRefreshed(BoolCallback callback, void *jsonPointer) {
Common::JSONValue *json = (Common::JSONValue *)jsonPointer;
if (!json) {
warning("OneDriveStorage: got NULL instead of JSON");
if (callback) (*callback)(false);
return;
}
Common::JSONObject result = json->asObject();
if (!result.contains("access_token") || !result.contains("user_id") || !result.contains("refresh_token")) {
warning("Bad response, no token or user_id passed");
debug("%s", json->stringify().c_str());
if (callback) (*callback)(false);
} else {
_token = result.getVal("access_token")->asString();
_uid = result.getVal("user_id")->asString();
_refreshToken = result.getVal("refresh_token")->asString();
g_system->getCloudManager()->save(); //ask CloudManager to save our new refreshToken
if (callback) (*callback)(true);
}
delete json;
}
OneDriveStorage::~OneDriveStorage() {
curl_global_cleanup();
void OneDriveStorage::codeFlowComplete(bool success) {
if (!success) {
warning("OneDriveStorage: failed to get access token through code flow");
return;
}
g_system->getCloudManager()->addStorage(this);
ConfMan.removeKey("onedrive_code", "cloud");
debug("Done! You can use OneDrive now! Look:");
g_system->getCloudManager()->syncSaves();
}
void OneDriveStorage::saveConfig(Common::String keyPrefix) {
ConfMan.set(keyPrefix + "type", "OneDrive", "cloud");
ConfMan.set(keyPrefix + "access_token", _token, "cloud");
ConfMan.set(keyPrefix + "user_id", _uid, "cloud");
ConfMan.set(keyPrefix + "refresh_token", _refreshToken, "cloud");
}
void OneDriveStorage::printJsonTokenReceived(bool success) {
if (success) syncSaves(0); //try again
}
void OneDriveStorage::printJson(void *jsonPointer) {
Common::JSONValue *json = (Common::JSONValue *)jsonPointer;
if (!json) {
warning("printJson: NULL");
return;
}
Common::JSONObject result = json->asObject();
if (result.contains("error")) {
//Common::JSONObject error = result.getVal("error")->asObject();
debug("bad token, trying again...");
getAccessToken(new Common::Callback<OneDriveStorage, bool>(this, &OneDriveStorage::printJsonTokenReceived));
delete json;
return;
}
debug("%s", json->stringify().c_str());
delete json;
}
void OneDriveStorage::syncSaves(BoolCallback callback) {
//this is not the real syncSaves() implementation
}
void OneDriveStorage::addStorage(Common::String token, Common::String uid) {
Storage *storage = new OneDriveStorage(token, uid);
g_system->getCloudManager()->addStorage(storage);
//this is not the real syncSaves() implementation
Common::BaseCallback<> *innerCallback = new Common::Callback<OneDriveStorage>(this, &OneDriveStorage::printJson);
Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.onedrive.com/v1.0/drives/");
request->addHeader("Authorization: bearer " + _token);
ConnMan.addRequest(request);
}
OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
@@ -98,9 +159,15 @@ OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
return 0;
}
if (!ConfMan.hasKey(keyPrefix + "refresh_token", "cloud")) {
warning("No refresh_token found");
return 0;
}
Common::String accessToken = ConfMan.get(keyPrefix + "access_token", "cloud");
Common::String userId = ConfMan.get(keyPrefix + "user_id", "cloud");
return new OneDriveStorage(accessToken, userId);
Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_token", "cloud");
return new OneDriveStorage(accessToken, userId, refreshToken);
}
Common::String OneDriveStorage::getAuthLink() {
@@ -109,7 +176,7 @@ Common::String OneDriveStorage::getAuthLink() {
url += "&redirect_uri=http://localhost:12345/"; //that's for copy-pasting
//url += "&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F"; //that's "http://localhost:12345/" for automatic opening
url += "&client_id=" + KEY;
url += "&scope=onedrive.appfolder"; //TODO
url += "&scope=onedrive.appfolder%20offline_access"; //TODO
return url;
}
@@ -124,7 +191,7 @@ void OneDriveStorage::authThroughConsole() {
if (ConfMan.hasKey("onedrive_code", "cloud")) {
//phase 2: get access_token using specified code
getAccessToken(ConfMan.get("onedrive_code", "cloud"));
new OneDriveStorage(ConfMan.get("onedrive_code", "cloud"));
return;
}
@@ -135,17 +202,5 @@ void OneDriveStorage::authThroughConsole() {
debug("http://wiki.scummvm.org/index.php/User_Manual/Configuring_ScummVM#Using_the_configuration_file_to_configure_ScummVM\n");
}
void OneDriveStorage::getAccessToken(Common::String code) {
Common::BaseCallback<> *callback = new Common::GlobalFunctionCallback(saveAccessTokenCallback);
Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, "https://login.live.com/oauth20_token.srf");
//Content-Type: application/x-www-form-urlencoded
request->addPostField("code=" + code);
request->addPostField("grant_type=authorization_code");
request->addPostField("client_id=" + KEY);
request->addPostField("client_secret=" + SECRET);
request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");
ConnMan.addRequest(request);
}
} //end of namespace OneDrive
} //end of namespace Cloud
+18 -8
View File
@@ -32,13 +32,28 @@ namespace OneDrive {
class OneDriveStorage: public Cloud::Storage {
static Common::String KEY, SECRET;
Common::String _token, _uid;
Common::String _token, _uid, _refreshToken;
/** This private constructor is called from loadFromConfig(). */
OneDriveStorage(Common::String token, Common::String uid);
OneDriveStorage(Common::String token, Common::String uid, Common::String refreshToken);
static void getAccessToken(Common::String code);
/**
* This private constructor is called from authThroughConsole() (phase 2).
* It uses OAuth code flow to get tokens.
*/
OneDriveStorage(Common::String code);
/**
* Gets new access_token. If <code> passed is "", refresh_token is used.
* Use "" in order to refresh token and pass a callback, so you could
* continue your work when new token is available.
*/
void getAccessToken(BoolCallback callback, Common::String code = "");
void tokenRefreshed(BoolCallback callback, void *jsonPointer);
void codeFlowComplete(bool success);
void printJson(void *jsonPointer);
void printJsonTokenReceived(bool success);
public:
virtual ~OneDriveStorage();
@@ -91,11 +106,6 @@ public:
/** Returns whether there are any requests running. */
virtual bool isWorking() { return false; } //TODO
/**
* Add OneDriveStorage with given token and uid into Cloud::Manager.
*/
static void addStorage(Common::String token, Common::String uid);
/**
* Load token and user id from configs and return OneDriveStorage for those.
* @return pointer to the newly created OneDriveStorage or 0 if some problem occured.
@@ -46,8 +46,10 @@ NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, C
curl_easy_setopt(_easy, CURLOPT_URL, url);
curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, postFields.size());
curl_easy_setopt(_easy, CURLOPT_COPYPOSTFIELDS, postFields.c_str());
if (postFields.size() != 0) {
curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, postFields.size());
curl_easy_setopt(_easy, CURLOPT_COPYPOSTFIELDS, postFields.c_str());
}
ConnMan.registerEasyHandle(_easy);
}