feat: Introduce auth step to verify connection
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
import { useApolloClient } from '@apollo/client';
|
|
||||||
import DialogTitle from '@mui/material/DialogTitle';
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
import DialogContent from '@mui/material/DialogContent';
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
import DialogContentText from '@mui/material/DialogContentText';
|
import DialogContentText from '@mui/material/DialogContentText';
|
||||||
@@ -8,8 +7,8 @@ import Button from '@mui/material/Button';
|
|||||||
import { FieldValues, SubmitHandler } from 'react-hook-form';
|
import { FieldValues, SubmitHandler } from 'react-hook-form';
|
||||||
|
|
||||||
import computeAuthStepVariables from 'helpers/computeAuthStepVariables';
|
import computeAuthStepVariables from 'helpers/computeAuthStepVariables';
|
||||||
|
import { processStep } from 'helpers/authenticationSteps';
|
||||||
import InputCreator from 'components/InputCreator';
|
import InputCreator from 'components/InputCreator';
|
||||||
import MUTATIONS from 'graphql/mutations';
|
|
||||||
import type { App } from 'types/app';
|
import type { App } from 'types/app';
|
||||||
import { Form } from './style';
|
import { Form } from './style';
|
||||||
|
|
||||||
@@ -22,24 +21,6 @@ type Response = {
|
|||||||
[key: string]: any;
|
[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[]) {
|
function* authStepGenerator(steps: any[]) {
|
||||||
for (const step of steps) {
|
for (const step of steps) {
|
||||||
yield step;
|
yield step;
|
||||||
@@ -52,8 +33,6 @@ export default function AddAppConnection(props: AddAppConnectionProps){
|
|||||||
const { application, onClose } = props;
|
const { application, onClose } = props;
|
||||||
const { key, fields, authenticationSteps } = application;
|
const { key, fields, authenticationSteps } = application;
|
||||||
|
|
||||||
const apollo = useApolloClient();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (window.opener) {
|
if (window.opener) {
|
||||||
window.opener.postMessage({ source: 'automatisch', payload: window.location.search });
|
window.opener.postMessage({ source: 'automatisch', payload: window.location.search });
|
||||||
@@ -69,56 +48,18 @@ export default function AddAppConnection(props: AddAppConnectionProps){
|
|||||||
|
|
||||||
const stepGenerator = authStepGenerator(authenticationSteps);
|
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);
|
const variables = computeAuthStepVariables(step, response);
|
||||||
|
|
||||||
if (step.type === 'mutation') {
|
const stepResponse = await processStep(step, variables);
|
||||||
const mutation = MUTATIONS[step.name];
|
|
||||||
const mutationResponse = await apollo.mutate({ mutation, variables });
|
|
||||||
const responseData = mutationResponse.data[step.name];
|
|
||||||
|
|
||||||
response[step.name] = responseData;
|
response[step.name] = stepResponse;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const firstStep = stepGenerator.next();
|
onClose?.();
|
||||||
|
}, [authenticationSteps, key, onClose]);
|
||||||
if (!firstStep.done) {
|
|
||||||
await processStep(firstStep.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
}, [apollo, authenticationSteps, key]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={true} onClose={onClose}>
|
<Dialog open={true} onClose={onClose}>
|
||||||
|
@@ -3,6 +3,7 @@ type Config = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const config: Config = {
|
const config: Config = {
|
||||||
|
baseUrl: process.env.REACT_APP_BASE_URL as string,
|
||||||
graphqlUrl: process.env.REACT_APP_GRAPHQL_URL as string,
|
graphqlUrl: process.env.REACT_APP_GRAPHQL_URL as string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { CREATE_CREDENTIAL } from './create-credentials';
|
import { CREATE_CREDENTIAL } from './create-credentials';
|
||||||
import { CREATE_AUTH_LINK } from './create-auth-link';
|
import { CREATE_AUTH_LINK } from './create-auth-link';
|
||||||
|
import { UPDATE_CREDENTIAL } from './update-credential';
|
||||||
|
|
||||||
type Mutations = {
|
type Mutations = {
|
||||||
[key: string]: any,
|
[key: string]: any,
|
||||||
@@ -7,6 +8,7 @@ type Mutations = {
|
|||||||
|
|
||||||
const mutations: Mutations = {
|
const mutations: Mutations = {
|
||||||
createCredential: CREATE_CREDENTIAL,
|
createCredential: CREATE_CREDENTIAL,
|
||||||
|
updateCredential: UPDATE_CREDENTIAL,
|
||||||
createAuthLink: CREATE_AUTH_LINK,
|
createAuthLink: CREATE_AUTH_LINK,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
15
packages/web/src/graphql/mutations/update-credential.ts
Normal file
15
packages/web/src/graphql/mutations/update-credential.ts
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
70
packages/web/src/helpers/authenticationSteps.ts
Normal file
70
packages/web/src/helpers/authenticationSteps.ts
Normal file
@@ -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);
|
||||||
|
});
|
||||||
|
};
|
Reference in New Issue
Block a user