Skip to content

Commit

Permalink
Merge pull request #7 from sahil143/auth
Browse files Browse the repository at this point in the history
feat: add authentication and webpack local proxy
  • Loading branch information
gbenhaim authored Aug 14, 2024
2 parents a86d9ef + f17f3a3 commit 76c3a63
Show file tree
Hide file tree
Showing 15 changed files with 477 additions and 8 deletions.
4 changes: 4 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
AUTH_URL=https://127.0.0.1:9443/
REGISTRATION_URL=https://127.0.0.1:9443/
PROXY_URL=https://127.0.0.1:9443/
PROXY_WEBSOCKET_URL=wss://127.0.0.1:9443/
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ module.exports = {
},
plugins: ['prettier', 'react-refresh'],
rules: {
"@typescript-eslint/no-misused-promises": "off",
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
camelcase: [
'error',
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"build": "webpack -c ./webpack.prod.config.js",
"start": "webpack server --open -c ./webpack.dev.config.js",
"start": "dotenvx run -- webpack server --open -c ./webpack.dev.config.js",
"test": "jest",
"coverage": "jest --coverage --runInBand --detectOpenHandles --forceExit --testPathIgnorePatterns=/pact-tests",
"lint": "yarn lint:ts && yarn lint:sass",
Expand All @@ -25,6 +25,7 @@
"devDependencies": {
"@commitlint/cli": "^19.3.0",
"@commitlint/config-conventional": "^19.2.2",
"@dotenvx/dotenvx": "^1.7.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
"@swc/core": "^1.6.13",
"@swc/jest": "^0.2.36",
Expand Down
22 changes: 22 additions & 0 deletions src/assets/konflux.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion src/assets/react.svg

This file was deleted.

63 changes: 63 additions & 0 deletions src/auth/AuthContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import * as React from 'react';
import { Bullseye, Spinner } from '@patternfly/react-core';

export type AuthContextType = {
user: { email: string | null };
isAuthenticated: boolean;
signOut?: () => void;
};

const redirectToLogin = () => {
window.location.replace(`/oauth2/sign_in?rd=${window.location.pathname}`);
};

export const AuthContext = React.createContext<AuthContextType>({
user: { email: null },
isAuthenticated: false,
});

export const AuthProvider: React.FC<React.PropsWithChildren> = ({ children }) => {
const [user, setUser] = React.useState<AuthContextType['user']>({ email: null });
const [isAuthenticated, setIsAuthenticated] = React.useState<boolean>(false);

React.useEffect(() => {
const checkAuthStatus = async () => {
try {
const userData = await fetch('/oauth2/userinfo');
if (userData?.status === 401) {
redirectToLogin();
} else {
setUser((await userData.json()) as AuthContextType['user']);
setIsAuthenticated(true);
}
} catch (err) {
// eslint-disable-next-line no-console
console.log('Error while Authenticating the user', err);
}
};
void checkAuthStatus();
}, []);

const signOut = async () => {
await fetch('/oauth2/sign_out');
redirectToLogin();
};

return (
<AuthContext.Provider
value={{
user,
isAuthenticated,
signOut,
}}
>
{isAuthenticated ? (
children
) : (
<Bullseye>
<Spinner />
</Bullseye>
)}
</AuthContext.Provider>
);
};
4 changes: 4 additions & 0 deletions src/auth/useAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import * as React from 'react';
import { AuthContext, AuthContextType } from './AuthContext';

export const useAuth = (): AuthContextType => React.useContext(AuthContext);
11 changes: 10 additions & 1 deletion src/components/AppRoot/AppHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import * as React from 'react';
import {
Brand,
Masthead,
MastheadBrand,
MastheadContent,
MastheadMain,
MastheadToggle,
PageToggleButton,
} from '@patternfly/react-core';
import BarsIcon from '@patternfly/react-icons/dist/js/icons/bars-icon';
import konfluxLogo from '../../assets/konflux.svg';
import { Header } from '../Header.tsx/Header';

export const AppHeader: React.FC<{ isSideBarOpen: boolean; onSideBarOpen: () => void }> = ({
isSideBarOpen,
Expand All @@ -25,8 +29,13 @@ export const AppHeader: React.FC<{ isSideBarOpen: boolean; onSideBarOpen: () =>
</PageToggleButton>
</MastheadToggle>
<MastheadMain>
<MastheadBrand>Konflux Logo</MastheadBrand>
<MastheadBrand>
<Brand src={konfluxLogo} alt="konflux" heights={{ default: '36px' }} />
</MastheadBrand>
</MastheadMain>
<MastheadContent>
<Header />
</MastheadContent>
</Masthead>
);
};
15 changes: 15 additions & 0 deletions src/components/Header.tsx/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as React from 'react';
import { Toolbar, ToolbarContent, ToolbarItem } from '@patternfly/react-core';
import { UserDropdown } from './UserDropdown';

export const Header: React.FC = () => {
return (
<Toolbar isFullHeight>
<ToolbarContent>
<ToolbarItem align={{ default: 'alignRight' }}>
<UserDropdown />
</ToolbarItem>
</ToolbarContent>
</Toolbar>
);
};
42 changes: 42 additions & 0 deletions src/components/Header.tsx/UserDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import * as React from 'react';
import {
Dropdown,
DropdownGroup,
DropdownItem,
DropdownList,
MenuToggle,
} from '@patternfly/react-core';
import { useAuth } from '../../auth/useAuth';

export const UserDropdown: React.FC = () => {
const [isOpen, setIsOpen] = React.useState(false);
const {
user: { email },
signOut,
} = useAuth();
return (
<Dropdown
aria-label="User action"
isOpen={isOpen}
onSelect={() => setIsOpen(!isOpen)}
onOpenChange={setIsOpen}
toggle={(toggleRef) => (
<MenuToggle
ref={toggleRef}
isFullHeight
onClick={() => {
setIsOpen(!isOpen);
}}
>
{email}
</MenuToggle>
)}
>
<DropdownGroup>
<DropdownList>
<DropdownItem onClick={() => signOut?.()}>Log out</DropdownItem>
</DropdownList>
</DropdownGroup>
</Dropdown>
);
};
3 changes: 3 additions & 0 deletions src/main.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#root {
height: 100%;
}
2 changes: 2 additions & 0 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import React from 'react';
import { RouterProvider } from 'react-router-dom';
import ReactDOM from 'react-dom/client';
import { router } from './routes';

import '@patternfly/react-core/dist/styles/base.css';
import './main.scss';

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
Expand Down
7 changes: 6 additions & 1 deletion src/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { createBrowserRouter } from 'react-router-dom';
import { AuthProvider } from '../auth/AuthContext';
import { AppRoot } from '../components/AppRoot/AppRoot';
import { Overview } from '../components/Overview/Overview';

export const router = createBrowserRouter([
{
path: '/',
element: <AppRoot />,
element: (
<AuthProvider>
<AppRoot />
</AuthProvider>
),
children: [
{
index: true,
Expand Down
50 changes: 50 additions & 0 deletions webpack.dev.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,62 @@ import { merge } from 'webpack-merge';
import commonConfig from './webpack.config.js';
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
import { config } from '@dotenvx/dotenvx';

config();
const DEV_SERVER_PORT = 8080;

export default merge(commonConfig, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
port: DEV_SERVER_PORT,
hot: true,
proxy: [
{
context: (path) => path.includes('/oauth2/') || path.includes('/idp/'),
target: process.env.AUTH_URL,
secure: false,
changeOrigin: true,
autoRewrite: true,
onProxyRes: (proxyRes) => {
const location = proxyRes.headers['location'];
if (location) {
proxyRes.headers['location'] = location.replace(
'https://localhost:9443',
`http://localhost:${DEV_SERVER_PORT}`,
);
}
},
},
{
context: (path) => path.includes('/api/k8s/registration'),
target: process.env.REGISTRATION_URL,
secure: false,
changeOrigin: true,
autoRewrite: true,
ws: true,
pathRewrite: { '^/api/k8s/registration': '' },
},
{
context: (path) => path.includes('/api/k8s'),
target: process.env.PROXY_URL,
secure: false,
changeOrigin: true,
autoRewrite: true,
ws: true,
pathRewrite: { '^/api/k8s': '' },
},
{
context: (path) => path.includes('/wss/k8s'),
target: process.env.PROXY_WEBSOCKET_URL,
secure: false,
changeOrigin: true,
autoRewrite: true,
ws: true,
pathRewrite: { '^/wss/k8s': '' },
},
],
},
module: {
rules: [
Expand Down
Loading

0 comments on commit 76c3a63

Please sign in to comment.