import * as sdk from '@botpress/sdk' import { DropboxClient } from './dropbox-api' import { getAuthorizationCode } from './dropbox-api/oauth-client' import * as bp from '.botpress' type RegisterProps = Parameters[0] export const register: bp.IntegrationProps['register'] = async (props) => { if (await _isAlreadyAuthenticatedWithSameCredentials(props)) { return } await _authenticate(props) await _saveRegistrationDate(props) } export const unregister: bp.IntegrationProps['unregister'] = async () => {} const _isAlreadyAuthenticatedWithSameCredentials = async (props: RegisterProps): Promise => { try { const dropboxClient = await DropboxClient.create(props) const authenticationSucceeded = await dropboxClient.isProperlyAuthenticated() if (!authenticationSucceeded) { return false } if (props.ctx.configurationType !== 'manual') { return true } // For manual configurations, compare the authorization code from context with the one in state const { state } = await props.client.getState({ type: 'integration', id: props.ctx.integrationId, name: 'authorization', }) const currentAuthorizationCode = getAuthorizationCode({ ctx: props.ctx }) return state.payload.authorizationCode === currentAuthorizationCode } catch {} return false } const _authenticateWithRefreshToken = async (props: RegisterProps): Promise => { const dropboxClient = await DropboxClient.create(props) return dropboxClient.isProperlyAuthenticated() } const _authenticateWithAuthorizationCode = async (props: RegisterProps): Promise => { const { logger } = props await DropboxClient.processAuthorizationCode(props) const dropboxClient = await DropboxClient.create(props) const authenticated = await dropboxClient.isProperlyAuthenticated() if (authenticated) { logger.forBot().info('Successfully created Dropbox client from authorization code') } return authenticated } const _authenticateManual = async (props: RegisterProps): Promise => { const { ctx, logger } = props const authorizationCode = getAuthorizationCode({ ctx }) if (!authorizationCode) { logger.forBot().info('No authorization code provided, using existing refresh token from state') return _authenticateWithRefreshToken(props).catch((err) => { logger.forBot().warn({ err }, 'Failed to authenticate with existing refresh token') return false }) } logger.forBot().info('Using authorization code from context') let isAuthenticated = false isAuthenticated = await _authenticateWithAuthorizationCode(props).catch((err) => { logger?.forBot().warn({ err }, 'Failed to create Dropbox client from authorization code; falling back') return false }) if (!isAuthenticated) { isAuthenticated = await _authenticateWithAuthorizationCode(props).catch((err) => { logger.forBot().error({ err }, 'Failed to authenticate with fallback') return false }) } return isAuthenticated } const _authenticateOAuth = async (props: RegisterProps): Promise => { const { logger } = props logger.forBot().info('Using refresh token from state') return _authenticateWithRefreshToken(props).catch((err) => { logger.forBot().warn({ err }, 'Failed to authenticate with existing refresh token') return false }) } const _getAuthFailureMessage = (configurationType: string): string => { if (configurationType === 'manual') { return ( 'Dropbox authentication failed. ' + 'Please note that the Access Code is only valid for a few minutes. ' + 'You may need to reauthorize your Dropbox application by navigating ' + "to the authorization URL and update the integration's config accordingly." ) } return ( 'Dropbox authentication failed. ' + 'Please use the OAuth wizard to re-authenticate your Dropbox application. ' + 'You can access the wizard through the integration configuration page.' ) } const _authenticate = async (props: RegisterProps): Promise => { const { ctx, logger } = props if (ctx.configurationType !== 'manual') { const authenticationSucceeded = await _authenticateOAuth(props) if (!authenticationSucceeded) { logger.forBot().info('No existing OAuth credentials found. Please complete the OAuth wizard to authenticate.') } return } const authenticationSucceeded = await _authenticateManual(props) if (!authenticationSucceeded) { throw new sdk.RuntimeError(_getAuthFailureMessage(ctx.configurationType ?? '')) } } const _saveRegistrationDate = async (props: RegisterProps): Promise => { await props.client.setState({ id: props.ctx.integrationId, type: 'integration', name: 'setupMeta', payload: { integrationRegisteredAt: new Date().toISOString(), }, }) }