diff --git a/packages/web/src/components/ApolloProvider/index.tsx b/packages/web/src/components/ApolloProvider/index.tsx
index 00716ce2..5be9549a 100644
--- a/packages/web/src/components/ApolloProvider/index.tsx
+++ b/packages/web/src/components/ApolloProvider/index.tsx
@@ -1,7 +1,9 @@
import * as React from 'react';
import { ApolloProvider as BaseApolloProvider } from '@apollo/client';
import { useSnackbar } from 'notistack';
+
import client, { setLink } from 'graphql/client';
+import useAuthentication from 'hooks/useAuthentication';
type ApolloProviderProps = {
children: React.ReactNode;
@@ -9,14 +11,15 @@ type ApolloProviderProps = {
const ApolloProvider = (props: ApolloProviderProps): React.ReactElement => {
const { enqueueSnackbar } = useSnackbar();
+ const authentication = useAuthentication();
const onError = React.useCallback((message) => {
enqueueSnackbar(message, { variant: 'error' });
}, [enqueueSnackbar]);
React.useEffect(() => {
- setLink({ onError })
- }, [onError]);
+ setLink({ onError, token: authentication.token })
+ }, [onError, authentication]);
return (
diff --git a/packages/web/src/components/LoginForm/index.tsx b/packages/web/src/components/LoginForm/index.tsx
index c7ea0cb6..2ed8189f 100644
--- a/packages/web/src/components/LoginForm/index.tsx
+++ b/packages/web/src/components/LoginForm/index.tsx
@@ -6,6 +6,7 @@ import Paper from '@mui/material/Paper';
import Typography from '@mui/material/Typography';
import LoadingButton from '@mui/lab/LoadingButton';
+import useAuthentication from 'hooks/useAuthentication';
import * as URLS from 'config/urls';
import { setItem } from 'helpers/storage';
import { LOGIN } from 'graphql/mutations/login';
@@ -38,6 +39,7 @@ function renderFields(props: { loading: boolean }) {
required
fullWidth
margin="dense"
+ autoComplete="current-password"
/>
{
+ if (authentication.isAuthenticated) {
+ navigate(URLS.DASHBOARD);
+ }
+ }, [authentication.isAuthenticated]);
+
const handleSubmit = async (values: any) => {
const { data } = await login({
variables: {
@@ -68,9 +77,7 @@ function LoginForm() {
const { token } = data.login;
- setItem('token', token);
-
- navigate(URLS.FLOWS);
+ authentication.updateToken(token);
};
const render = React.useMemo(() => renderFields({ loading }), [loading]);
diff --git a/packages/web/src/config/urls.ts b/packages/web/src/config/urls.ts
index e476f2fa..9f608e4b 100644
--- a/packages/web/src/config/urls.ts
+++ b/packages/web/src/config/urls.ts
@@ -1,4 +1,3 @@
-export const DASHBOARD = '/dashboard';
export const CONNECTIONS = '/connections';
export const EXPLORE = '/explore';
@@ -26,3 +25,6 @@ export const FLOWS = '/flows';
// TODO: revert this back to /flows/:flowId once we have a proper single flow page
export const FLOW = (flowId: string): string => `/editor/${flowId}`;
export const FLOW_PATTERN = '/flows/:flowId';
+
+
+export const DASHBOARD = FLOWS;
diff --git a/packages/web/src/contexts/Authentication.tsx b/packages/web/src/contexts/Authentication.tsx
new file mode 100644
index 00000000..d4e4e421
--- /dev/null
+++ b/packages/web/src/contexts/Authentication.tsx
@@ -0,0 +1,39 @@
+import * as React from 'react';
+import { getItem, setItem } from 'helpers/storage';
+
+export type AuthenticationContextParams = {
+ token: string | null;
+ updateToken: (token: string) => void;
+};
+
+export const AuthenticationContext = React.createContext({
+ token: null,
+ updateToken: () => void 0,
+});
+
+type AuthenticationProviderProps = {
+ children: React.ReactNode;
+};
+
+export const AuthenticationProvider = (props: AuthenticationProviderProps): React.ReactElement => {
+ const { children } = props;
+ const [token, setToken] = React.useState(() => getItem('token'));
+
+ const value = React.useMemo(() => {
+ return {
+ token,
+ updateToken: (newToken: string) => {
+ setToken(newToken);
+ setItem('token', newToken);
+ },
+ };
+ }, [token]);
+
+ return (
+
+ {children}
+
+ );
+};
\ No newline at end of file
diff --git a/packages/web/src/graphql/client.ts b/packages/web/src/graphql/client.ts
index 16b4807b..e321c9ba 100644
--- a/packages/web/src/graphql/client.ts
+++ b/packages/web/src/graphql/client.ts
@@ -5,6 +5,7 @@ import appConfig from 'config/app';
type CreateClientOptions = {
onError?: (message: string) => void;
+ token?: string | null;
};
const client = new ApolloClient({
@@ -12,8 +13,9 @@ const client = new ApolloClient({
link: createLink({ uri: appConfig.graphqlUrl })
});
-export function setLink({ onError }: CreateClientOptions): typeof client {
- const link = createLink({ uri: appConfig.graphqlUrl, onError });
+export function setLink(options: CreateClientOptions): typeof client {
+ const { onError, token } = options;
+ const link = createLink({ uri: appConfig.graphqlUrl, token, onError });
client.setLink(link);
diff --git a/packages/web/src/graphql/link.ts b/packages/web/src/graphql/link.ts
index d49b883e..42a4e5c7 100644
--- a/packages/web/src/graphql/link.ts
+++ b/packages/web/src/graphql/link.ts
@@ -3,16 +3,17 @@ import type { ApolloLink } from '@apollo/client';
import { onError } from '@apollo/client/link/error';
import * as URLS from 'config/urls';
-import { getItem } from 'helpers/storage';
type CreateLinkOptions = {
uri: string;
+ token?: string | null;
onError?: (message: string) => void;
};
-const createHttpLink = (uri: CreateLinkOptions['uri']): ApolloLink => {
+const createHttpLink = (options: Pick): ApolloLink => {
+ const { uri, token } = options;
const headers = {
- authorization: getItem('token') || '',
+ authorization: token,
};
return new HttpLink({ uri, headers });
}
@@ -41,8 +42,16 @@ const createErrorLink = (callback: CreateLinkOptions['onError']): ApolloLink =>
// eslint-disable-next-line @typescript-eslint/no-empty-function
const noop = () => {};
-const createLink = ({ uri, onError = noop }: CreateLinkOptions): ApolloLink => {
- return from([createErrorLink(onError), createHttpLink(uri)]);
+const createLink = (options: CreateLinkOptions): ApolloLink => {
+ const {
+ uri,
+ onError = noop,
+ token,
+ } = options;
+
+ const httpOptions = { uri, token };
+
+ return from([createErrorLink(onError), createHttpLink(httpOptions)]);
};
export default createLink;
diff --git a/packages/web/src/hooks/useAuthentication.ts b/packages/web/src/hooks/useAuthentication.ts
index 502a8376..5ee93349 100644
--- a/packages/web/src/hooks/useAuthentication.ts
+++ b/packages/web/src/hooks/useAuthentication.ts
@@ -1,7 +1,19 @@
-import { getItem } from 'helpers/storage';
+import * as React from 'react';
+import { AuthenticationContext } from 'contexts/Authentication';
+import type { AuthenticationContextParams } from 'contexts/Authentication';
-export default function useAuthentication(): boolean {
- const token = getItem('token');
+type UseAuthenticationReturn = {
+ isAuthenticated: boolean;
+ token: AuthenticationContextParams["token"];
+ updateToken: AuthenticationContextParams["updateToken"];
+};
- return Boolean(token);
+export default function useAuthentication(): UseAuthenticationReturn {
+ const authenticationContext = React.useContext(AuthenticationContext);
+
+ return {
+ token: authenticationContext.token,
+ updateToken: authenticationContext.updateToken,
+ isAuthenticated: Boolean(authenticationContext.token),
+ };
}
diff --git a/packages/web/src/index.tsx b/packages/web/src/index.tsx
index 48da2b62..ac150301 100644
--- a/packages/web/src/index.tsx
+++ b/packages/web/src/index.tsx
@@ -4,21 +4,24 @@ import ThemeProvider from 'components/ThemeProvider';
import IntlProvider from 'components/IntlProvider';
import ApolloProvider from 'components/ApolloProvider';
import SnackbarProvider from 'components/SnackbarProvider';
+import { AuthenticationProvider } from 'contexts/Authentication';
import Router from 'components/Router';
import routes from 'routes';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
-
-
-
-
- {routes}
-
-
-
-
+
+
+
+
+
+ {routes}
+
+
+
+
+
,
document.getElementById('root')
)