Skip to content

Commit b59a2e0

Browse files
author
Sebastian Kippe
authored
Merge pull request #1101 from remotestorage/bugfix/1097-fix_gd_dropbox_authorization
Fix Google Drive and Dropbox authorization in Cordova apps
2 parents d4c0dbf + 5bcb0d4 commit b59a2e0

File tree

7 files changed

+140
-30
lines changed

7 files changed

+140
-30
lines changed

doc/js-api/remotestorage.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ Prototype functions
143143

144144
The following functions can be called on your ``remoteStorage`` instance:
145145

146-
.. autofunction:: RemoteStorage#authorize(authURL, [cordovaRedirectUri])
146+
.. autofunction:: RemoteStorage#authorize(options)
147147
:short-name:
148148

149149
.. autofunction:: RemoteStorage#connect(userAddress, [token])

src/authorize.js

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,24 @@ function extractParams(url) {
3636
}, {});
3737
}
3838

39+
function buildOAuthURL (authURL, redirectUri, scope, clientId) {
40+
const hashPos = redirectUri.indexOf('#');
41+
let url = authURL;
3942

40-
var Authorize = function (remoteStorage, authURL, scope, redirectUri, clientId) {
43+
url += authURL.indexOf('?') > 0 ? '&' : '?';
44+
url += 'redirect_uri=' + encodeURIComponent(redirectUri.replace(/#.*$/, ''));
45+
url += '&scope=' + encodeURIComponent(scope);
46+
url += '&client_id=' + encodeURIComponent(clientId);
47+
48+
if (hashPos !== - 1 && hashPos+1 !== redirectUri.length) {
49+
url += '&state=' + encodeURIComponent(redirectUri.substring(hashPos+1));
50+
}
51+
url += '&response_type=token';
52+
53+
return url;
54+
}
55+
56+
const Authorize = function (remoteStorage, { authURL, scope, redirectUri, clientId }) {
4157
log('[Authorize] authURL = ', authURL, 'scope = ', scope, 'redirectUri = ', redirectUri, 'clientId = ', clientId);
4258

4359
// keep track of the discovery data during redirect if we can't save it in localStorage
@@ -55,15 +71,7 @@ var Authorize = function (remoteStorage, authURL, scope, redirectUri, clientId)
5571
redirectUri += 'rsDiscovery=' + btoa(JSON.stringify(discoveryData));
5672
}
5773

58-
var url = authURL, hashPos = redirectUri.indexOf('#');
59-
url += authURL.indexOf('?') > 0 ? '&' : '?';
60-
url += 'redirect_uri=' + encodeURIComponent(redirectUri.replace(/#.*$/, ''));
61-
url += '&scope=' + encodeURIComponent(scope);
62-
url += '&client_id=' + encodeURIComponent(clientId);
63-
if (hashPos !== - 1 && hashPos+1 !== redirectUri.length) {
64-
url += '&state=' + encodeURIComponent(redirectUri.substring(hashPos+1));
65-
}
66-
url += '&response_type=token';
74+
const url = buildOAuthURL(authURL, redirectUri, scope, clientId);
6775

6876
if (util.globalContext.cordova) {
6977
return Authorize.openWindow(url, redirectUri, 'location=yes,clearsessioncache=yes,clearcache=yes')

src/dropbox.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ Dropbox.prototype = {
209209
if (this.token){
210210
hookIt(this.rs);
211211
} else {
212-
Authorize(this.rs, AUTH_URL, '', String(Authorize.getLocation()), this.clientId);
212+
this.rs.authorize({ authURL: AUTH_URL, scope: '', clientId: this.clientId });
213213
}
214214
},
215215

src/googledrive.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
* Docs: https://developers.google.com/drive/v3/web/quickstart/js
1515
**/
1616

17-
const Authorize = require('./authorize');
1817
const BaseClient = require('./baseclient');
1918
const WireClient = require('./wireclient');
2019
const eventHandling = require('./eventhandling');
@@ -207,7 +206,7 @@ GoogleDrive.prototype = {
207206
*/
208207
connect: function () {
209208
this.rs.setBackend('googledrive');
210-
Authorize(this.rs, AUTH_URL, AUTH_SCOPE, String(Authorize.getLocation()), this.clientId);
209+
this.rs.authorize({ authURL: AUTH_URL, scope: AUTH_SCOPE, clientId: this.clientId });
211210
},
212211

213212
/**

src/remotestorage.js

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -172,20 +172,32 @@ RemoteStorage.prototype = {
172172
},
173173

174174
/**
175-
* TODO: document
175+
* Initiate the OAuth authorization flow.
176+
*
177+
* This function is called by custom storage backend implementations
178+
* (e.g. Dropbox or Google Drive).
179+
*
180+
* @param {object} options
181+
* @param {string} options.authURL - URL of the authorization endpoint
182+
* @param {string} [options.scope] - access scope
183+
* @param {string} [options.clientId] - client identifier (defaults to the
184+
* origin of the redirectUri)
176185
*
177-
* @param {string} authUrl
178-
* @param {string} cordovaRedirectUri
186+
* @private
179187
*/
180-
authorize: function authorize(authURL, cordovaRedirectUri) {
188+
authorize: function authorize (options) {
181189
this.access.setStorageType(this.remote.storageApi);
182-
var scope = this.access.scopeParameter;
190+
if (typeof options.scope === 'undefined') {
191+
options.scope = this.access.scopeParameter;
192+
}
183193

184-
var redirectUri = globalContext.cordova ? cordovaRedirectUri : String(Authorize.getLocation());
194+
options.redirectUri = globalContext.cordova ? config.cordovaRedirectUri : String(Authorize.getLocation());
185195

186-
var clientId = redirectUri.match(/^(https?:\/\/[^/]+)/)[0];
196+
if (typeof options.clientId === 'undefined') {
197+
options.clientId = options.redirectUri.match(/^(https?:\/\/[^/]+)/)[0];
198+
}
187199

188-
Authorize(this, authURL, scope, redirectUri, clientId);
200+
Authorize(this, options);
189201
},
190202

191203
/**
@@ -280,7 +292,7 @@ RemoteStorage.prototype = {
280292
if (info.authURL) {
281293
if (typeof token === 'undefined') {
282294
// Normal authorization step; the default way to connect
283-
this.authorize(info.authURL, config.cordovaRedirectUri);
295+
this.authorize({ authURL: info.authURL });
284296
} else if (typeof token === 'string') {
285297
// Token supplied directly by app/developer/user
286298
log('Skipping authorization sequence and connecting with known token');

test/unit/authorize-suite.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,14 @@ define([ 'require', './src/authorize', './src/config'], function(require, Author
5151
{
5252
desc: "Authorize redirects to the provider's OAuth location",
5353
run: function(env, test) {
54-
var authUrl = 'http://storage.provider.com/oauth';
54+
var authURL = 'http://storage.provider.com/oauth';
5555
var scope = 'contacts:r';
5656
var redirectUri = 'http://awesome.app.com/#custom/path';
5757
var clientId = 'http://awesome.app.com/';
5858

5959
this.localStorageAvailable = function() { return true; };
6060

61-
Authorize(this, authUrl, scope, redirectUri, clientId);
61+
Authorize(this, {authURL, scope, redirectUri, clientId});
6262

6363
var expectedUrl = 'http://storage.provider.com/oauth?redirect_uri=http%3A%2F%2Fawesome.app.com%2F&scope=contacts%3Ar&client_id=http%3A%2F%2Fawesome.app.com%2F&state=custom%2Fpath&response_type=token';
6464
test.assert(document.location.href, expectedUrl);
@@ -68,14 +68,14 @@ define([ 'require', './src/authorize', './src/config'], function(require, Author
6868
{
6969
desc: "Authorize redirects to the provider's OAuth location with empty fragment",
7070
run: function(env, test) {
71-
var authUrl = 'http://storage.provider.com/oauth';
71+
var authURL = 'http://storage.provider.com/oauth';
7272
var scope = 'contacts:r';
7373
var redirectUri = 'http://awesome.app.com/#';
7474
var clientId = 'http://awesome.app.com/';
7575

7676
this.localStorageAvailable = function() { return true; };
7777

78-
Authorize(this, authUrl, scope, redirectUri, clientId);
78+
Authorize(this, {authURL, scope, redirectUri, clientId});
7979

8080
var expectedUrl = 'http://storage.provider.com/oauth?redirect_uri=http%3A%2F%2Fawesome.app.com%2F&scope=contacts%3Ar&client_id=http%3A%2F%2Fawesome.app.com%2F&response_type=token';
8181
test.assert(document.location.href, expectedUrl);
@@ -86,7 +86,7 @@ define([ 'require', './src/authorize', './src/config'], function(require, Author
8686
desc: "Authorize doesn't redirect, but opens an in-app-browser window",
8787
run: function(env, test) {
8888
document.location.href = 'file:///some/cordova/path';
89-
var authUrl = 'http://storage.provider.com/oauth';
89+
var authURL = 'http://storage.provider.com/oauth';
9090
var scope = 'contacts:r';
9191
var redirectUri = 'http://awesome.app.com/#';
9292
var clientId = 'http://awesome.app.com/';
@@ -97,10 +97,11 @@ define([ 'require', './src/authorize', './src/config'], function(require, Author
9797

9898
Authorize.openWindow = function(url, uri) {
9999
test.assertAnd(uri, redirectUri);
100+
delete global.cordova;
100101
test.done();
101102
};
102103

103-
Authorize(this, authUrl, scope, redirectUri, clientId);
104+
Authorize(this, {authURL, scope, redirectUri, clientId});
104105

105106
test.assertAnd(document.location.href, 'file:///some/cordova/path');
106107
}

test/unit/remotestorage-suite.js

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -648,7 +648,97 @@ define(['require', 'tv4', './src/eventhandling'], function (require, tv4, eventH
648648

649649
test.assertAnd(env.rs._syncTimer, undefined);
650650
}
651-
}
651+
},
652+
653+
{
654+
desc: "#authorize redirects to the OAuth provider",
655+
run: function (env, test) {
656+
global.document = {
657+
location: {
658+
href: 'https://app.com:5000/foo/bar',
659+
toString: function() { return this.href; }
660+
}
661+
};
662+
663+
const authURL = 'https://provider.com/oauth';
664+
env.rs.access.claim('contacts', 'r');
665+
666+
env.rs.authorize({ authURL });
667+
668+
test.assert(document.location.href, 'https://provider.com/oauth?redirect_uri=https%3A%2F%2Fapp.com%3A5000%2Ffoo%2Fbar&scope=contacts%3Ar&client_id=https%3A%2F%2Fapp.com%3A5000&response_type=token');
669+
670+
delete global.document;
671+
}
672+
},
673+
674+
{
675+
desc: "#authorize uses the given scope",
676+
run: function (env, test) {
677+
global.document = {
678+
location: {
679+
href: 'https://app.com:5000/foo/bar',
680+
toString: function() { return this.href; }
681+
}
682+
};
683+
684+
const authURL = 'https://provider.com/oauth';
685+
686+
env.rs.authorize({ authURL, scope: 'custom-scope' });
687+
688+
test.assert(document.location.href, 'https://provider.com/oauth?redirect_uri=https%3A%2F%2Fapp.com%3A5000%2Ffoo%2Fbar&scope=custom-scope&client_id=https%3A%2F%2Fapp.com%3A5000&response_type=token');
689+
690+
delete global.document;
691+
}
692+
},
693+
694+
{
695+
desc: "#authorize uses the cordovaRedirectUri when in Cordova",
696+
run: function (env, test) {
697+
global.document = {
698+
location: {
699+
href: 'https://app.com:5000/foo/bar',
700+
toString: function() { return this.href; }
701+
}
702+
};
703+
704+
global.cordova = { foo: 'bar' }; // Pretend we run in Cordova
705+
706+
global.Authorize.openWindow = function(url, redirectUri) {
707+
test.assertAnd(url, 'https://provider.com/oauth?redirect_uri=https%3A%2F%2Fmy.custom-redirect.url&scope=contacts%3Ar&client_id=https%3A%2F%2Fmy.custom-redirect.url&response_type=token');
708+
test.assertAnd(redirectUri, 'https://my.custom-redirect.url');
709+
delete global.cordova;
710+
delete global.document;
711+
test.done();
712+
};
713+
714+
const authURL = 'https://provider.com/oauth';
715+
716+
env.rs.access.claim('contacts', 'r');
717+
env.rs.setCordovaRedirectUri('https://my.custom-redirect.url');
718+
env.rs.authorize({ authURL });
719+
}
720+
},
721+
722+
{
723+
desc: "#authorize uses the given clientId",
724+
run: function (env, test) {
725+
global.document = {
726+
location: {
727+
href: 'https://app.com:5000/foo/bar',
728+
toString: function() { return this.href; }
729+
}
730+
};
731+
732+
const authURL = 'https://provider.com/oauth';
733+
env.rs.access.claim('contacts', 'r');
734+
735+
env.rs.authorize({ authURL, clientId: 'my-client-id' });
736+
737+
test.assert(document.location.href, 'https://provider.com/oauth?redirect_uri=https%3A%2F%2Fapp.com%3A5000%2Ffoo%2Fbar&scope=contacts%3Ar&client_id=my-client-id&response_type=token');
738+
739+
delete global.document;
740+
}
741+
},
652742
]
653743
});
654744

0 commit comments

Comments
 (0)