Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="favicon.ico" />
<base href="/" />
<title>Capstone 1</title>
<script defer src="main.js"></script>
</head>
Expand Down
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"license": "ISC",
"description": "",
"dependencies": {
"@auth0/auth0-react": "^2.3.0",
"@auth0/auth0-react": "^2.4.0",
"@babel/core": "^7.27.4",
"@babel/preset-react": "^7.27.1",
"axios": "^1.10.0",
Expand Down
103 changes: 86 additions & 17 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,125 @@ import React, { useState, useEffect } from "react";
import { createRoot } from "react-dom/client";
import axios from "axios";
import "./AppStyles.css";
import { BrowserRouter as Router, Routes, Route, useNavigate } from "react-router-dom";
import { API_URL } from "./shared";
import { Auth0Provider, useAuth0 } from "@auth0/auth0-react";
import { auth0Config } from "./auth0-config";

import NavBar from "./components/NavBar";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Login from "./components/Login";
import Signup from "./components/Signup";
import Home from "./components/Home";
import NotFound from "./components/NotFound";
import { API_URL } from "./shared";
import SpotifyConnect from "./components/SpotifyConnect";
import SpotifyCallback from "./components/SpotifyCallback";

const App = () => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const navigate = useNavigate();

const {
isAuthenticated,
isLoading: auth0Loading,
user: auth0User,
logout: auth0Logout,
} = useAuth0();

const checkAuth = async () => {
try {
const response = await axios.get(`${API_URL}/auth/me`, {
withCredentials: true,
});
setUser(response.data.user);
} catch {
if (response.data.user) {
setUser(response.data.user);
}
} catch (error) {
console.log("Not authenticated");
setUser(null);
}
};

// Check authentication status on app load
useEffect(() => {
checkAuth();
}, []);
const initAuth = async () => {
if (!auth0Loading) {
if (isAuthenticated && auth0User) {
await handleAuth0Login();
} else {
await checkAuth();
}
setLoading(false);
}
};
initAuth();
}, [isAuthenticated, auth0User, auth0Loading]);

const handleLogout = async () => {
const handleAuth0Login = async () => {
try {
// Logout from our backend
await axios.post(
`${API_URL}/auth/logout`,
{},
setLoading(true);
const response = await axios.post(
`${API_URL}/auth/auth0`,
{
auth0Id: auth0User.sub,
email: auth0User.email,
username: auth0User.nickname || auth0User.email?.split("@")[0],
},
{
withCredentials: true,
}
);
setUser(null);

if (response.data.token) {
localStorage.setItem('token', response.data.token);
}

setUser(response.data.user);
navigate("/");
} catch (error) {
console.error("Auth0 login error:", error);
} finally {
setLoading(false);
}
};

const handleLogout = async () => {
try {
await axios.post(`${API_URL}/auth/logout`, {}, {
withCredentials: true,
});
} catch (error) {
console.error("Logout error:", error);
} finally {
localStorage.removeItem('token');
setUser(null);

if (isAuthenticated) {
auth0Logout({
logoutParams: {
returnTo: window.location.origin,
},
});
} else {
navigate("/");
}
}
};

if (loading || auth0Loading) {
return <div>Loading...</div>;
}

return (
<div>
<NavBar user={user} onLogout={handleLogout} />
<div className="app">
<Routes>
<Route path="/login" element={<Login setUser={setUser} />} />
<Route path="/signup" element={<Signup setUser={setUser} />} />
<Route exact path="/" element={<Home />} />
<Route path="/" element={<Home />} />
<Route path="/spotify" element={<SpotifyConnect />} />
<Route path="/callback/spotify" element={<SpotifyCallback />} />
<Route path="*" element={<NotFound />} />
</Routes>
</div>
Expand All @@ -63,11 +130,13 @@ const App = () => {

const Root = () => {
return (
<Router>
<App />
</Router>
<Auth0Provider {...auth0Config}>
<Router>
<App />
</Router>
</Auth0Provider>
);
};

const root = createRoot(document.getElementById("root"));
root.render(<Root />);
root.render(<Root />);
9 changes: 9 additions & 0 deletions src/auth0-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const auth0Config = {
domain: process.env.REACT_APP_AUTH0_DOMAIN || "franccescopetta.us.auth0.com",
clientId: process.env.REACT_APP_AUTH0_CLIENT_ID || "h1SYjGM6qWwIZMRTOSI7yjdjEzp3iAkS",
authorizationParams: {
redirect_uri: `${window.location.origin}/login`,
audience: process.env.REACT_APP_AUTH0_AUDIENCE,
scope: "openid profile email",
},
};
146 changes: 146 additions & 0 deletions src/components/CSS/AuthStyles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
.auth-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 60vh;
padding: 20px;
}

.auth-form {
background: white;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 400px;
}

.auth-form h2 {
text-align: center;
margin-bottom: 1.5rem;
color: #333;
}

.form-group {
margin-bottom: 1rem;
}

.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: #555;
}

.form-group input {
width: 100%;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
transition: border-color 0.2s;
}

.form-group input:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
}

.form-group input.error {
border-color: #dc3545;
}

.error-text {
color: #dc3545;
font-size: 0.875rem;
margin-top: 0.25rem;
display: block;
}

.error-message {
background-color: #f8d7da;
color: #721c24;
padding: 0.75rem;
border-radius: 4px;
margin-bottom: 1rem;
border: 1px solid #f5c6cb;
}

.auth-form button {
width: 100%;
padding: 0.75rem;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s;
}

.auth-form button:hover:not(:disabled) {
background-color: #0056b3;
}

.auth-form button:disabled {
background-color: #6c757d;
cursor: not-allowed;
}

.auth-divider {
text-align: center;
margin: 1.5rem 0;
position: relative;
}

.auth-divider::before {
content: "";
position: absolute;
top: 50%;
left: 0;
right: 0;
height: 1px;
background-color: #ddd;
}

.auth-divider span {
background-color: white;
padding: 0 1rem;
color: #666;
font-size: 0.875rem;
}

.auth0-login-btn {
width: 100%;
padding: 0.75rem;
background-color: #eb5424;
color: white;
border: none;
border-radius: 4px;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s;
margin-bottom: 1rem;
}

.auth0-login-btn:hover {
background-color: #d4451d;
}

.auth-link {
text-align: center;
margin-top: 1rem;
color: #666;
}

.auth-link a {
color: #007bff;
text-decoration: none;
}

.auth-link a:hover {
text-decoration: underline;
}
Loading