mirror of https://github.com/keeweb/keeweb.git
oauth2
This commit is contained in:
parent
039bb075c4
commit
ec8efbf4d3
|
@ -6,7 +6,6 @@ var Timeouts = {
|
|||
AutoHideHint: 3000,
|
||||
FileChangeSync: 3000,
|
||||
BeforeAutoLock: 300,
|
||||
ScriptLoad: 15000,
|
||||
CheckWindowClosed: 300
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
'use strict';
|
||||
|
||||
var Backbone = require('backbone'),
|
||||
SettingsStore = require('../comp/settings-store');
|
||||
|
||||
var RuntimeDataModel = Backbone.Model.extend({
|
||||
defaults: {},
|
||||
|
||||
initialize: function() {
|
||||
this.listenTo(this, 'change', this.save);
|
||||
},
|
||||
|
||||
load: function() {
|
||||
var data = SettingsStore.load('runtime-data');
|
||||
if (data) {
|
||||
this.set(data, {silent: true});
|
||||
}
|
||||
},
|
||||
|
||||
save: function() {
|
||||
SettingsStore.save('runtime-data', this.attributes);
|
||||
}
|
||||
});
|
||||
|
||||
RuntimeDataModel.instance = new RuntimeDataModel();
|
||||
RuntimeDataModel.instance.load();
|
||||
|
||||
module.exports = RuntimeDataModel;
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
var Backbone = require('backbone'),
|
||||
Logger = require('../util/logger'),
|
||||
AppSettingsModel = require('../models/app-settings-model');
|
||||
AppSettingsModel = require('../models/app-settings-model'),
|
||||
RuntimeDataModel = require('../models/runtime-data-model');
|
||||
|
||||
var StorageBase = function() {
|
||||
};
|
||||
|
@ -17,6 +18,7 @@ _.extend(StorageBase.prototype, {
|
|||
|
||||
logger: null,
|
||||
appSettings: AppSettingsModel.instance,
|
||||
runtimeData: RuntimeDataModel.instance,
|
||||
|
||||
init: function() {
|
||||
if (!this.name) {
|
||||
|
@ -51,6 +53,10 @@ _.extend(StorageBase.prototype, {
|
|||
return config.error && config.error('timeout');
|
||||
});
|
||||
xhr.open(config.method || 'GET', config.url);
|
||||
if (this._oauthToken) {
|
||||
xhr.setRequestHeader('Authorization',
|
||||
this._oauthToken.tokenType + ' ' + this._oauthToken.accessToken);
|
||||
}
|
||||
_.forEach(config.headers, function(value, key) {
|
||||
xhr.setRequestHeader(key, value);
|
||||
});
|
||||
|
@ -84,6 +90,58 @@ _.extend(StorageBase.prototype, {
|
|||
win.focus();
|
||||
}
|
||||
return win;
|
||||
},
|
||||
|
||||
_oauthAuthorize: function(opts) {
|
||||
var that = this;
|
||||
var oldToken = that.runtimeData.get(that.name + 'OAuthToken');
|
||||
if (oldToken) {
|
||||
that._oauthToken = oldToken;
|
||||
opts.callback();
|
||||
return;
|
||||
}
|
||||
that.logger.debug('OAuth popup opened');
|
||||
that._openPopup(opts.url, 'OAuth', opts.width, opts.height);
|
||||
var popupClosed = function() {
|
||||
Backbone.off('popup-closed', popupClosed);
|
||||
window.removeEventListener('message', windowMessage);
|
||||
that.logger.error('OAuth error', 'popup closed');
|
||||
opts.callback('popup closed');
|
||||
};
|
||||
var windowMessage = function(e) {
|
||||
if (!e.data) {
|
||||
return;
|
||||
}
|
||||
Backbone.off('popup-closed', popupClosed);
|
||||
window.removeEventListener('message', windowMessage);
|
||||
var token = that._oauthMsgToToken(e.data);
|
||||
if (token.error) {
|
||||
that.logger.error('OAuth error', token.error, token.errorDescription);
|
||||
opts.callback(token.error);
|
||||
} else {
|
||||
that._oauthToken = token;
|
||||
that.runtimeData.set(that.name + 'OAuthToken', token);
|
||||
that.logger.debug('OAuth success');
|
||||
opts.callback();
|
||||
}
|
||||
};
|
||||
Backbone.on('popup-closed', popupClosed);
|
||||
window.addEventListener('message', windowMessage);
|
||||
},
|
||||
|
||||
_oauthMsgToToken: function(data) {
|
||||
// jshint camelcase:false
|
||||
if (data.error || !data.token_type) {
|
||||
return { error: data.error || 'no token', errorDescription: data.error_description };
|
||||
}
|
||||
return {
|
||||
tokenType: data.token_type,
|
||||
accessToken: data.access_token,
|
||||
authenticationToken: data.authentication_token,
|
||||
expiresIn: data.expires_in,
|
||||
scope: data.scope,
|
||||
userId: data.user_id
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
'use strict';
|
||||
|
||||
var StorageBase = require('./storage-base'),
|
||||
Backbone = require('backbone'),
|
||||
Timeouts = require('../const/timeouts');
|
||||
var StorageBase = require('./storage-base');
|
||||
|
||||
var GDriveClinetId = '847548101761-koqkji474gp3i2gn3k5omipbfju7pbt1.apps.googleusercontent.com';
|
||||
var GDriveClientId = '847548101761-koqkji474gp3i2gn3k5omipbfju7pbt1.apps.googleusercontent.com';
|
||||
|
||||
var StorageGDrive = StorageBase.extend({
|
||||
name: 'gdrive',
|
||||
|
@ -14,7 +12,7 @@ var StorageGDrive = StorageBase.extend({
|
|||
'<path d="M120.76421 71.989219 84.87226 9.6679848l-41.828196 0 35.899791 62.3212342zM58.014073 56.294956 37.107816 19.986746 1.2237094 82.284404 ' +
|
||||
'22.137808 118.59261Zm-21.415974 63.012814 69.180421 0 20.9141-39.459631-67.635587 0z"/></svg>',
|
||||
|
||||
_gapi: null,
|
||||
_baseUrl: 'https://www.googleapis.com/drive/v3',
|
||||
|
||||
load: function(path, opts, callback) {
|
||||
var that = this;
|
||||
|
@ -22,13 +20,12 @@ var StorageGDrive = StorageBase.extend({
|
|||
if (err) { return callback && callback(err); }
|
||||
that.logger.debug('Load', path);
|
||||
var ts = that.logger.ts();
|
||||
var url = 'https://www.googleapis.com/drive/v3/files/{id}/revisions/{rev}?alt=media'
|
||||
var url = that._baseUrl + '/files/{id}/revisions/{rev}?alt=media'
|
||||
.replace('{id}', path)
|
||||
.replace('{rev}', stat.rev);
|
||||
that._xhr({
|
||||
url: url,
|
||||
responseType: 'arraybuffer',
|
||||
headers: { 'Authorization': that._getAuthHeader() },
|
||||
success: function(response) {
|
||||
that.logger.debug('Loaded', path, stat.rev, that.logger.ts(ts));
|
||||
return callback && callback(null, response, { rev: stat.rev });
|
||||
|
@ -43,18 +40,17 @@ var StorageGDrive = StorageBase.extend({
|
|||
|
||||
stat: function(path, opts, callback) {
|
||||
var that = this;
|
||||
this._getClient(function(err) {
|
||||
this._authorize(function(err) {
|
||||
if (err) {
|
||||
return callback && callback(err);
|
||||
}
|
||||
that.logger.debug('Stat', path);
|
||||
var ts = that.logger.ts();
|
||||
var url = 'https://www.googleapis.com/drive/v3/files/{id}?fields=headRevisionId'
|
||||
var url = that._baseUrl + '/files/{id}?fields=headRevisionId'
|
||||
.replace('{id}', path);
|
||||
that._xhr({
|
||||
url: url,
|
||||
responseType: 'json',
|
||||
headers: { 'Authorization': that._getAuthHeader() },
|
||||
success: function(response) {
|
||||
var rev = response.headRevisionId;
|
||||
that.logger.debug('Stated', path, rev, that.logger.ts(ts));
|
||||
|
@ -86,7 +82,6 @@ var StorageGDrive = StorageBase.extend({
|
|||
url: url,
|
||||
method: 'PATCH',
|
||||
responseType: 'json',
|
||||
headers: { 'Authorization': that._getAuthHeader() },
|
||||
data: new Blob([data], {type: 'application/octet-stream'}),
|
||||
success: function(response) {
|
||||
that.logger.debug('Saved', path, that.logger.ts(ts));
|
||||
|
@ -106,17 +101,16 @@ var StorageGDrive = StorageBase.extend({
|
|||
|
||||
list: function(callback) {
|
||||
var that = this;
|
||||
this._getClient(function(err) {
|
||||
this._authorize(function(err) {
|
||||
if (err) { return callback && callback(err); }
|
||||
that.logger.debug('List');
|
||||
var url = 'https://www.googleapis.com/drive/v3/files?fields={fields}&q={q}'
|
||||
var url = that._baseUrl + '/files?fields={fields}&q={q}'
|
||||
.replace('{fields}', encodeURIComponent('files'))
|
||||
.replace('{q}', encodeURIComponent('fileExtension="kdbx" and mimeType="application/octet-stream" and trashed=false'));
|
||||
var ts = that.logger.ts();
|
||||
that._xhr({
|
||||
url: url,
|
||||
responseType: 'json',
|
||||
headers: { 'Authorization': that._getAuthHeader() },
|
||||
success: function(response) {
|
||||
if (!response) {
|
||||
that.logger.error('List error', that.logger.ts(ts));
|
||||
|
@ -140,77 +134,22 @@ var StorageGDrive = StorageBase.extend({
|
|||
});
|
||||
},
|
||||
|
||||
_getClient: function(callback) {
|
||||
var that = this;
|
||||
if (that._gapi) {
|
||||
return that._authorize(callback);
|
||||
}
|
||||
that._gapiLoadTimeout = setTimeout(function() {
|
||||
callback('Gdrive api load timeout');
|
||||
delete that._gapiLoadTimeout;
|
||||
}, Timeouts.ScriptLoad);
|
||||
if (that._gapi) {
|
||||
that._authorize.bind(callback);
|
||||
} else {
|
||||
that.logger.debug('Loading gapi client');
|
||||
window.gApiClientLoaded = function() {
|
||||
if (that._gapiLoadTimeout) {
|
||||
that.logger.debug('Loaded gapi client');
|
||||
delete window.gDriveClientLoaded;
|
||||
that._gapi = window.gapi;
|
||||
that._authorize(callback);
|
||||
}
|
||||
};
|
||||
$.getScript('https://apis.google.com/js/client.js?onload=gApiClientLoaded', function() {
|
||||
that.logger.debug('Loaded gapi script');
|
||||
}).fail(function() {
|
||||
that.logger.error('Failed to load gapi');
|
||||
return callback('gapi load failed');
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_authorize: function(callback) {
|
||||
var that = this;
|
||||
var scopes = ['https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/drive.file'];
|
||||
if (that._gapi.auth.getToken()) {
|
||||
if (this._oauthToken) {
|
||||
return callback();
|
||||
}
|
||||
var clientId = this.appSettings.get('gdriveClientId') || GDriveClinetId;
|
||||
that._gapi.auth.authorize({'client_id': clientId, scope: scopes, immediate: true}, function(res) {
|
||||
if (res && !res.error) {
|
||||
callback();
|
||||
} else {
|
||||
that.logger.debug('Authorizing...');
|
||||
var handlePopupClosed = function() {
|
||||
that.logger.debug('Auth popup closed');
|
||||
Backbone.off('popup-closed', handlePopupClosed);
|
||||
callback('popup closed');
|
||||
callback = null;
|
||||
};
|
||||
Backbone.on('popup-closed', handlePopupClosed);
|
||||
var ts = that.logger.ts();
|
||||
that._gapi.auth.authorize({'client_id': clientId, scope: scopes, immediate: false}, function(res) {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
Backbone.off('popup-closed', handlePopupClosed);
|
||||
if (res && !res.error) {
|
||||
that.logger.debug('Authorized', that.logger.ts(ts));
|
||||
callback();
|
||||
} else {
|
||||
that.logger.error('Authorize error', that.logger.ts(ts), res);
|
||||
callback(res && res.error || 'authorize error');
|
||||
}
|
||||
});
|
||||
}
|
||||
var clientId = this.appSettings.get('gdriveClientId') || GDriveClientId;
|
||||
var url = 'https://accounts.google.com/o/oauth2/v2/auth' +
|
||||
'?client_id={cid}&scope={scope}&response_type=token&redirect_uri={url}'
|
||||
.replace('{cid}', clientId)
|
||||
.replace('{scope}', encodeURIComponent('https://www.googleapis.com/auth/drive'))
|
||||
.replace('{url}', encodeURIComponent(window.location));
|
||||
this._oauthAuthorize({
|
||||
url: url,
|
||||
callback: callback,
|
||||
width: 600,
|
||||
height: 400
|
||||
});
|
||||
},
|
||||
|
||||
_getAuthHeader: function() {
|
||||
// jshint camelcase:false
|
||||
var token = this._gapi.auth.getToken();
|
||||
return token.token_type + ' ' + token.access_token;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
var Backbone = require('backbone'),
|
||||
StorageBase = require('./storage-base');
|
||||
var StorageBase = require('./storage-base');
|
||||
|
||||
var OneDriveClientId = {
|
||||
Production: '000000004818ED3A',
|
||||
|
@ -24,7 +23,6 @@ var StorageOneDrive = StorageBase.extend({
|
|||
'6.28c-17.7-17.7-46.59-21.53-71.15-9.42-9.81 4.84-17.7 11.78-23.65 20.83-4.25 6.45-9.66 18.48-9.66 21.47 0 2.12-1.72 3.18-9.05 5.58-22.69 7.44-' +
|
||||
'35.94 24.63-35.93 46.62 0 8 2.06 17.8 4.93 23.41 1.08 2.11 1.68 4.13 1.34 4.47-0.88 0.88-29.11 0.58-33.01-0.35z" /></g></g></g></svg>',
|
||||
|
||||
_token: null,
|
||||
_baseUrl: 'https://api.onedrive.com/v1.0',
|
||||
|
||||
load: function(path, opts, callback) {
|
||||
|
@ -39,7 +37,6 @@ var StorageOneDrive = StorageBase.extend({
|
|||
that._xhr({
|
||||
url: url,
|
||||
responseType: 'json',
|
||||
headers: {'Authorization': that._getAuthHeader()},
|
||||
success: function (response) {
|
||||
var downloadUrl = response['@content.downloadUrl'];
|
||||
var rev = response.eTag;
|
||||
|
@ -50,7 +47,6 @@ var StorageOneDrive = StorageBase.extend({
|
|||
that._xhr({
|
||||
url: downloadUrl,
|
||||
responseType: 'arraybuffer',
|
||||
headers: {'Authorization': that._getAuthHeader()},
|
||||
success: function (response, xhr) {
|
||||
rev = xhr.getResponseHeader('ETag') || rev;
|
||||
that.logger.debug('Loaded', path, rev, that.logger.ts(ts));
|
||||
|
@ -82,7 +78,6 @@ var StorageOneDrive = StorageBase.extend({
|
|||
that._xhr({
|
||||
url: url,
|
||||
responseType: 'json',
|
||||
headers: {'Authorization': that._getAuthHeader()},
|
||||
success: function (response) {
|
||||
var rev = response.eTag;
|
||||
if (!rev) {
|
||||
|
@ -113,10 +108,7 @@ var StorageOneDrive = StorageBase.extend({
|
|||
url: url,
|
||||
method: 'PUT',
|
||||
responseType: 'json',
|
||||
headers: {
|
||||
'Authorization': that._getAuthHeader(),
|
||||
'If-Match': rev
|
||||
},
|
||||
headers: { 'If-Match': rev },
|
||||
data: new Blob([data], {type: 'application/octet-stream'}),
|
||||
statuses: [200, 412],
|
||||
success: function (response, xhr) {
|
||||
|
@ -150,7 +142,6 @@ var StorageOneDrive = StorageBase.extend({
|
|||
that._xhr({
|
||||
url: url,
|
||||
responseType: 'json',
|
||||
headers: { 'Authorization': that._getAuthHeader() },
|
||||
success: function(response) {
|
||||
if (!response || !response.value) {
|
||||
that.logger.error('List error', that.logger.ts(ts), response);
|
||||
|
@ -185,52 +176,21 @@ var StorageOneDrive = StorageBase.extend({
|
|||
},
|
||||
|
||||
_authorize: function(callback) {
|
||||
if (this._token) {
|
||||
if (this._oauthToken) {
|
||||
return callback();
|
||||
}
|
||||
var clinetId = this._getClientId();
|
||||
var url = 'https://login.live.com/oauth20_authorize.srf?client_id={cid}&scope={scope}&response_type=token&redirect_uri={url}'
|
||||
.replace('{cid}', clinetId)
|
||||
var clientId = this._getClientId();
|
||||
var url = 'https://login.live.com/oauth20_authorize.srf' +
|
||||
'?client_id={cid}&scope={scope}&response_type=token&redirect_uri={url}'
|
||||
.replace('{cid}', clientId)
|
||||
.replace('{scope}', 'onedrive.readwrite')
|
||||
.replace('{url}', encodeURIComponent(window.location));
|
||||
this._openPopup(url, 'onedriveAuth', 400, 600);
|
||||
var that = this;
|
||||
var popupClosed = function() {
|
||||
Backbone.off('popup-closed', popupClosed);
|
||||
window.removeEventListener('message', windowMessage);
|
||||
that.logger.error('Auth error', 'popup closed');
|
||||
callback('popup closed');
|
||||
};
|
||||
var windowMessage = function(e) {
|
||||
var msgData = e.data;
|
||||
if (!msgData) {
|
||||
return;
|
||||
}
|
||||
// jshint camelcase:false
|
||||
if (msgData.token_type) {
|
||||
Backbone.off('popup-closed', popupClosed);
|
||||
window.removeEventListener('message', windowMessage);
|
||||
that._token = {
|
||||
tokenType: msgData.token_type,
|
||||
accessToken: msgData.access_token,
|
||||
authenticationToken: msgData.authentication_token,
|
||||
expiresIn: msgData.expires_in,
|
||||
scope: msgData.scope,
|
||||
userId: msgData.user_id
|
||||
};
|
||||
that.logger.debug('Auth success');
|
||||
callback();
|
||||
} else if (msgData.error) {
|
||||
that.logger.error('Auth error', msgData.error, msgData.error_description);
|
||||
callback(msgData.error);
|
||||
}
|
||||
};
|
||||
Backbone.on('popup-closed', popupClosed);
|
||||
window.addEventListener('message', windowMessage);
|
||||
},
|
||||
|
||||
_getAuthHeader: function() {
|
||||
return this._token ? this._token.tokenType + ' ' + this._token.accessToken : undefined;
|
||||
this._oauthAuthorize({
|
||||
url: url,
|
||||
callback: callback,
|
||||
width: 600,
|
||||
height: 500
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue