diff --git a/contract/src/main/resources/swagger/kafbat-ui-api.yaml b/contract/src/main/resources/swagger/kafbat-ui-api.yaml
index d1215ec87..f1ce3702c 100644
--- a/contract/src/main/resources/swagger/kafbat-ui-api.yaml
+++ b/contract/src/main/resources/swagger/kafbat-ui-api.yaml
@@ -2256,7 +2256,10 @@ paths:
/auth:
post:
+ tags:
+ - ApplicationConfig
summary: Authenticate
+ operationId: authenticate
requestBody:
required: true
content:
diff --git a/frontend/src/components/AuthPage/AuthPage.tsx b/frontend/src/components/AuthPage/AuthPage.tsx
index 9adf7f783..a5ce93313 100644
--- a/frontend/src/components/AuthPage/AuthPage.tsx
+++ b/frontend/src/components/AuthPage/AuthPage.tsx
@@ -1,7 +1,6 @@
import React from 'react';
import { useAuthSettings } from 'lib/hooks/api/appConfig';
-import Footer from './Footer/Footer';
import Header from './Header/Header';
import SignIn from './SignIn/SignIn';
import * as S from './AuthPage.styled';
@@ -13,7 +12,6 @@ function AuthPage() {
{data && }
-
);
}
diff --git a/frontend/src/components/AuthPage/Footer/Footer.styled.tsx b/frontend/src/components/AuthPage/Footer/Footer.styled.tsx
deleted file mode 100644
index 63fa63010..000000000
--- a/frontend/src/components/AuthPage/Footer/Footer.styled.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import styled, { css } from 'styled-components';
-import GitHubIcon from 'components/common/Icons/GitHubIcon';
-
-export const FooterStyledWrapper = styled.div`
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 8px;
- padding: 19px 150px;
- font-size: 12px;
-`;
-
-export const AppVersionStyled = styled.div(
- ({ theme }) => css`
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 5px;
- width: 200px;
-
- ${GitHubIcon} {
- width: 14px;
- height: 14px;
- fill: ${theme.auth_page.icons.githubColor};
- }
- `
-);
-
-export const AppVersionTextStyled = styled.span(
- ({ theme }) => css`
- font-weight: ${theme.auth_page.footer.span.fontWeight};
- line-height: 18px;
- color: ${theme.auth_page.footer.span.color};
- `
-);
-
-export const InformationTextStyled = styled.p(
- ({ theme }) => css`
- font-size: 12px;
- font-weight: ${theme.auth_page.footer.p.fontWeight};
- line-height: 16px;
- color: ${theme.auth_page.footer.p.color};
- text-align: center;
- `
-);
diff --git a/frontend/src/components/AuthPage/Footer/Footer.tsx b/frontend/src/components/AuthPage/Footer/Footer.tsx
deleted file mode 100644
index 484559e8d..000000000
--- a/frontend/src/components/AuthPage/Footer/Footer.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import React from 'react';
-import GitHubIcon from 'components/common/Icons/GitHubIcon';
-import { useLatestVersion } from 'lib/hooks/api/latestVersion';
-
-import * as S from './Footer.styled';
-
-function Footer() {
- // const { data: latestVersionInfo = {} } = useLatestVersion();
- // const { versionTag } = latestVersionInfo.latestRelease;
- // const { commitId } = latestVersionInfo.build;
-
- return (
-
-
-
-
- {/* {versionTag} ({commitId}) */}
-
-
-
- Access to the system is provided by your system administrator. If you
- have any questions, please contact your system administrator
-
-
- );
-}
-
-export default Footer;
diff --git a/frontend/src/components/AuthPage/SignIn/BasicSignIn/BasicSignIn.styled.tsx b/frontend/src/components/AuthPage/SignIn/BasicSignIn/BasicSignIn.styled.tsx
index 5b59c859d..919fa662b 100644
--- a/frontend/src/components/AuthPage/SignIn/BasicSignIn/BasicSignIn.styled.tsx
+++ b/frontend/src/components/AuthPage/SignIn/BasicSignIn/BasicSignIn.styled.tsx
@@ -6,6 +6,7 @@ export const Form = styled.form`
align-items: center;
justify-content: center;
gap: 40px;
+ width: 100%;
div {
width: 100%;
@@ -19,6 +20,7 @@ export const Fieldset = styled.fieldset`
justify-content: center;
gap: 16px;
border: none;
+ width: 100%;
`;
export const Field = styled.div`
@@ -35,3 +37,20 @@ export const Label = styled.label`
font-weight: 500;
line-height: 16px;
`;
+
+export const ErrorMessage = styled.div`
+ display: flex;
+ column-gap: 2px;
+ align-items: center;
+ justify-content: center;
+ font-weight: 400;
+ font-size: 14px;
+ line-height: 20px;
+`;
+
+export const ErrorMessageText = styled.span`
+ ${({ theme }) => theme.auth_page.signIn.errorMessage};
+ font-weight: 400;
+ font-size: 14px;
+ line-height: 20px;
+`;
diff --git a/frontend/src/components/AuthPage/SignIn/BasicSignIn/BasicSignIn.tsx b/frontend/src/components/AuthPage/SignIn/BasicSignIn/BasicSignIn.tsx
index 01a8cc875..0bb6c8a75 100644
--- a/frontend/src/components/AuthPage/SignIn/BasicSignIn/BasicSignIn.tsx
+++ b/frontend/src/components/AuthPage/SignIn/BasicSignIn/BasicSignIn.tsx
@@ -1,7 +1,10 @@
import React from 'react';
import { Button } from 'components/common/Button/Button';
import Input from 'components/common/Input/Input';
-import { FormProvider, useForm } from 'react-hook-form';
+import { Controller, FormProvider, useForm } from 'react-hook-form';
+import { useAuthenticate } from 'lib/hooks/api/appConfig';
+import AlertIcon from 'components/common/Icons/AlertIcon';
+import { useNavigate } from 'react-router-dom';
import * as S from './BasicSignIn.styled';
@@ -11,36 +14,78 @@ interface FormValues {
}
function BasicSignIn() {
- const methods = useForm();
+ const methods = useForm({
+ defaultValues: { username: '', password: '' },
+ });
+ const navigate = useNavigate();
+ const { mutateAsync } = useAuthenticate();
+
+ const onSubmit = async (data: FormValues) => {
+ await mutateAsync(data, {
+ onSuccess(response) {
+ if (response.raw.url.includes('error')) {
+ methods.setError('root', { message: 'error' });
+ } else {
+ navigate('/');
+ }
+ },
+ });
+ };
return (
-
-
-
- Username
-
-
-
- Password
-
-
+
+
+ {methods.formState.errors.root && (
+
+
+
+ Username or password entered incorrectly
+
+
+ )}
+ (
+
+ Username
+
+
+ )}
+ />
+ (
+
+ Password
+
+
+ )}
+ />
diff --git a/frontend/src/components/AuthPage/SignIn/OAuthSignIn/AuthCard/AuthCard.styled.tsx b/frontend/src/components/AuthPage/SignIn/OAuthSignIn/AuthCard/AuthCard.styled.tsx
index 189e0b843..d1eae050f 100644
--- a/frontend/src/components/AuthPage/SignIn/OAuthSignIn/AuthCard/AuthCard.styled.tsx
+++ b/frontend/src/components/AuthPage/SignIn/OAuthSignIn/AuthCard/AuthCard.styled.tsx
@@ -1,6 +1,5 @@
import styled, { css } from 'styled-components';
import GitHubIcon from 'components/common/Icons/GitHubIcon';
-import { Link } from 'react-router-dom';
import { Button } from 'components/common/Button/Button';
export const AuthCardStyled = styled.div(
@@ -64,4 +63,4 @@ export const ServiceButton = styled(Button)`
border-radius: 8px;
font-size: 14px;
text-decoration: none;
-`
+`;
diff --git a/frontend/src/components/AuthPage/SignIn/OAuthSignIn/OAuthSignIn.tsx b/frontend/src/components/AuthPage/SignIn/OAuthSignIn/OAuthSignIn.tsx
index e333f29ba..5814a99c5 100644
--- a/frontend/src/components/AuthPage/SignIn/OAuthSignIn/OAuthSignIn.tsx
+++ b/frontend/src/components/AuthPage/SignIn/OAuthSignIn/OAuthSignIn.tsx
@@ -24,7 +24,11 @@ function OAuthSignIn({ oAuthProviders }: Props) {
))}
diff --git a/frontend/src/components/AuthPage/SignIn/SignIn.styled.tsx b/frontend/src/components/AuthPage/SignIn/SignIn.styled.tsx
index c36847b79..0f24b45fd 100644
--- a/frontend/src/components/AuthPage/SignIn/SignIn.styled.tsx
+++ b/frontend/src/components/AuthPage/SignIn/SignIn.styled.tsx
@@ -7,6 +7,7 @@ export const SignInStyled = styled.div`
justify-content: center;
width: 320px;
gap: 56px;
+ flex-grow: 1;
`;
export const SignInTitle = styled.span(
diff --git a/frontend/src/components/AuthPage/SignIn/SignIn.tsx b/frontend/src/components/AuthPage/SignIn/SignIn.tsx
index fb93f9e89..88c6e1df1 100644
--- a/frontend/src/components/AuthPage/SignIn/SignIn.tsx
+++ b/frontend/src/components/AuthPage/SignIn/SignIn.tsx
@@ -15,8 +15,9 @@ function SignInForm({ appAuthenticationSettings }: Props) {
return (
Sign in
- {(authType === AuthType.LDAP ||
- authType === AuthType.LOGIN_FORM) && }
+ {(authType === AuthType.LDAP || authType === AuthType.LOGIN_FORM) && (
+
+ )}
{authType === AuthType.OAUTH2 && (
)}
diff --git a/frontend/src/components/NavBar/UserInfo/UserInfo.tsx b/frontend/src/components/NavBar/UserInfo/UserInfo.tsx
index dae43364c..b52cc7631 100644
--- a/frontend/src/components/NavBar/UserInfo/UserInfo.tsx
+++ b/frontend/src/components/NavBar/UserInfo/UserInfo.tsx
@@ -19,7 +19,7 @@ const UserInfo = () => {
}
>
-
+
Log out
diff --git a/frontend/src/components/common/Icons/AlertIcon.tsx b/frontend/src/components/common/Icons/AlertIcon.tsx
new file mode 100644
index 000000000..3c79f78e6
--- /dev/null
+++ b/frontend/src/components/common/Icons/AlertIcon.tsx
@@ -0,0 +1,22 @@
+import React from 'react';
+
+const AlertIcon: React.FC = () => {
+ return (
+
+ );
+};
+
+export default AlertIcon;
diff --git a/frontend/src/components/contexts/GlobalSettingsContext.tsx b/frontend/src/components/contexts/GlobalSettingsContext.tsx
index 4de05307b..bcf87841f 100644
--- a/frontend/src/components/contexts/GlobalSettingsContext.tsx
+++ b/frontend/src/components/contexts/GlobalSettingsContext.tsx
@@ -1,6 +1,7 @@
import { useAppInfo } from 'lib/hooks/api/appConfig';
import React from 'react';
import { ApplicationInfoEnabledFeaturesEnum } from 'generated-sources';
+import { useNavigate } from 'react-router-dom';
interface GlobalSettingsContextProps {
hasDynamicConfig: boolean;
@@ -15,6 +16,14 @@ export const GlobalSettingsProvider: React.FC<
React.PropsWithChildren
> = ({ children }) => {
const info = useAppInfo();
+ const navigate = useNavigate();
+
+ React.useEffect(() => {
+ if (info.data?.raw.url.includes('auth')) {
+ navigate('auth');
+ }
+ }, []);
+
const value = React.useMemo(() => {
const features = info.data?.enabledFeatures || [];
return {
diff --git a/frontend/src/lib/hooks/api/appConfig.ts b/frontend/src/lib/hooks/api/appConfig.ts
index 66d746c33..22f5da0dd 100644
--- a/frontend/src/lib/hooks/api/appConfig.ts
+++ b/frontend/src/lib/hooks/api/appConfig.ts
@@ -14,10 +14,19 @@ export function useAuthSettings() {
);
}
+export function useAuthenticate() {
+ return useMutation({
+ mutationFn: (params: { username: string; password: string }) =>
+ api.authenticateRaw(params, {
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ }),
+ });
+}
+
export function useAppInfo() {
return useQuery(
['app', 'info'],
- () => api.getApplicationInfo(),
+ () => api.getApplicationInfoRaw(),
QUERY_REFETCH_OFF_OPTIONS
);
}
diff --git a/frontend/src/theme/theme.ts b/frontend/src/theme/theme.ts
index bb246bd2d..bdfe93271 100644
--- a/frontend/src/theme/theme.ts
+++ b/frontend/src/theme/theme.ts
@@ -57,6 +57,7 @@ const Colors = {
'10': '#FAD1D1',
'20': '#F5A3A3',
'50': '#E51A1A',
+ '52': '#E63B19',
'55': '#CF1717',
'60': '#B81414',
},
@@ -89,6 +90,9 @@ const baseTheme = {
},
signIn: {
titleColor: Colors.brand[90],
+ errorMessage: {
+ color: Colors.red[52],
+ },
label: {
color: Colors.brand[70],
},
@@ -866,12 +870,14 @@ export const darkTheme: ThemeType = {
LogoTextColor: Colors.brand[90],
},
signIn: {
+ ...baseTheme.auth_page.signIn,
titleColor: Colors.brand[0],
label: {
color: Colors.brand[30],
},
authCard: {
...baseTheme.auth_page.signIn.authCard,
+ borderColor: Colors.brand[80],
backgroundColor: Colors.brand[85],
serviceNamecolor: Colors.brand[0],
},
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index 3a4e861e9..6cf7c0f15 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -3,6 +3,7 @@ import react from '@vitejs/plugin-react-swc';
import tsconfigPaths from 'vite-tsconfig-paths';
import { ViteEjsPlugin } from 'vite-plugin-ejs';
import checker from 'vite-plugin-checker';
+import { IncomingMessage } from 'http';
export default defineConfig(({ mode }) => {
process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
@@ -87,6 +88,21 @@ export default defineConfig(({ mode }) => {
...defaultConfig.server,
open: true,
proxy: {
+ '/auth': {
+ target: isProxy,
+ changeOrigin: true,
+ secure: false,
+ bypass: (req: IncomingMessage) => {
+ if (req.method === 'GET') {
+ return req.url;
+ }
+ },
+ },
+ '/logout': {
+ target: isProxy,
+ changeOrigin: true,
+ secure: false,
+ },
'/api': {
target: isProxy,
changeOrigin: true,