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": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/dropbox/-/dropbox-4.0.9.tgz",
"integrity": "sha512-UeaKw7DY24ZGLRV8xboZvbZXhbTVrFjPjfpr0LfF/KVOzBUad9vJJwqz3udqTLNxD0FXbFlC9rlNLLNXaj9msg==",
"version": "10.7.0",
"resolved": "https://registry.npmjs.org/dropbox/-/dropbox-10.7.0.tgz",
"integrity": "sha512-btNLOYHxukACfnkEUNhlTPCnkecfbL89mrPU3RMKAWdCQXM18aRLm+t+0xIpzvRUSGeXPER+3d+QJk5Wi+4QGw==",
"requires": {
"buffer": "^5.0.8",
"moment": "^2.19.3"
"node-fetch": "^2.6.1"
},
"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": {

View File

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

View File

@ -24,7 +24,7 @@ export function authorizeDropbox() {
_authorizeDropbox(dropbox.appKey, redirectURI)
.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.
*
* @param {string} token - The new token.
* @param {string} rToken - The refresh token.
* @param {string} expireDate - The token expiration date as ISO string.
* @returns {{
* 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 {
type: UPDATE_DROPBOX_TOKEN,
token
token,
rToken,
expireDate
};
}

View File

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

View File

@ -1,12 +1,8 @@
// @flow
import { Dropbox } from 'dropbox';
import { Dropbox, DropboxAuth } from 'dropbox';
import {
getJitsiMeetGlobalNS,
parseStandardURIString,
parseURLParams
} from '../base/util';
import { getJitsiMeetGlobalNS } from '../base/util';
/**
* 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.
*
@ -41,16 +47,47 @@ function authorize(authUrl: string): Promise<string> {
export function _authorizeDropbox(
appKey: string,
redirectURI: string
): Promise<string> {
const dropboxAPI = new Dropbox({ clientId: appKey });
const url = dropboxAPI.getAuthenticationUrl(redirectURI);
): Promise<Object> {
const dropbox = new DropboxAuth({ clientId: appKey });
return authorize(url).then(returnUrl => {
const params
= parseURLParams(parseStandardURIString(returnUrl), true) || {};
return dropbox.getAuthenticationUrl(redirectURI, undefined, 'code', 'offline', undefined, undefined, true)
.then(authorize)
.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 (
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 => {
const { allocation, used } = space;
const { allocation, used } = space.result;
const { allocated } = allocation;
return {

View File

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

View File

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