chore(dropbox-web) Accommodate short-lived access token

This commit is contained in:
hmuresan 2021-08-30 17:43:17 +03:00 committed by Horatiu Muresan
parent 5367d43c26
commit bec9920c79
7 changed files with 119 additions and 32 deletions

16
package-lock.json generated
View File

@ -7489,12 +7489,18 @@
} }
}, },
"dropbox": { "dropbox": {
"version": "4.0.9", "version": "10.7.0",
"resolved": "https://registry.npmjs.org/dropbox/-/dropbox-4.0.9.tgz", "resolved": "https://registry.npmjs.org/dropbox/-/dropbox-10.7.0.tgz",
"integrity": "sha512-UeaKw7DY24ZGLRV8xboZvbZXhbTVrFjPjfpr0LfF/KVOzBUad9vJJwqz3udqTLNxD0FXbFlC9rlNLLNXaj9msg==", "integrity": "sha512-btNLOYHxukACfnkEUNhlTPCnkecfbL89mrPU3RMKAWdCQXM18aRLm+t+0xIpzvRUSGeXPER+3d+QJk5Wi+4QGw==",
"requires": { "requires": {
"buffer": "^5.0.8", "node-fetch": "^2.6.1"
"moment": "^2.19.3" },
"dependencies": {
"node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
}
} }
}, },
"duplexer": { "duplexer": {

View File

@ -47,7 +47,7 @@
"base64-js": "1.3.1", "base64-js": "1.3.1",
"bc-css-flags": "3.0.0", "bc-css-flags": "3.0.0",
"clipboard-copy": "4.0.1", "clipboard-copy": "4.0.1",
"dropbox": "4.0.9", "dropbox": "10.7.0",
"focus-visible": "5.1.0", "focus-visible": "5.1.0",
"i18n-iso-countries": "6.8.0", "i18n-iso-countries": "6.8.0",
"i18next": "17.0.6", "i18next": "17.0.6",

View File

@ -24,7 +24,7 @@ export function authorizeDropbox() {
_authorizeDropbox(dropbox.appKey, redirectURI) _authorizeDropbox(dropbox.appKey, redirectURI)
.then( .then(
token => dispatch(updateDropboxToken(token))); ({ token, rToken, expireDate }) => dispatch(updateDropboxToken(token, rToken, expireDate)));
}; };
} }
@ -32,14 +32,20 @@ export function authorizeDropbox() {
* Action to update the dropbox access token. * Action to update the dropbox access token.
* *
* @param {string} token - The new token. * @param {string} token - The new token.
* @param {string} rToken - The refresh token.
* @param {string} expireDate - The token expiration date as ISO string.
* @returns {{ * @returns {{
* type: UPDATE_DROPBOX_TOKEN, * type: UPDATE_DROPBOX_TOKEN,
* token: string * token: string,
* rToken: string,
* expireDate: string
* }} * }}
*/ */
export function updateDropboxToken(token: string) { export function updateDropboxToken(token: string, rToken: string, expireDate: string) {
return { return {
type: UPDATE_DROPBOX_TOKEN, type: UPDATE_DROPBOX_TOKEN,
token token,
rToken,
expireDate
}; };
} }

View File

@ -13,7 +13,10 @@ const { Dropbox } = NativeModules;
* access token or rejected with an error. * access token or rejected with an error.
*/ */
export function _authorizeDropbox(): Promise<string> { export function _authorizeDropbox(): Promise<string> {
return Dropbox.authorize(); return Dropbox.authorize()
.then(token => {
return { token };
});
} }
/** /**

View File

@ -1,12 +1,8 @@
// @flow // @flow
import { Dropbox } from 'dropbox'; import { Dropbox, DropboxAuth } from 'dropbox';
import { import { getJitsiMeetGlobalNS } from '../base/util';
getJitsiMeetGlobalNS,
parseStandardURIString,
parseURLParams
} from '../base/util';
/** /**
* Executes the oauth flow. * Executes the oauth flow.
@ -31,6 +27,16 @@ function authorize(authUrl: string): Promise<string> {
}); });
} }
/**
* Returns the token's expiry date as ISO string.
*
* @param {number} expiresIn - The seconds in which the token expires.
* @returns {string} - The ISO value for the expiry date.
*/
function getTokenExpiresAtDate(expiresIn: number) {
return new Date(Date.now() + (expiresIn * 1000)).toISOString();
}
/** /**
* Action to authorize the Jitsi Recording app in dropbox. * Action to authorize the Jitsi Recording app in dropbox.
* *
@ -41,16 +47,47 @@ function authorize(authUrl: string): Promise<string> {
export function _authorizeDropbox( export function _authorizeDropbox(
appKey: string, appKey: string,
redirectURI: string redirectURI: string
): Promise<string> { ): Promise<Object> {
const dropboxAPI = new Dropbox({ clientId: appKey }); const dropbox = new DropboxAuth({ clientId: appKey });
const url = dropboxAPI.getAuthenticationUrl(redirectURI);
return authorize(url).then(returnUrl => { return dropbox.getAuthenticationUrl(redirectURI, undefined, 'code', 'offline', undefined, undefined, true)
const params .then(authorize)
= parseURLParams(parseStandardURIString(returnUrl), true) || {}; .then(returnUrl => {
const params = new URLSearchParams(new URL(returnUrl).search);
const code = params.get('code');
return params.access_token; return dropbox.getAccessTokenFromCode(redirectURI, code);
}); })
.then(resp => {
return {
token: resp.result.access_token,
rToken: resp.result.refresh_token,
expireDate: getTokenExpiresAtDate(resp.result.expires_in)
};
});
}
/**
* Gets a new acccess token based on the refresh token.
*
* @param {string} appKey - The dropbox appKey.
* @param {string} rToken - The refresh token.
* @returns {Promise}
*/
export function getNewAccessToken(appKey: string, rToken: string) {
const dropbox = new DropboxAuth({ clientId: appKey });
dropbox.setRefreshToken(rToken);
return dropbox.refreshAccessToken()
.then(() => {
return {
token: dropbox.getAccessToken(),
rToken: dropbox.getRefreshToken(),
expireDate: dropbox.getAccessTokenExpiresAt().toISOString()
};
});
} }
/** /**
@ -68,7 +105,7 @@ export function getDisplayName(token: string, appKey: string) {
return ( return (
dropboxAPI.usersGetCurrentAccount() dropboxAPI.usersGetCurrentAccount()
.then(account => account.name.display_name)); .then(account => account.result.name.display_name));
} }
/** /**
@ -85,7 +122,7 @@ export function getSpaceUsage(token: string, appKey: string) {
}); });
return dropboxAPI.usersGetSpaceUsage().then(space => { return dropboxAPI.usersGetSpaceUsage().then(space => {
const { allocation, used } = space; const { allocation, used } = space.result;
const { allocated } = allocation; const { allocated } = allocation;
return { return {

View File

@ -19,7 +19,9 @@ ReducerRegistry.register(STORE_NAME, (state = {}, action) => {
case UPDATE_DROPBOX_TOKEN: case UPDATE_DROPBOX_TOKEN:
return { return {
...state, ...state,
token: action.token token: action.token,
rToken: action.rToken,
expireDate: action.expireDate
}; };
default: default:
return state; return state;

View File

@ -9,7 +9,9 @@ import {
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet'; import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
import { import {
getDropboxData, getDropboxData,
isEnabled as isDropboxEnabled isEnabled as isDropboxEnabled,
getNewAccessToken,
updateDropboxToken
} from '../../../dropbox'; } from '../../../dropbox';
import { showErrorNotification } from '../../../notifications'; import { showErrorNotification } from '../../../notifications';
import { toggleRequestingSubtitles } from '../../../subtitles'; import { toggleRequestingSubtitles } from '../../../subtitles';
@ -50,6 +52,16 @@ type Props = {
*/ */
_isDropboxEnabled: boolean, _isDropboxEnabled: boolean,
/**
* The dropbox refresh token.
*/
_rToken: string,
/**
* Access token's expiration date as ISO string.
*/
_tokenExpireDate?: string,
/** /**
* The dropbox access token. * The dropbox access token.
*/ */
@ -209,7 +221,7 @@ class AbstractStartRecordingDialog extends Component<Props, State> {
* @returns {void} * @returns {void}
*/ */
_onTokenUpdated() { _onTokenUpdated() {
const { _appKey, _isDropboxEnabled, _token } = this.props; const { _appKey, _isDropboxEnabled, _token, _rToken, _tokenExpireDate, dispatch } = this.props;
if (!_isDropboxEnabled) { if (!_isDropboxEnabled) {
return; return;
@ -221,6 +233,13 @@ class AbstractStartRecordingDialog extends Component<Props, State> {
isValidating: false isValidating: false
}); });
} else { } else {
if (_tokenExpireDate && Date.now() > new Date(_tokenExpireDate)) {
getNewAccessToken(_appKey, _rToken)
.then(resp => dispatch(updateDropboxToken(resp.token, resp.rToken, resp.expireDate)));
return;
}
this.setState({ this.setState({
isTokenValid: false, isTokenValid: false,
isValidating: true isValidating: true
@ -251,7 +270,15 @@ class AbstractStartRecordingDialog extends Component<Props, State> {
* @returns {boolean} - True (to note that the modal should be closed). * @returns {boolean} - True (to note that the modal should be closed).
*/ */
_onSubmit() { _onSubmit() {
const { _autoCaptionOnRecord, _conference, _isDropboxEnabled, _token, dispatch } = this.props; const {
_appKey,
_autoCaptionOnRecord,
_conference,
_isDropboxEnabled,
_rToken,
_token,
dispatch
} = this.props;
let appData; let appData;
const attributes = {}; const attributes = {};
@ -261,7 +288,9 @@ class AbstractStartRecordingDialog extends Component<Props, State> {
'file_recording_metadata': { 'file_recording_metadata': {
'upload_credentials': { 'upload_credentials': {
'service_name': RECORDING_TYPES.DROPBOX, 'service_name': RECORDING_TYPES.DROPBOX,
'token': _token 'token': _token,
'r_token': _rToken,
'app_key': _appKey
} }
} }
}); });
@ -320,6 +349,8 @@ class AbstractStartRecordingDialog extends Component<Props, State> {
* _fileRecordingsServiceEnabled: boolean, * _fileRecordingsServiceEnabled: boolean,
* _fileRecordingsServiceSharingEnabled: boolean, * _fileRecordingsServiceSharingEnabled: boolean,
* _isDropboxEnabled: boolean, * _isDropboxEnabled: boolean,
* _rToken:string,
* _tokenExpireDate: string,
* _token: string * _token: string
* }} * }}
*/ */
@ -338,6 +369,8 @@ export function mapStateToProps(state: Object) {
_fileRecordingsServiceEnabled: fileRecordingsServiceEnabled, _fileRecordingsServiceEnabled: fileRecordingsServiceEnabled,
_fileRecordingsServiceSharingEnabled: fileRecordingsServiceSharingEnabled, _fileRecordingsServiceSharingEnabled: fileRecordingsServiceSharingEnabled,
_isDropboxEnabled: isDropboxEnabled(state), _isDropboxEnabled: isDropboxEnabled(state),
_rToken: state['features/dropbox'].rToken,
_tokenExpireDate: state['features/dropbox'].expireDate,
_token: state['features/dropbox'].token _token: state['features/dropbox'].token
}; };
} }