From 9cc10f634606760203e9249bc1029f4ec7c88d34 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Fri, 15 Oct 2021 18:15:46 +0200 Subject: [PATCH] feat: Introduce auth step to verify connection --- .../src/components/AddAppConnection/index.tsx | 75 ++----------------- packages/web/src/config/app.ts | 1 + packages/web/src/graphql/mutations/index.ts | 2 + .../graphql/mutations/update-credential.ts | 15 ++++ .../web/src/helpers/authenticationSteps.ts | 70 +++++++++++++++++ 5 files changed, 96 insertions(+), 67 deletions(-) create mode 100644 packages/web/src/graphql/mutations/update-credential.ts create mode 100644 packages/web/src/helpers/authenticationSteps.ts diff --git a/packages/web/src/components/AddAppConnection/index.tsx b/packages/web/src/components/AddAppConnection/index.tsx index eb4adca9..8bc422d4 100644 --- a/packages/web/src/components/AddAppConnection/index.tsx +++ b/packages/web/src/components/AddAppConnection/index.tsx @@ -1,5 +1,4 @@ import { useCallback, useEffect } from 'react'; -import { useApolloClient } from '@apollo/client'; import DialogTitle from '@mui/material/DialogTitle'; import DialogContent from '@mui/material/DialogContent'; import DialogContentText from '@mui/material/DialogContentText'; @@ -8,8 +7,8 @@ import Button from '@mui/material/Button'; import { FieldValues, SubmitHandler } from 'react-hook-form'; import computeAuthStepVariables from 'helpers/computeAuthStepVariables'; +import { processStep } from 'helpers/authenticationSteps'; import InputCreator from 'components/InputCreator'; -import MUTATIONS from 'graphql/mutations'; import type { App } from 'types/app'; import { Form } from './style'; @@ -22,24 +21,6 @@ type Response = { [key: string]: any; } -const BASE_URL = 'http://localhost:3001'; - -const parseData = (event: any) => { - const searchParams = new URLSearchParams(event.data); - - return getObjectOfEntries(searchParams.entries()); -}; - -function getObjectOfEntries(iterator: any) { - const result: any = {}; - - for (const [key, value] of iterator) { - result[key] = value; - } - - return result; -} - function* authStepGenerator(steps: any[]) { for (const step of steps) { yield step; @@ -52,8 +33,6 @@ export default function AddAppConnection(props: AddAppConnectionProps){ const { application, onClose } = props; const { key, fields, authenticationSteps } = application; - const apollo = useApolloClient(); - useEffect(() => { if (window.opener) { window.opener.postMessage({ source: 'automatisch', payload: window.location.search }); @@ -69,56 +48,18 @@ export default function AddAppConnection(props: AddAppConnectionProps){ const stepGenerator = authStepGenerator(authenticationSteps); - const processStep = async (step: any) => { + let authenticationStep; + while (!(authenticationStep = stepGenerator.next()).done) { + const step = authenticationStep.value; const variables = computeAuthStepVariables(step, response); - if (step.type === 'mutation') { - const mutation = MUTATIONS[step.name]; - const mutationResponse = await apollo.mutate({ mutation, variables }); - const responseData = mutationResponse.data[step.name]; + const stepResponse = await processStep(step, variables); - response[step.name] = responseData; - - const nextStep = stepGenerator.next(); - - if (!nextStep.done) { - await processStep(nextStep.value); - } - } else if (step.type === 'openWithPopup') { - const windowFeatures = 'toolbar=no, menubar=no, width=600, height=700, top=100, left=100'; - const url = variables.url; - - const popup: any = window.open(url, '_blank', windowFeatures); - popup?.focus(); - - const messageHandler = async (event: any) => { - // check origin and data.source to trust the event - if (event.origin !== BASE_URL || event.data.source !== 'automatisch') { - return; - } - - const data = parseData(event); - response[step.name] = data; - - const nextStep = stepGenerator.next(); - if (!nextStep.done) { - await processStep(nextStep.value); - } - - window.removeEventListener('message', messageHandler); - }; - - window.addEventListener('message', messageHandler, false); - } + response[step.name] = stepResponse; } - const firstStep = stepGenerator.next(); - - if (!firstStep.done) { - await processStep(firstStep.value); - } - - }, [apollo, authenticationSteps, key]); + onClose?.(); + }, [authenticationSteps, key, onClose]); return ( diff --git a/packages/web/src/config/app.ts b/packages/web/src/config/app.ts index f85b7a2a..7a18be9a 100644 --- a/packages/web/src/config/app.ts +++ b/packages/web/src/config/app.ts @@ -3,6 +3,7 @@ type Config = { }; const config: Config = { + baseUrl: process.env.REACT_APP_BASE_URL as string, graphqlUrl: process.env.REACT_APP_GRAPHQL_URL as string, }; diff --git a/packages/web/src/graphql/mutations/index.ts b/packages/web/src/graphql/mutations/index.ts index 77751b2c..1291f8f9 100644 --- a/packages/web/src/graphql/mutations/index.ts +++ b/packages/web/src/graphql/mutations/index.ts @@ -1,5 +1,6 @@ import { CREATE_CREDENTIAL } from './create-credentials'; import { CREATE_AUTH_LINK } from './create-auth-link'; +import { UPDATE_CREDENTIAL } from './update-credential'; type Mutations = { [key: string]: any, @@ -7,6 +8,7 @@ type Mutations = { const mutations: Mutations = { createCredential: CREATE_CREDENTIAL, + updateCredential: UPDATE_CREDENTIAL, createAuthLink: CREATE_AUTH_LINK, }; diff --git a/packages/web/src/graphql/mutations/update-credential.ts b/packages/web/src/graphql/mutations/update-credential.ts new file mode 100644 index 00000000..4f1b4502 --- /dev/null +++ b/packages/web/src/graphql/mutations/update-credential.ts @@ -0,0 +1,15 @@ +import { gql } from '@apollo/client'; + +export const UPDATE_CREDENTIAL = gql` + mutation UpdateCredential($id: String!, $data: twitterCredentialInput!) { + updateCredential(id: $id, data: $data) { + id + key + verified + data { + consumerKey + consumerSecret + } + } + } +`; diff --git a/packages/web/src/helpers/authenticationSteps.ts b/packages/web/src/helpers/authenticationSteps.ts new file mode 100644 index 00000000..385ff8f6 --- /dev/null +++ b/packages/web/src/helpers/authenticationSteps.ts @@ -0,0 +1,70 @@ +import apolloClient from 'graphql/client'; +import MUTATIONS from 'graphql/mutations'; +import appConfig from 'config/app'; + +enum AuthenticationSteps { + Mutation = 'mutation', + OpenWithPopup = 'openWithPopup', +} + +const processMutation = async (step: any, variables: any) => { + const mutation = MUTATIONS[step.name]; + const mutationResponse = await apolloClient.mutate({ mutation, variables }); + const responseData = mutationResponse.data[step.name]; + + return responseData; +}; + +const parseUrlSearchParams = (event: any) => { + const searchParams = new URLSearchParams(event.data.payload); + + return getObjectOfEntries(searchParams.entries()); +}; + +function getObjectOfEntries(iterator: any) { + const result: any = {}; + + for (const [key, value] of iterator) { + result[key] = value; + } + + return result; +} + +const processOpenWithPopup = (step: any, variables: any) => { + return new Promise((resolve, reject) => { + const windowFeatures = 'toolbar=no, menubar=no, width=600, height=700, top=100, left=100'; + const url = variables.url; + + const popup: any = window.open(url, '_blank', windowFeatures); + popup?.focus(); + + const messageHandler = async (event: any) => { + // check origin and data.source to trust the event + if (event.origin !== appConfig.baseUrl || event.data.source !== 'automatisch') { + return; + } + + const data = parseUrlSearchParams(event); + window.removeEventListener('message', messageHandler); + + resolve(data); + }; + + window.addEventListener('message', messageHandler, false); + }); +}; + +export const processStep = (step: any, variables: any) => { + return new Promise(async (resolve, reject) => { + let response; + + if (step.type === AuthenticationSteps.Mutation) { + response = await processMutation(step, variables); + } else if (step.type === AuthenticationSteps.OpenWithPopup) { + response = await processOpenWithPopup(step, variables); + } + + resolve(response); + }); +};