diff --git a/src/components/AnchorForCommentsOnly/AnchorWithAuthToken.js b/src/components/AnchorForCommentsOnly/AnchorWithAuthToken.js deleted file mode 100644 index 14440fc60dee..000000000000 --- a/src/components/AnchorForCommentsOnly/AnchorWithAuthToken.js +++ /dev/null @@ -1,50 +0,0 @@ -import _ from 'underscore'; -import React from 'react'; -import PropTypes from 'prop-types'; -import {withOnyx} from 'react-native-onyx'; -import ONYXKEYS from '../../ONYXKEYS'; -import { - propTypes as anchorForCommentsOnlyPropTypes, - defaultProps as anchorForCommentsOnlyDefaultProps, -} from './anchorForCommentsOnlyPropTypes'; -import BaseAnchorForCommentsOnly from './BaseAnchorForCommentsOnly'; -import addAuthTokenToURL from '../../libs/addAuthTokenToURL'; - -const propTypes = { - /** Session info for the currently logged in user. */ - session: PropTypes.shape({ - /** Currently logged in user authToken */ - authToken: PropTypes.string, - }), - - ...anchorForCommentsOnlyPropTypes, -}; - -const defaultProps = { - session: { - authToken: null, - }, - ...anchorForCommentsOnlyDefaultProps, -}; - -const AnchorWithAuthToken = (props) => { - const urlWithAuthToken = addAuthTokenToURL({ - url: props.href, - authToken: props.session.authToken, - }); - const propsToPass = _.omit(props, 'session'); - propsToPass.href = urlWithAuthToken; - propsToPass.shouldDownloadFile = true; - // eslint-disable-next-line react/jsx-props-no-spreading - return ; -}; - -AnchorWithAuthToken.propTypes = propTypes; -AnchorWithAuthToken.defaultProps = defaultProps; -AnchorWithAuthToken.displayName = 'AnchorWithAuthToken'; - -export default withOnyx({ - session: { - key: ONYXKEYS.SESSION, - }, -})(AnchorWithAuthToken); diff --git a/src/components/AnchorForCommentsOnly/index.js b/src/components/AnchorForCommentsOnly/index.js index 6a5daf6a672b..080324d755d6 100644 --- a/src/components/AnchorForCommentsOnly/index.js +++ b/src/components/AnchorForCommentsOnly/index.js @@ -5,8 +5,8 @@ import { propTypes as anchorForCommentsOnlyPropTypes, defaultProps as anchorForCommentsOnlyDefaultProps, } from './anchorForCommentsOnlyPropTypes'; -import AnchorWithAuthToken from './AnchorWithAuthToken'; import BaseAnchorForCommentsOnly from './BaseAnchorForCommentsOnly'; +import addEncryptedAuthTokenToURL from '../../libs/addEncryptedAuthTokenToURL'; const propTypes = { /** Do we need an auth token to view this link or download the remote resource? */ @@ -27,11 +27,12 @@ const defaultProps = { */ const AnchorForCommentsOnly = (props) => { const propsToPass = _.omit(props, 'isAuthTokenRequired'); - return props.isAuthTokenRequired - // eslint-disable-next-line react/jsx-props-no-spreading - ? - // eslint-disable-next-line react/jsx-props-no-spreading - : ; + if (props.isAuthTokenRequired) { + propsToPass.href = addEncryptedAuthTokenToURL(props.href); + propsToPass.shouldDownloadFile = true; + } + // eslint-disable-next-line react/jsx-props-no-spreading + return ; }; AnchorForCommentsOnly.propTypes = propTypes; diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 599456e55442..6207fa5bb2f8 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -2,14 +2,12 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {View} from 'react-native'; import Str from 'expensify-common/lib/str'; -import {withOnyx} from 'react-native-onyx'; import CONST from '../CONST'; import Modal from './Modal'; import AttachmentView from './AttachmentView'; import styles from '../styles/styles'; import themeColors from '../styles/themes/default'; -import ONYXKEYS from '../ONYXKEYS'; -import addAuthTokenToURL from '../libs/addAuthTokenToURL'; +import addEncryptedAuthTokenToURL from '../libs/addEncryptedAuthTokenToURL'; import compose from '../libs/compose'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; import Button from './Button'; @@ -41,11 +39,6 @@ const propTypes = { /** Do the urls require an authToken? */ isAuthTokenRequired: PropTypes.bool, - /** Current user session */ - session: PropTypes.shape({ - authToken: PropTypes.string.isRequired, - }).isRequired, - ...withLocalizePropTypes, ...windowDimensionsPropTypes, @@ -89,11 +82,9 @@ class AttachmentModal extends PureComponent { } render() { - const sourceURL = addAuthTokenToURL({ - url: this.state.sourceURL, - authToken: this.props.session.authToken, - required: this.props.isAuthTokenRequired, - }); + const sourceURL = this.props.isAuthTokenRequired + ? addEncryptedAuthTokenToURL(this.state.sourceURL) + : this.state.sourceURL; const attachmentViewStyles = this.props.isSmallScreenWidth ? [styles.imageModalImageCenterContainer] @@ -164,10 +155,5 @@ AttachmentModal.propTypes = propTypes; AttachmentModal.defaultProps = defaultProps; export default compose( withWindowDimensions, - withOnyx({ - session: { - key: ONYXKEYS.SESSION, - }, - }), withLocalize, )(AttachmentModal); diff --git a/src/components/ThumbnailImage.js b/src/components/ThumbnailImage.js index 83280826602f..e1bebb06656a 100644 --- a/src/components/ThumbnailImage.js +++ b/src/components/ThumbnailImage.js @@ -1,10 +1,8 @@ import React, {PureComponent} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; -import {withOnyx} from 'react-native-onyx'; -import ONYXKEYS from '../ONYXKEYS'; import ImageWithSizeCalculation from './ImageWithSizeCalculation'; -import addAuthTokenToURL from '../libs/addAuthTokenToURL'; +import addEncryptedAuthTokenToURL from '../libs/addEncryptedAuthTokenToURL'; import styles, {getWidthAndHeightStyle} from '../styles/styles'; const propTypes = { @@ -15,11 +13,6 @@ const propTypes = { // eslint-disable-next-line react/forbid-prop-types style: PropTypes.any, - /** Current user session */ - session: PropTypes.shape({ - authToken: PropTypes.string.isRequired, - }).isRequired, - /** Do the urls require an authToken? */ isAuthTokenRequired: PropTypes.bool.isRequired, }; @@ -50,11 +43,9 @@ class ThumbnailImage extends PureComponent { } render() { - const url = addAuthTokenToURL({ - url: this.props.previewSourceURL, - authToken: this.props.session.authToken, - required: this.props.isAuthTokenRequired, - }); + const url = this.props.isAuthTokenRequired + ? addEncryptedAuthTokenToURL(this.props.previewSourceURL) + : this.props.previewSourceURL; return ( { - const {authToken, email} = authenticateResponse; - createTemporaryLogin(authToken, email); + const {authToken, encryptedAuthToken, email} = authenticateResponse; + createTemporaryLogin(authToken, encryptedAuthToken, email); }) .catch((error) => { Onyx.merge(ONYXKEYS.ACCOUNT, {error: error.message, loading: false}); @@ -256,7 +257,7 @@ function setPassword(password, validateCode, accountID) { }) .then((response) => { if (response.jsonCode === 200) { - createTemporaryLogin(response.authToken, response.email); + createTemporaryLogin(response.authToken, response.encryptedAuthToken, response.email); return; } diff --git a/src/libs/addAuthTokenToURL.js b/src/libs/addAuthTokenToURL.js deleted file mode 100644 index 41c89590385b..000000000000 --- a/src/libs/addAuthTokenToURL.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Add authToken to this attachment URL if necessary - * - * @param {Object} parameters - * @param {String} parameters.url - * @param {Boolean} parameters.required - * @param {String} parameters.authToken - * @returns {String} - */ -export default function ({url, authToken, required = true}) { - return required - ? `${url}?authToken=${authToken}` - : url; -} diff --git a/src/libs/addEncryptedAuthTokenToURL.js b/src/libs/addEncryptedAuthTokenToURL.js new file mode 100644 index 000000000000..82c4c1ac640f --- /dev/null +++ b/src/libs/addEncryptedAuthTokenToURL.js @@ -0,0 +1,19 @@ +import lodashGet from 'lodash/get'; +import Onyx from 'react-native-onyx'; +import ONYXKEYS from '../ONYXKEYS'; + +let encryptedAuthToken = ''; +Onyx.connect({ + key: ONYXKEYS.SESSION, + callback: session => encryptedAuthToken = lodashGet(session, 'encryptedAuthToken', ''), +}); + +/** + * Add encryptedAuthToken to this attachment URL + * + * @param {String} url + * @returns {String} + */ +export default function (url) { + return `${url}?encryptedAuthToken=${encryptedAuthToken}`; +} diff --git a/src/libs/migrateOnyx.js b/src/libs/migrateOnyx.js index 8b0d428076c2..3466bc2606b6 100644 --- a/src/libs/migrateOnyx.js +++ b/src/libs/migrateOnyx.js @@ -1,3 +1,4 @@ +import AddEncryptedAuthToken from './migrations/AddEncryptedAuthToken'; import RenameActiveClientsKey from './migrations/RenameActiveClientsKey'; import RenamePriorityModeKey from './migrations/RenamePriorityModeKey'; @@ -10,6 +11,7 @@ export default function () { const migrationPromises = [ RenameActiveClientsKey, RenamePriorityModeKey, + AddEncryptedAuthToken, ]; // Reduce all promises down to a single promise. All promises run in a linear fashion, waiting for the diff --git a/src/libs/migrations/AddEncryptedAuthToken.js b/src/libs/migrations/AddEncryptedAuthToken.js new file mode 100644 index 000000000000..24660b4f3a91 --- /dev/null +++ b/src/libs/migrations/AddEncryptedAuthToken.js @@ -0,0 +1,41 @@ +import _ from 'underscore'; +import Onyx from 'react-native-onyx'; +import ONYXKEYS from '../../ONYXKEYS'; +import {reauthenticate} from '../API'; + +/** + * This migration adds an encryptedAuthToken to the SESSION key, if it is not present. + * + * @returns {Promise} + */ +export default function () { + return new Promise((resolve) => { + const connectionID = Onyx.connect({ + key: ONYXKEYS.SESSION, + callback: (session) => { + Onyx.disconnect(connectionID); + + if (session && !_.isEmpty(session.encryptedAuthToken)) { + console.debug('[Migrate Onyx] Skipped migration AddEncryptedAuthToken'); + return resolve(); + } + + if (!session || !session.authToken) { + console.debug('[Migrate Onyx] Skipped migration AddEncryptedAuthToken'); + return resolve(); + } + + // If there is an auth token but no encrypted auth token, reauthenticate. + if (session.authToken && _.isUndefined(session.encryptedAuthToken)) { + return reauthenticate('Onyx_Migration_AddEncryptedAuthToken') + .then(() => { + console.debug('[Migrate Onyx] Ran migration AddEncryptedAuthToken'); + return resolve(); + }); + } + + return resolve(); + }, + }); + }); +}