diff --git a/src/components/organisms/sidePanel/CreateEnv.tsx b/src/components/organisms/sidePanel/CreateEnv.tsx index e8b0d97..c76b378 100644 --- a/src/components/organisms/sidePanel/CreateEnv.tsx +++ b/src/components/organisms/sidePanel/CreateEnv.tsx @@ -6,7 +6,6 @@ import SolidIconButtonSeparated from 'components/atoms/SolidIconButtonSeparated' import { VerticalSpacer } from 'components/atoms/Spacer'; import { useServiceNavigate } from 'components/hooks/PathHook'; import { useAuthState } from 'components/hooks/ReduxStateHook'; -import { useGRPCClients } from 'components/templates/GRPCClients'; import { Formik, FormikProps } from 'formik'; import { EnvNameSchema } from 'libraries/helpers/yup'; import React, { useEffect } from 'react'; @@ -20,6 +19,7 @@ import { IconButton, Typography } from '@material-ui/core'; import CloseIcon from '@material-ui/icons/CloseRounded'; import { createEnvironment } from 'libraries/grpc/environment'; import { bps } from 'theme'; +import { useGRPCWrapper } from '../../templates/GRPCWrapper'; const StyledDiv = styled.div` position: relative; @@ -104,9 +104,9 @@ const renderNameEnv = (props: FormikProps, next: Function) => { }; export default () => { - const clients = useGRPCClients(); - const dispatch = useDispatch(); + const grpcWrapper = useGRPCWrapper(); + const { environments } = useAuthState(); const { navigateToServices } = useServiceNavigate(); @@ -129,10 +129,9 @@ export default () => { const createEnv = async () => { props.setSubmitting(true); try { - const env = await createEnvironment( - clients.kkcClient!, - props.values.envName - ); + const env = await grpcWrapper(createEnvironment, { + envName: props.values.envName, + }); // submit the env token const envId = env.getId(); diff --git a/src/components/organisms/sidePanel/EditEnvForm.tsx b/src/components/organisms/sidePanel/EditEnvForm.tsx index 1322d14..165fac9 100644 --- a/src/components/organisms/sidePanel/EditEnvForm.tsx +++ b/src/components/organisms/sidePanel/EditEnvForm.tsx @@ -4,7 +4,6 @@ import FormikTextField from 'components/atoms/FormikTextField'; import { FlexEndRow } from 'components/atoms/Row'; import SolidIconButton from 'components/atoms/SolidIconButton'; import { VerticalSpacer } from 'components/atoms/Spacer'; -import { useGRPCClients } from 'components/templates/GRPCClients'; import { Formik, FormikProps } from 'formik'; import { updateEnvironment } from 'libraries/grpc/environment'; import { trackError } from 'libraries/helpers'; @@ -16,6 +15,7 @@ import { Environment } from 'types/environment'; import * as Yup from 'yup'; import FormHelperText from '@material-ui/core/FormHelperText'; +import { useGRPCWrapper } from '../../templates/GRPCWrapper'; type Props = { env: Environment; @@ -26,7 +26,7 @@ type EnvValues = { }; export default ({ env }: Props) => { - const clients = useGRPCClients(); + const grpcWrapper = useGRPCWrapper(); const [error, setError] = useState(null); const [updated, setUpdated] = useState(false); @@ -43,7 +43,10 @@ export default ({ env }: Props) => { onSubmit={async (values, actions) => { setError(null); try { - await updateEnvironment(clients.kkcClient!, env.envId, values.name); + await grpcWrapper(updateEnvironment, { + envId: env.envId, + envName: values.name, + }); setUpdated(true); dispatch(updateEnvName(env.envId, values.name)); // restore to save button after 1s diff --git a/src/components/pages/CreateFirstEnv.tsx b/src/components/pages/CreateFirstEnv.tsx index e403b1f..06b60c9 100644 --- a/src/components/pages/CreateFirstEnv.tsx +++ b/src/components/pages/CreateFirstEnv.tsx @@ -4,7 +4,6 @@ import FormikTextField from 'components/atoms/FormikTextField'; import SolidIconButtonSeparated from 'components/atoms/SolidIconButtonSeparated'; import { VerticalSpacer } from 'components/atoms/Spacer'; import { useServiceNavigate } from 'components/hooks/PathHook'; -import { useGRPCClients } from 'components/templates/GRPCClients'; import { Formik, FormikProps } from 'formik'; import { EnvNameSchema } from 'libraries/helpers/yup'; import React, { useEffect, useState } from 'react'; @@ -21,6 +20,7 @@ import { doHidePanel } from 'states/sidePanel/actions'; import { PATH_MAINTENANCE } from 'libraries/constants'; import { push } from 'connected-react-router'; import FullPageLoading from '../molecules/FullPageLoading'; +import { useGRPCWrapper } from '../templates/GRPCWrapper'; const StyledDiv = styled.div` position: relative; @@ -62,7 +62,7 @@ type SignUpValues = { }; export default () => { - const clients = useGRPCClients(); + const grpcWrapper = useGRPCWrapper(); const dispatch = useDispatch(); const { navigateToServices } = useServiceNavigate(); @@ -71,7 +71,7 @@ export default () => { useEffect(() => { const initLoad = async () => { try { - const envs = await getEnvironments(clients.kkcClient!); + const envs = await grpcWrapper(getEnvironments, {}); const envList = envs.getItemsList(); if (envList.length !== 0) { navigateToServices({ targetEnvId: envList[0].getId() }); @@ -104,10 +104,9 @@ export default () => { const createEnv = async () => { props.setSubmitting(true); try { - const env = await createEnvironment( - clients.kkcClient!, - props.values.envName - ); + const env = await grpcWrapper(createEnvironment, { + envName: props.values.envName, + }); // submit the env token const envId = env.getId(); diff --git a/src/components/pages/LoginPage.tsx b/src/components/pages/LoginPage.tsx new file mode 100644 index 0000000..96c7f4c --- /dev/null +++ b/src/components/pages/LoginPage.tsx @@ -0,0 +1,100 @@ +import React, { useState } from 'react'; +import { push } from 'connected-react-router'; +import Typography from '@material-ui/core/Typography'; +import styled from 'styled-components'; +import { VerticalSpacer } from 'components/atoms/Spacer'; +import SolidIconButton from 'components/atoms/SolidIconButton'; +import { useDispatch } from 'react-redux'; +import { Formik, FormikProps } from 'formik'; +import { bps } from 'theme'; +import FormikTextField from 'components/atoms/FormikTextField'; +import DoneButton from '../atoms/DoneButton'; +import { updateToken } from '../../states/auth/actions'; +import { PATH_APP } from '../../libraries/constants'; + + +const StyledDiv = styled.div` + box-sizing: border-box; + padding-top: 35%; + padding-left: 40px; + padding-right: 40px; + ${bps.down('sm')} { + padding: 40% 20px 0 20px; + } + display: flex; + flex-direction: column; + height: 100%; + .retry-button { + width: 140px; + } +`; + +interface LoginProps { + secret: string; +} + +const LoginPage = () => { + const dispatch = useDispatch(); + const [loggedIn, setLoggedIn] = useState(false); + return ( + + Provide your credential :) + + + Looks like you have enabled authentication on kinto-core. + + + + + initialValues={{ + secret: '', + }} + onSubmit={(values, { setSubmitting }) => { + setSubmitting(true); + dispatch(updateToken(values.secret)); + setTimeout(() => { + setLoggedIn(true); + setSubmitting(false); + dispatch(push(PATH_APP)); + }, 800); + }} + > + {({ handleSubmit, isSubmitting }: FormikProps) => { + return ( +
+
+ + +
+ {loggedIn ? ( + + ) : ( + + )} +
+
+
+ ); + }} + +
+ ); +}; + +export default LoginPage; diff --git a/src/components/templates/GRPCWrapper.tsx b/src/components/templates/GRPCWrapper.tsx index 55069d8..98e2a82 100644 --- a/src/components/templates/GRPCWrapper.tsx +++ b/src/components/templates/GRPCWrapper.tsx @@ -8,11 +8,17 @@ import { isGRPCStreamTimeout, isPermissionDenied } from 'libraries/grpc/errors'; import { trackError } from 'libraries/helpers'; import { useEffect, useRef } from 'react'; +import { useDispatch } from 'react-redux'; +import { push } from 'connected-react-router'; +import { PATH_AUTH } from 'libraries/constants'; + import { GRPCClients, useGRPCClients } from './GRPCClients'; +import { useAuthState } from '../hooks/ReduxStateHook'; export const useGRPCWrapper = () => { const clients = useGRPCClients(); - + const dispatch = useDispatch(); + const { token } = useAuthState(); // since passing only the clients won't update the callbacks // we pass the ref of the clients and listen to the update const ref = useRef(); @@ -25,12 +31,16 @@ export const useGRPCWrapper = () => { return (grpc: CoreMethod, params: P): Promise => { const { kkcClient } = ref.current!; return new Promise((resolve, reject) => { - // TODO: remove token - grpc(kkcClient!, '', params) + grpc(kkcClient!, token!, params) .then((res) => { resolve(res!); }) .catch((error) => { + if ( + error.message === 'you are not authorized to query this endpoint' + ) { + dispatch(push(PATH_AUTH)); + } reject(error); }); }); @@ -46,6 +56,7 @@ export const useGRPCStream = ( earlyExit?: () => boolean ) => { const clients = useGRPCClients(); + const { token } = useAuthState(); let retryCount = 0; useEffect(() => { @@ -70,8 +81,7 @@ export const useGRPCStream = ( } const pStream = stream; - // TODO: leave token as empty first - stream = ws(clients.kkcClient!, '', params, { + stream = ws(clients.kkcClient!, token!, params, { onData: (msg) => { callbacks.onData(msg); }, @@ -98,7 +108,7 @@ export const useGRPCStream = ( } }; - stream = ws(clients.kkcClient!, '', params, { + stream = ws(clients.kkcClient!, token!, params, { onData: (message) => { callbacks.onData(message); }, diff --git a/src/components/templates/app/AuthApp.tsx b/src/components/templates/app/AuthApp.tsx index 90543d8..185e5f7 100644 --- a/src/components/templates/app/AuthApp.tsx +++ b/src/components/templates/app/AuthApp.tsx @@ -1,10 +1,10 @@ import { useAppState } from 'components/hooks/ReduxStateHook'; import FullPageLoading from 'components/molecules/FullPageLoading'; -import { useGRPCClients } from 'components/templates/GRPCClients'; import AxiosInterceptor from 'libraries/axios'; import React, { useEffect } from 'react'; import { useDispatch } from 'react-redux'; import { doInitBackgroundLoad } from 'states/auth/actions'; +import { useGRPCWrapper } from '../GRPCWrapper'; /** * Will check the token from local storage and check if the user is logged in. @@ -12,12 +12,12 @@ import { doInitBackgroundLoad } from 'states/auth/actions'; */ export default ({ children }: React.PropsWithChildren<{}>) => { const dispatch = useDispatch(); - const { kkcClient } = useGRPCClients(); + const grpcWrapper = useGRPCWrapper(); const { isInitialLoading } = useAppState(); useEffect(() => { // skip loading for maintenance mode - dispatch(doInitBackgroundLoad(kkcClient!)); + dispatch(doInitBackgroundLoad(grpcWrapper)); }, []); return isInitialLoading ? ( diff --git a/src/libraries/axios.tsx b/src/libraries/axios.tsx index 3215805..1e951ab 100644 --- a/src/libraries/axios.tsx +++ b/src/libraries/axios.tsx @@ -3,6 +3,7 @@ import axios from 'axios'; import { RootState } from 'states/types'; import { AuthState } from 'states/auth/types'; import { useSelector } from 'react-redux'; +import { getAuthorizationHeader } from './helpers'; const AxiosInterceptor = () => { const { token } = useSelector( @@ -13,7 +14,7 @@ const AxiosInterceptor = () => { axios.interceptors.request.use((config) => { if (token) { // eslint-disable-next-line no-param-reassign - config.headers.Authorization = `Bearer ${token}`; + config.headers.Authorization = getAuthorizationHeader(token); } return config; }); diff --git a/src/libraries/constants/index.tsx b/src/libraries/constants/index.tsx index 0ad7b50..4460dba 100644 --- a/src/libraries/constants/index.tsx +++ b/src/libraries/constants/index.tsx @@ -14,7 +14,7 @@ export const PATH_AUTH = '/auth'; export const PATH_APP = '/app'; export const PATH_ENV = `${PATH_APP}/environment`; -// export const PATH_SERVICES = `${PATH_APP}/services`; + export const PATH_ACCOUNT = `${PATH_APP}/account`; export const PATH_BILLING = `${PATH_APP}/billing`; diff --git a/src/libraries/grpc/app.ts b/src/libraries/grpc/app.ts index 13ba891..ae1eb8d 100644 --- a/src/libraries/grpc/app.ts +++ b/src/libraries/grpc/app.ts @@ -5,11 +5,12 @@ import { SyncTimeRequest, SyncTimeResponse } from 'types/proto/coreapi_pb'; import { invokeGRPC, CoreMethod } from './common'; export const getKintoConfig: CoreMethod = ( - client: KintoCoreServiceClient + client: KintoCoreServiceClient, + token ): Promise => { return invokeGRPC( client.getKintoConfiguration, - '', + token, new Empty(), client ); diff --git a/src/libraries/grpc/common.ts b/src/libraries/grpc/common.ts index d72026e..0bca110 100644 --- a/src/libraries/grpc/common.ts +++ b/src/libraries/grpc/common.ts @@ -3,6 +3,7 @@ import { ServiceError, } from 'types/proto/coreapi_pb_service'; import { grpc } from '@improbable-eng/grpc-web'; +import { getAuthorizationHeader } from '../helpers'; export interface WatchStream { ( @@ -41,7 +42,7 @@ export const invokeGRPC = ( context: KintoCoreServiceClient ): Promise => { const headers = new grpc.Metadata(); - headers.set('Authorization', `Bearer ${token}`); + headers.set('Authorization', getAuthorizationHeader(token)); return new Promise((resolve, reject) => { call.bind(context)(req, headers, (err, message) => { @@ -52,10 +53,3 @@ export const invokeGRPC = ( }); }); }; - -export const getAuthHeader = (token: string): grpc.Metadata => { - const headers = new grpc.Metadata(); - headers.set('Authorization', `Bearer ${token}`); - - return headers; -}; diff --git a/src/libraries/grpc/environment.ts b/src/libraries/grpc/environment.ts index 409b8be..31393ac 100644 --- a/src/libraries/grpc/environment.ts +++ b/src/libraries/grpc/environment.ts @@ -9,36 +9,32 @@ import { Environment, Environments } from 'types/proto/models_pb'; import { invokeGRPC, CoreMethod } from './common'; -export const getEnvironments = ( - client: KintoCoreServiceClient -): Promise => { - return new Promise((resolve, reject) => { - client.getEnvironments(new Empty(), (err, message) => { - if (err) { - reject(err); - return; - } - resolve(message!); - }); - }); +export const getEnvironments: CoreMethod = ( + client: KintoCoreServiceClient, + token: string +) => { + return invokeGRPC( + client.getEnvironments, + token, + new Empty(), + client + ); }; -export const createEnvironment = ( +export const createEnvironment: CoreMethod = ( client: KintoCoreServiceClient, - envName: string -): Promise => { + token: string, + { envName } +) => { const req = new CreateEnvironmentRequest(); req.setName(envName); - return new Promise((resolve, reject) => { - client.createEnvironment(req, (err, message) => { - if (err) { - reject(err); - return; - } - resolve(message!); - }); - }); + return invokeGRPC( + client.createEnvironment, + token, + req, + client + ); }; // eslint-disable-next-line max-len @@ -73,22 +69,18 @@ export const deleteEnvironment: CoreMethod = ( ); }; -// eslint-disable-next-line max-len -export const updateEnvironment = ( - client: KintoCoreServiceClient, - envId: string, - envName: string -): Promise => { +export const updateEnvironment: CoreMethod< + Environment, + { envId: string; envName: string } +> = (client: KintoCoreServiceClient, token: string, { envId, envName }) => { const req = new UpdateEnvironmentRequest(); - req.setId(envId); req.setName(envName); - return new Promise((resolve, reject) => { - client.updateEnvironment(req, (error, message) => { - if (error) { - reject(error); - return; - } - resolve(message!); - }); - }); + req.setId(envId); + + return invokeGRPC( + client.updateEnvironment, + token, + req, + client + ); }; diff --git a/src/libraries/grpc/release.ts b/src/libraries/grpc/release.ts index 2c2894d..b4c4189 100644 --- a/src/libraries/grpc/release.ts +++ b/src/libraries/grpc/release.ts @@ -25,6 +25,7 @@ import { CoreMethod, invokeGRPC, } from './common'; +import { getAuthorizationHeader } from '../helpers'; // try not to use this class as it depends on grpc Status export type ReleaseStatusMap = { @@ -215,7 +216,7 @@ export const watchBuildLogs: WatchStream< req.setBlockname(blockName); const headers = new grpc.Metadata(); - headers.set('Authorization', `Bearer ${token}`); + headers.set('Authorization', getAuthorizationHeader(token)); const stream = client.watchBuildLogs(req, headers); stream.on('data', (message: Logs) => { @@ -253,7 +254,7 @@ export const watchReleaseStatus: WatchStream< req.setName(blockName); const headers = new grpc.Metadata(); - headers.set('Authorization', `Bearer ${token}`); + headers.set('Authorization', getAuthorizationHeader(token)); const stream = client.watchReleasesStatus(req, headers); stream.on('data', (message: ReleasesStatus) => { diff --git a/src/libraries/grpc/service.ts b/src/libraries/grpc/service.ts index d63acc4..13e19d0 100644 --- a/src/libraries/grpc/service.ts +++ b/src/libraries/grpc/service.ts @@ -37,6 +37,7 @@ import { StreamCallbacks, WatchStream, } from './common'; +import { getAuthorizationHeader } from '../helpers'; export interface ConsoleLogMessage { message: string; @@ -72,7 +73,7 @@ export const getService: CoreMethod< req.setName(serviceName); const headers = new grpc.Metadata(); - headers.set('Authorization', `Bearer ${token}`); + headers.set('Authorization', getAuthorizationHeader(token)); return invokeGRPC( client.getBlock, @@ -275,7 +276,7 @@ export const watchConsoleLogs: WatchStream< req.setEnvid(envId); const headers = new grpc.Metadata(); - headers.set('Authorization', `Bearer ${token}`); + headers.set('Authorization', getAuthorizationHeader(token)); const stream = client.watchConsoleLogs(req, headers); stream.on('data', (message: ConsoleLog) => { @@ -321,7 +322,7 @@ export const watchServiceHealth: WatchStream< req.setId(envId); const headers = new grpc.Metadata(); - headers.set('Authorization', `Bearer ${token}`); + headers.set('Authorization', getAuthorizationHeader(token)); const stream = client.watchBlocksHealthStatuses(req, headers); stream.on('data', (status: BlockStatuses) => { @@ -358,7 +359,7 @@ export const watchMetrics: WatchStream< req.setEnvid(envId); const headers = new grpc.Metadata(); - headers.set('Authorization', `Bearer ${token}`); + headers.set('Authorization', getAuthorizationHeader(token)); const stream = client.watchBlocksMetrics(req, headers); stream.on('data', (metrics: BlocksMetrics) => { @@ -392,7 +393,7 @@ export const watchJobStatus: WatchStream< req.setEnvid(envId); const headers = new grpc.Metadata(); - headers.set('Authorization', `Bearer ${token}`); + headers.set('Authorization', getAuthorizationHeader(token)); const stream = client.watchJobsStatus(req, headers); stream.on('data', (status: JobStatus) => { diff --git a/src/libraries/helpers/index.ts b/src/libraries/helpers/index.ts index 25d104b..fe3e137 100644 --- a/src/libraries/helpers/index.ts +++ b/src/libraries/helpers/index.ts @@ -123,3 +123,10 @@ export const trackError = (type: string, error: Error | string) => { // eslint-disable-next-line no-console console.error(type, error); }; + + +export const getAuthorizationHeader = (token: string) => { + // Now it is secret but not bearer token. + // We should have logic around that once we implement different type of auth + return token; +} diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 8938ae7..b43f1e5 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -12,6 +12,7 @@ import { Redirect, Route, Switch, useLocation } from 'react-router'; import { TransitionGroup } from 'react-transition-group'; import EnvironmentApp from 'components/templates/app/EnvironmentApp'; import CreateFirstEnv from 'components/pages/CreateFirstEnv'; +import LoginPage from '../components/pages/LoginPage'; const Routes = () => { const location = useLocation(); @@ -21,6 +22,12 @@ const Routes = () => { return ( + + + + + + diff --git a/src/states/auth/actions.ts b/src/states/auth/actions.ts index 186772e..a57d656 100644 --- a/src/states/auth/actions.ts +++ b/src/states/auth/actions.ts @@ -14,11 +14,13 @@ import { PATH_CREATE_ENV, PATH_MAINTENANCE } from 'libraries/constants'; import { getEnvironments } from 'libraries/grpc/environment'; import { getKintoConfig, syncTime } from 'libraries/grpc/app'; import moment from 'moment'; +import { CoreMethod } from '../../libraries/grpc/common'; export const ACTION_ENV_LOGIN = 'ENV_LOGIN'; export const ACTION_UPDATE_ENV_LIST = 'UPDATE_ENV_LIST'; export const ACTION_UPDATE_ENV_NAME = 'UPDATE_ENV_NAME'; export const ACTION_DELETE_ENV = 'DELETE_ENV'; +export const ACTION_UPDATE_TOKEN = 'UPDATE_TOKEN'; export interface EnvLoginAction { type: typeof ACTION_ENV_LOGIN; @@ -41,6 +43,11 @@ export interface DeleteEnvAction { envId: string; } +export interface UpdateTokenAction { + type: typeof ACTION_UPDATE_TOKEN; + token: string; +} + export const envLogin = (envId: string): EnvLoginAction => { return { type: ACTION_ENV_LOGIN, @@ -62,6 +69,13 @@ export const updateEnvName = ( envName, }); +export const updateToken = ( + token: string +): UpdateTokenAction => ({ + type: ACTION_UPDATE_TOKEN, + token +}); + export const deleteEnv = (envId: string): DeleteEnvAction => ({ type: ACTION_DELETE_ENV, envId, @@ -93,12 +107,12 @@ export const doSyncTime = ( * Get the list of environments before everything else */ export const doInitBackgroundLoad = ( - coreClient: KintoCoreServiceClient + grpcWrapper: (grpc: CoreMethod, params: P) => Promise ): ThunkAction, RootState, {}, AnyAction> => async ( dispatch: ThunkDispatch<{}, {}, AnyAction> ): Promise => { try { - const envs = await getEnvironments(coreClient); + const envs = await grpcWrapper(getEnvironments, {}); const envList = envs.getItemsList(); // If no environment, force him to create one // FIXME: debug @@ -109,7 +123,7 @@ export const doInitBackgroundLoad = ( return; } - const config = await getKintoConfig(coreClient, '', {}); + const config = await grpcWrapper(getKintoConfig, {}); dispatch(updateKintoConfig(config!)); dispatch(setInitialLoading(false)); @@ -162,4 +176,5 @@ export type AuthActionsTypes = | EnvLoginAction | UpdateEnvNameAction | DeleteEnvAction - | UpdateEnvListAction; + | UpdateEnvListAction + | UpdateTokenAction; diff --git a/src/states/auth/reducers.ts b/src/states/auth/reducers.ts index d49bf70..6a87c54 100644 --- a/src/states/auth/reducers.ts +++ b/src/states/auth/reducers.ts @@ -1,16 +1,14 @@ import { getAuthState } from 'libraries/localstorage'; import _cloneDeep from 'lodash.clonedeep'; -import { - ACTION_UPDATE_KINTO_CONFIG, - UpdateKintoConfigAction, -} from 'states/app/actions'; +import { ACTION_UPDATE_KINTO_CONFIG, UpdateKintoConfigAction } from 'states/app/actions'; import { + ACTION_DELETE_ENV, ACTION_ENV_LOGIN, ACTION_UPDATE_ENV_LIST, ACTION_UPDATE_ENV_NAME, - ACTION_DELETE_ENV, - AuthActionsTypes, + ACTION_UPDATE_TOKEN, + AuthActionsTypes } from './actions'; import { AuthState } from './types'; @@ -58,6 +56,12 @@ export default function systemReducer( environments, }; } + case ACTION_UPDATE_TOKEN: { + return { + ...state, + token: action.token, + } + } default: return state; } diff --git a/src/states/auth/types.ts b/src/states/auth/types.ts index 5c36419..a72c168 100644 --- a/src/states/auth/types.ts +++ b/src/states/auth/types.ts @@ -2,7 +2,6 @@ import { Environment } from 'types/environment'; export interface AuthState { version: string; - // This is not used atm. but we may include authentication at any time so we will still keep it here. token: string | null; envId: string | null; isConfigLoaded: boolean;