diff --git a/packages/cozy-harvest-lib/src/components/EditAccountModal.jsx b/packages/cozy-harvest-lib/src/components/EditAccountModal.jsx
index 3c9dde842b..c0e40c4101 100644
--- a/packages/cozy-harvest-lib/src/components/EditAccountModal.jsx
+++ b/packages/cozy-harvest-lib/src/components/EditAccountModal.jsx
@@ -95,8 +95,10 @@ const DumbEditAccountModal = withRouter(
showError={true}
onVaultDismiss={redirectToAccount}
fieldOptions={fieldOptions}
+ reconnect={fromReconnect}
/>
)}
+
)
diff --git a/packages/cozy-harvest-lib/src/components/OAuthForm.jsx b/packages/cozy-harvest-lib/src/components/OAuthForm.jsx
index db1f36f661..6a8a575664 100644
--- a/packages/cozy-harvest-lib/src/components/OAuthForm.jsx
+++ b/packages/cozy-harvest-lib/src/components/OAuthForm.jsx
@@ -20,14 +20,12 @@ export class OAuthForm extends PureComponent {
this.handleOAuthCancel = this.handleOAuthCancel.bind(this)
this.handleExtraParams = this.handleExtraParams.bind(this)
this.state = {
- initialValues: null,
showingOAuthModal: false
}
}
componentDidMount() {
const { account, konnector, flow, client } = this.props
- this.setState({ initialValues: account ? account.oauth : null })
const konnectorPolicy = findKonnectorPolicy(konnector)
@@ -72,30 +70,33 @@ export class OAuthForm extends PureComponent {
}
render() {
- const { konnector, t, flowState } = this.props
- const { initialValues, showOAuthWindow, needExtraParams, extraParams } =
- this.state
+ const { konnector, t, flowState, reconnect, account } = this.props
+ const { showOAuthWindow, needExtraParams, extraParams } = this.state
const isBusy =
showOAuthWindow === true ||
flowState.running ||
(needExtraParams && !extraParams)
- return initialValues ? null : (
+ const buttonLabel = reconnect ? 'oauth.reconnect.label' : 'oauth.connect.label'
+
+ return (
<>
{showOAuthWindow && (
)}
>
@@ -111,7 +112,9 @@ OAuthForm.propTypes = {
/** Success callback, takes account as parameter */
onSuccess: PropTypes.func,
/** Translation function */
- t: PropTypes.func.isRequired
+ t: PropTypes.func.isRequired,
+ /** Is it a reconnection or not */
+ reconnect: PropTypes.bool
}
export default compose(translate(), withConnectionFlow())(OAuthForm)
diff --git a/packages/cozy-harvest-lib/src/components/OAuthForm.spec.js b/packages/cozy-harvest-lib/src/components/OAuthForm.spec.js
index c3bf0b4076..36f78d6c9f 100644
--- a/packages/cozy-harvest-lib/src/components/OAuthForm.spec.js
+++ b/packages/cozy-harvest-lib/src/components/OAuthForm.spec.js
@@ -29,16 +29,17 @@ describe('OAuthForm', () => {
expect(component).toMatchSnapshot()
})
- it('should not render button when update', () => {
+ it('should render reconnect button when updating an account', () => {
const component = shallow(
).getElement()
- expect(component).toBeNull()
+ expect(component).toMatchSnapshot()
})
it('should call policy fetchExtraOAuthUrlParams with proper params', () => {
shallow(
diff --git a/packages/cozy-harvest-lib/src/components/OAuthWindow.jsx b/packages/cozy-harvest-lib/src/components/OAuthWindow.jsx
index 946213cef4..beb6459296 100644
--- a/packages/cozy-harvest-lib/src/components/OAuthWindow.jsx
+++ b/packages/cozy-harvest-lib/src/components/OAuthWindow.jsx
@@ -45,7 +45,7 @@ export class OAuthWindow extends PureComponent {
}
componentDidMount() {
- const { client, konnector, redirectSlug, extraParams } = this.props
+ const { client, konnector, redirectSlug, extraParams, reconnect, account } = this.props
this.realtime = new CozyRealtime({ client })
this.realtime.subscribe(
'notified',
@@ -57,7 +57,9 @@ export class OAuthWindow extends PureComponent {
client,
konnector,
redirectSlug,
- extraParams
+ extraParams,
+ reconnect,
+ account
)
this.setState({ oAuthStateKey, oAuthUrl, succeed: false })
}
@@ -171,7 +173,11 @@ OAuthWindow.propTypes = {
an account id */
onCancel: PropTypes.func,
/** The app we want to redirect the user on, after the OAuth flow. It used by the stack */
- redirectSlug: PropTypes.string
+ redirectSlug: PropTypes.string,
+ /** Is it a reconnection or not */
+ reconnect: PropTypes.bool,
+ /** Existing account */
+ account: PropTypes.object
}
export default translate()(withClient(OAuthWindow))
diff --git a/packages/cozy-harvest-lib/src/components/TriggerManager.jsx b/packages/cozy-harvest-lib/src/components/TriggerManager.jsx
index 5f57b6cd77..ed3fda0ca3 100644
--- a/packages/cozy-harvest-lib/src/components/TriggerManager.jsx
+++ b/packages/cozy-harvest-lib/src/components/TriggerManager.jsx
@@ -320,7 +320,8 @@ export class DumbTriggerManager extends Component {
flow,
flowState,
client,
- OAuthFormWrapperComp
+ OAuthFormWrapperComp,
+ reconnect
} = this.props
const submitting = flowState.running
@@ -345,6 +346,7 @@ export class DumbTriggerManager extends Component {
client={client}
flow={flow}
account={account}
+ reconnect={reconnect}
konnector={konnector}
onSuccess={this.handleOAuthAccountId}
/>
@@ -444,7 +446,9 @@ DumbTriggerManager.propTypes = {
flow: PropTypes.object,
flowState: PropTypes.object,
// Used to inject a component around OAuthForm, and so customize the UI from the app
- OAuthFormWrapperComp: PropTypes.node
+ OAuthFormWrapperComp: PropTypes.node,
+ /** Is it a reconnection or not */
+ reconnect: PropTypes.bool,
}
const TriggerManager = compose(
diff --git a/packages/cozy-harvest-lib/src/components/__snapshots__/OAuthForm.spec.js.snap b/packages/cozy-harvest-lib/src/components/__snapshots__/OAuthForm.spec.js.snap
index 89d19a0799..d4092bf1d5 100644
--- a/packages/cozy-harvest-lib/src/components/__snapshots__/OAuthForm.spec.js.snap
+++ b/packages/cozy-harvest-lib/src/components/__snapshots__/OAuthForm.spec.js.snap
@@ -12,3 +12,16 @@ exports[`OAuthForm should render 1`] = `
/>
`;
+
+exports[`OAuthForm should render reconnect button when update 1`] = `
+
+
+
+`;
diff --git a/packages/cozy-harvest-lib/src/helpers/oauth.js b/packages/cozy-harvest-lib/src/helpers/oauth.js
index 0b9ffeb13f..f125a413bf 100644
--- a/packages/cozy-harvest-lib/src/helpers/oauth.js
+++ b/packages/cozy-harvest-lib/src/helpers/oauth.js
@@ -100,6 +100,9 @@ export const handleOAuthResponse = (options = {}) => {
* @param {string} redirectSlug The app we want to redirect the user on after the end of the flow
* @param {string} nonce unique nonce string
* @param {Object} extraParams some extra parameters to add to the query string
+ * @param {Boolean} reconnect Are we trying to reconnect an existing account ?
+ * @param {io.cozy.accounts} account targeted account if any
+ * @returns {String} final OAuth url string
*/
export const getOAuthUrl = ({
cozyUrl,
@@ -108,9 +111,18 @@ export const getOAuthUrl = ({
oAuthConf = {},
nonce,
redirectSlug,
- extraParams
+ extraParams,
+ reconnect,
+ account
}) => {
- let oAuthUrl = `${cozyUrl}/accounts/${accountType}/start?state=${oAuthStateKey}&nonce=${nonce}`
+ const startOrReconnect = reconnect ? 'reconnect' : 'start'
+ const accountIdParam = reconnect ? account._id + '/' : ''
+ const oAuthUrl = new URL(
+ `${cozyUrl}/accounts/${accountType}/${accountIdParam}${startOrReconnect}`
+ )
+ oAuthUrl.searchParams.set('state', oAuthStateKey)
+ oAuthUrl.searchParams.set('nonce', nonce)
+
if (
oAuthConf.scope !== undefined &&
oAuthConf.scope !== null &&
@@ -119,19 +131,19 @@ export const getOAuthUrl = ({
const urlScope = Array.isArray(oAuthConf.scope)
? oAuthConf.scope.join('+')
: oAuthConf.scope
- oAuthUrl += `&scope=${urlScope}`
+ oAuthUrl.searchParams.set('scope', urlScope)
}
if (redirectSlug) {
- oAuthUrl += `&slug=${redirectSlug}`
+ oAuthUrl.searchParams.set('slug', redirectSlug)
}
if (extraParams) {
- for (const key in extraParams) {
- oAuthUrl += `&${key}=${extraParams[key]}`
- }
+ Object.entries(extraParams).forEach(([key, value]) =>
+ oAuthUrl.searchParams.set(key, value)
+ )
}
- return oAuthUrl
+ return oAuthUrl.toString()
}
const getAppSlug = client => {
@@ -145,10 +157,19 @@ const getAppSlug = client => {
* @param {string} domain Cozy domain
* @param {Object} konnector
* @param {string} redirectSlug The app we want to redirect the user on after the end of the flow
- * @return {Object} Object containing: `oAuthUrl` (URL of cozy stack
- * OAuth endpoint) and `oAuthStateKey` (localStorage key)
+ * @param {Object} extraParams some extra parameters to add to the query string
+ * @param {Boolean} reconnect Are we trying to reconnect an existing account ?
+ * @param {io.cozy.accounts} account targetted account if any
+ * @return {Object} Object containing: `oAuthUrl` (URL of cozy stack OAuth endpoint) and `oAuthStateKey` (localStorage key)
*/
-export const prepareOAuth = (client, konnector, redirectSlug, extraParams) => {
+export const prepareOAuth = (
+ client,
+ konnector,
+ redirectSlug,
+ extraParams,
+ reconnect = false,
+ account
+) => {
const { oauth } = konnector
const accountType = konnectors.getAccountType(konnector)
@@ -168,7 +189,9 @@ export const prepareOAuth = (client, konnector, redirectSlug, extraParams) => {
oAuthConf: oauth,
nonce: Date.now(),
redirectSlug: redirectSlug || getAppSlug(client),
- extraParams
+ extraParams,
+ reconnect,
+ account
})
return { oAuthStateKey, oAuthUrl }
diff --git a/packages/cozy-harvest-lib/src/helpers/oauth.spec.js b/packages/cozy-harvest-lib/src/helpers/oauth.spec.js
index 1a51800994..b3370cdb1d 100644
--- a/packages/cozy-harvest-lib/src/helpers/oauth.spec.js
+++ b/packages/cozy-harvest-lib/src/helpers/oauth.spec.js
@@ -50,7 +50,7 @@ describe('Oauth helper', () => {
oAuthConf: { scope: ['thescope', 'thescope2'] }
})
expect(url).toEqual(
- 'https://cozyurl/accounts/testslug/start?state=statekey&nonce=1234&scope=thescope+thescope2'
+ 'https://cozyurl/accounts/testslug/start?state=statekey&nonce=1234&scope=thescope%2Bthescope2'
)
})
it('should use redirectSlug if present', () => {
@@ -76,6 +76,21 @@ describe('Oauth helper', () => {
'https://cozyurl/accounts/testslug/start?state=statekey&nonce=1234&token=thetoken&id_connector=40'
)
})
+ it('should return reconnect url with account id if reconnect', () => {
+ const url = getOAuthUrl({
+ ...defaultConf,
+ oAuthConf: {},
+ account: { _id: 'accountid' },
+ reconnect: true,
+ extraParams: {
+ code: 'thecode',
+ connection_id: 12345
+ }
+ })
+ expect(url).toEqual(
+ 'https://cozyurl/accounts/testslug/accountid/reconnect?state=statekey&nonce=1234&code=thecode&connection_id=12345'
+ )
+ })
})
describe('handleOAuthResponse', () => {
let originalLocation
diff --git a/packages/cozy-harvest-lib/src/locales/en.json b/packages/cozy-harvest-lib/src/locales/en.json
index 54da77e2c4..818fcf7aec 100644
--- a/packages/cozy-harvest-lib/src/locales/en.json
+++ b/packages/cozy-harvest-lib/src/locales/en.json
@@ -386,6 +386,9 @@
}
},
"oauth": {
+ "reconnect": {
+ "label": "Reconnect"
+ },
"connect": {
"label": "Connect"
},
diff --git a/packages/cozy-harvest-lib/src/locales/fr.json b/packages/cozy-harvest-lib/src/locales/fr.json
index 14c815dcce..6cfcafe719 100644
--- a/packages/cozy-harvest-lib/src/locales/fr.json
+++ b/packages/cozy-harvest-lib/src/locales/fr.json
@@ -386,6 +386,9 @@
}
},
"oauth": {
+ "reconnect": {
+ "label": "Se reconnecter"
+ },
"connect": {
"label": "Connecter"
},
diff --git a/packages/cozy-harvest-lib/src/services/biWebView.js b/packages/cozy-harvest-lib/src/services/biWebView.js
index cc46681be8..fcf324b479 100644
--- a/packages/cozy-harvest-lib/src/services/biWebView.js
+++ b/packages/cozy-harvest-lib/src/services/biWebView.js
@@ -13,7 +13,8 @@ import {
saveBIConfig,
findAccountWithBiConnection,
convertBIErrortoKonnectorJobError,
- isBudgetInsightConnector
+ isBudgetInsightConnector,
+ getBIConnectionIdFromAccount
} from './budget-insight'
import { KonnectorJobError } from '../helpers/konnectors'
import { waitForRealtimeEvent } from './jobUtils'
@@ -189,6 +190,36 @@ export const onBIAccountCreation = async ({
return updatedAccount
}
+/**
+ * Create OAuth extra parameters specific to reconnect webview
+ *
+ * @param {Number} options.biBankId - Connector bank id (compatible with non webview bi connectors)
+ * @param {Array} options.biBankIds - connector bank ids (for webview connectors)
+ * @param {String} options.token - BI temporary token
+ * @param {Number} options.connId - BI bi connection id
+ * @return {Object}
+ */
+const getReconnectExtraOAuthUrlParams = async ({
+ biBankId,
+ biBankIds,
+ token,
+ connId
+}) => {
+ return {
+ id_connector: biBankId || biBankIds,
+ code: token,
+ connection_id: connId
+ }
+}
+
+/**
+ * Create OAuth extra parameters
+ *
+ * @param {CozyClient} options.client - CozyClient instance
+ * @param {io.cozy.konnectors} options.konnector connector manifest content
+ * @param {io.cozy.accounts} options.account The account content
+ * @return {Promise