Auth implemented
This commit is contained in:
@@ -3,8 +3,9 @@ import { ThemeProvider, CssBaseline, AppBar, Toolbar, Typography, Box } from '@m
|
||||
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
||||
import theme from './theme';
|
||||
import CreateSession from './pages/CreateSession';
|
||||
|
||||
import SessionPage from './pages/SessionPage';
|
||||
import LoginPage from './pages/LoginPage'; // Import LoginPage
|
||||
import PrivateRoute from './components/PrivateRoute'; // Import PrivateRoute
|
||||
|
||||
function App() {
|
||||
return (
|
||||
@@ -24,9 +25,23 @@ function App() {
|
||||
</AppBar>
|
||||
<Box component="main" sx={{ flexGrow: 1, p: 3 }}>
|
||||
<Routes>
|
||||
<Route path="/" element={<CreateSession />} />
|
||||
{/* Other routes will be added here */}
|
||||
<Route path="/session/:sessionId" element={<SessionPage />} />
|
||||
<Route path="/login" element={<LoginPage />} /> {/* Add login page route */}
|
||||
<Route
|
||||
path="/"
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<CreateSession />
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/session/:sessionId"
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<SessionPage />
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
</Routes>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
48
frontend/src/components/PassphraseInput.tsx
Normal file
48
frontend/src/components/PassphraseInput.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
// frontend/src/components/PassphraseInput.tsx
|
||||
import React, { useState } from 'react';
|
||||
import { TextField, Button, Box } from '@mui/material';
|
||||
|
||||
interface PassphraseInputProps {
|
||||
onSubmit: (passphrase: string) => void;
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
const PassphraseInput: React.FC<PassphraseInputProps> = ({ onSubmit, isLoading, error }) => {
|
||||
const [passphrase, setPassphrase] = useState('');
|
||||
|
||||
const handleSubmit = (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
onSubmit(passphrase);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box component="form" onSubmit={handleSubmit} sx={{ mt: 1 }}>
|
||||
<TextField
|
||||
margin="normal"
|
||||
required
|
||||
fullWidth
|
||||
name="passphrase"
|
||||
label="Passphrase"
|
||||
type="password"
|
||||
id="passphrase"
|
||||
autoComplete="current-password"
|
||||
value={passphrase}
|
||||
onChange={(e) => setPassphrase(e.target.value)}
|
||||
error={!!error}
|
||||
helperText={error}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
fullWidth
|
||||
variant="contained"
|
||||
sx={{ mt: 3, mb: 2 }}
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading ? 'Submitting...' : 'Enter'}
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default PassphraseInput;
|
||||
17
frontend/src/components/PrivateRoute.tsx
Normal file
17
frontend/src/components/PrivateRoute.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
// frontend/src/components/PrivateRoute.tsx
|
||||
import React from 'react';
|
||||
import { Navigate, useLocation } from 'react-router-dom';
|
||||
import { getSessionToken } from '../services/authService';
|
||||
|
||||
interface PrivateRouteProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const PrivateRoute: React.FC<PrivateRouteProps> = ({ children }) => {
|
||||
const sessionToken = getSessionToken();
|
||||
const location = useLocation();
|
||||
|
||||
return sessionToken ? <>{children}</> : <Navigate to="/login" state={{ from: location }} replace />;
|
||||
};
|
||||
|
||||
export default PrivateRoute;
|
||||
53
frontend/src/pages/LoginPage.tsx
Normal file
53
frontend/src/pages/LoginPage.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
// frontend/src/pages/LoginPage.tsx
|
||||
import React, { useState } from 'react';
|
||||
import { Container, Typography, Box, Alert } from '@mui/material';
|
||||
import PassphraseInput from '../components/PassphraseInput';
|
||||
import { authenticate, setSessionToken } from '../services/authService';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
|
||||
const LoginPage: React.FC = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const from = location.state?.from?.pathname || '/';
|
||||
|
||||
const handleSubmitPassphrase = async (passphrase: string) => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const sessionToken = await authenticate(passphrase);
|
||||
setSessionToken(sessionToken);
|
||||
navigate(from, { replace: true }); // Redirect to the original intended page
|
||||
} catch (err: any) {
|
||||
setError(err.message || 'An unknown error occurred.');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Container component="main" maxWidth="xs">
|
||||
<Box
|
||||
sx={{
|
||||
marginTop: 8,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography component="h1" variant="h5">
|
||||
Enter Passphrase
|
||||
</Typography>
|
||||
{error && (
|
||||
<Alert severity="error" sx={{ width: '100%', mt: 2 }}>
|
||||
{error}
|
||||
</Alert>
|
||||
)}
|
||||
<PassphraseInput onSubmit={handleSubmitPassphrase} isLoading={isLoading} error={null} />
|
||||
</Box>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPage;
|
||||
32
frontend/src/services/authService.ts
Normal file
32
frontend/src/services/authService.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
// frontend/src/services/authService.ts
|
||||
const API_BASE_URL = process.env.REACT_APP_API_BASE_URL || 'http://localhost:8000';
|
||||
|
||||
export const authenticate = async (passphrase: string): Promise<string> => {
|
||||
const response = await fetch(`${API_BASE_URL}/api/auth/passphrase`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ passphrase }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.message || 'Authentication failed');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data.sessionToken;
|
||||
};
|
||||
|
||||
export const setSessionToken = (token: string): void => {
|
||||
localStorage.setItem('sessionToken', token);
|
||||
};
|
||||
|
||||
export const getSessionToken = (): string | null => {
|
||||
return localStorage.getItem('sessionToken');
|
||||
};
|
||||
|
||||
export const removeSessionToken = (): void => {
|
||||
localStorage.removeItem('sessionToken');
|
||||
};
|
||||
Reference in New Issue
Block a user