Skip to content
Snippets Groups Projects
Commit 649a034d authored by Sarthak Shrivastava's avatar Sarthak Shrivastava
Browse files

semi-working auth, broken routes

parent 010cd558
No related branches found
No related tags found
No related merge requests found
Showing
with 594 additions and 40 deletions
......@@ -8,17 +8,32 @@ import {
Register,
UpdatePassword,
} from "./components/user/index";
import { useState } from "react";
import useToken from "./components/useToken";
import AccountInformation from "./components/user/AccountInformation";
import ProtectedRoute from "./routes/ProtectedRoute";
function App() {
// const [token, setToken] = useState();
const { token, setToken } = useToken();
// const token = getToken();
// if (!token)
// {
// return <Login setToken={setToken}/>
// }
return (
<div className="App">
<Navbar />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login setToken={setToken}/>} />
<Route path="/register" element={<Register />} />
<Route path="/login" element={<Login />} />
<Route path="/deleteUser" element={<DeleteUser />} />
<Route path='/accountinfo' element={<ProtectedRoute token={token}> <AccountInformation token={token}/> </ProtectedRoute>}>
</Route>
{/* <Route path="/deleteUser" element={<DeleteUser token={token}/>} />
<Route path="/updatepassword" element={<UpdatePassword />} />
<Route path="/accountinfo" element={<AccountInformation token={token}/> } /> */}
</Routes>
</div>
);
......
/* Add some basic styling for better presentation */
.home-container {
max-width: 90%;
margin: 0 auto;
padding: 20px;
text-align: center;
}
h1 {
color: #007bff;
}
h2 {
color: #333;
}
ul {
list-style-type: none;
padding: 0;
}
li {
margin-bottom: 8px;
}
import "./Home.css";
import React from "react";
import "./Home.css";
export const Home = () => {
return <div>Home</div>;
return (
<div className="home-container">
<h1>Welcome to inVT</h1>
<p>
inVT is the Virginia Tech Student Organization Inventory Management System.
Manage your organization's inventory efficiently with our user-friendly platform.
</p>
<h2>Key Features:</h2>
<ul>
<li>Create Organizations</li>
<li>Manage Inventory</li>
<li>Buy and Sell Items with Other Clubs/People</li>
</ul>
<p>
Streamline your organization's operations and transactions seamlessly.
Join inVT today for a better inventory management experience!
</p>
</div>
);
};
......@@ -31,6 +31,9 @@ export const Navbar = () => {
<li>
<NavLink to="/updatepassword">Update Password</NavLink>
</li>
<li>
<NavLink to="/accountinfo">Account Information</NavLink>
</li>
</ul>
</nav>
);
......
import { useState } from 'react';
import Axios from "axios";
export default function useToken() {
const getToken = () => {
const tokenString = sessionStorage.getItem('token');
if (!tokenString)
return null;
console.log(tokenString);
const userToken = JSON.parse(tokenString);
console.log(userToken);
if (userToken.jwt != null)
{
Axios.post("http://localhost:8080/auth/verify", {
jwt: userToken.jwt
}).then((response) => {
console.log(response);
if (response.user)
return response;
});
}
return null;
};
const [token, setToken] = useState(getToken());
const saveToken = userToken => {
sessionStorage.setItem('token', JSON.stringify(userToken.data));
setToken(userToken.data);
};
return {
setToken: saveToken,
token
}
}
\ No newline at end of file
/* AccountInformation.css */
.account-info-container {
max-width: 400px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 5px;
}
.info-form {
display: flex;
flex-direction: column;
}
label {
margin-bottom: 8px;
}
input {
padding: 8px;
margin-bottom: 16px;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 10px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
.success-message {
color: green;
margin-top: 10px;
}
\ No newline at end of file
import React, { useState, useEffect } from "react";
import Axios from "axios";
import PropTypes from "prop-types";
import './AccountInformation.css'; // Import your external CSS file
const AccountInformation = ({ token }) => {
const [userInfo, setUserInfo] = useState({
fname: "",
lname: "",
password: "",
phoneNumber: "",
email: "",
});
const [updateSuccess, setUpdateSuccess] = useState(false);
useEffect(() => {
// Fetch user information when the component mounts
getUserInfo();
}, []); // Empty dependency array ensures the effect runs once
const getUserInfo = async () => {
try {
const response = await Axios.post(
"http://localhost:8080/user/user",
{ jwt: token.jwt }
);
setUserInfo(response.data);
} catch (error) {
console.error("Error fetching user information:", error);
}
};
const handleUpdate = async () => {
try {
const response = await Axios.put(
"http://localhost:8080/user/update",
{
fname: userInfo.fname,
lname: userInfo.lname,
password: userInfo.password,
phoneNumber: userInfo.phoneNumber,
email: userInfo.email,
jwt: token.jwt,
}
);
setUserInfo(response.data);
setUpdateSuccess(true);
console.log("User information updated successfully");
} catch (error) {
console.error("Error updating user information:", error);
}
};
const handleChange = (e) => {
const { name, value } = e.target;
setUserInfo((prevUserInfo) => ({
...prevUserInfo,
[name]: value,
}));
};
return (
<div className="account-info-container">
<h2>Account Information</h2>
{updateSuccess && <p className="success-message">Information updated successfully!</p>}
<div className="info-form">
<label htmlFor="fname">First Name</label>
<input
type="text"
id="fname"
name="fname"
value={userInfo.fname}
onChange={handleChange}
/>
<label htmlFor="lname">Last Name</label>
<input
type="text"
id="lname"
name="lname"
value={userInfo.lname}
onChange={handleChange}
/>
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
name="password"
value={userInfo.password}
onChange={handleChange}
/>
<label htmlFor="phoneNumber">Phone Number</label>
<input
type="text"
id="phoneNumber"
name="phoneNumber"
value={userInfo.phoneNumber || ""}
onChange={handleChange}
/>
<label htmlFor="email">Email</label>
<input
type="email"
id="email"
name="email"
value={userInfo.email}
onChange={handleChange}
/>
<button type="button" onClick={handleUpdate}>
Update Information
</button>
</div>
</div>
);
};
AccountInformation.propTypes = {
token: PropTypes.shape({
jwt: PropTypes.string.isRequired,
}).isRequired,
};
export default AccountInformation;
import React, { useState } from "react";
import Axios from "axios";
import useToken from "../useToken";
export const DeleteUser = () => {
export const DeleteUser = (token) => {
const [email, setEmail] = useState("");
// console.log(JSON.parse(token.token).user);
// console.log(token.token.user);
console.log(token);
const handleSubmit = (e) => {
e.preventDefault();
Axios.delete("http://localhost:8080/user/delete", {
headers: {},
data: {
email: email
}
Axios.post("http://localhost:8080/user/delete", {
jwt: token.token.jwt
}).then((response) => {
console.log(response);
// console.log(response);
// if (response.data.email == "")
});
};
// Axios.delete("http://localhost:8080/user/delete", {
// email: email,
// }).then((response) => {
// console.log(response);
// });
// };
return (
<div className="auth-form-container">
......
/* Login.css */
.auth-form-container {
max-width: 400px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 5px;
}
.login-form {
display: flex;
flex-direction: column;
}
label {
margin-bottom: 8px;
}
input {
padding: 8px;
margin-bottom: 16px;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 10px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
/* Add this style for error messages */
.error-message {
color: red;
margin-top: 10px;
}
\ No newline at end of file
import React, { useState } from "react";
import Axios from "axios";
import PropTypes from "prop-types";
import { Link } from "react-router-dom"; // Import Link from React Router
import './Login.css'; // Import your external CSS file
export const Login = (props) => {
const [email, setEmail] = useState("");
const [pass, setPass] = useState("");
export const Login = ({ setToken }) => {
const [email, setEmail] = useState();
const [pass, setPass] = useState();
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
console.log(email);
console.log(pass);
setLoading(true);
setError("");
Axios.post("http://localhost:8080/auth/login", {
email: email,
password: pass,
}).then((response) => {
console.log(response);
// console.log(response.data.login);
if (response.data.result === "success") {
setToken(response);
} else {
setError("Invalid email or password. Please try again.");
}
}).catch((error) => {
setError("An error occurred. Please try again later.");
console.error("Login error:", error);
})
.finally(() => {
setLoading(false);
});
};
return (
......@@ -34,10 +56,21 @@ export const Login = (props) => {
id="password"
name="password"
/>
<button type="submit">Log In</button>
{loading ? (
<p>Loading...</p>
) : (
<button type="submit">Log In</button>
)}
{error && <p className="error-message">{error}</p>}
{/* Add a Link to the Register page */}
<p>Don't have an account? <Link to="/register">Register here</Link></p>
</form>
</div>
);
};
Login.propTypes = {
setToken: PropTypes.func.isRequired
};
export default Login;
import React from 'react';
import { Route, Navigate } from 'react-router-dom';
const ProtectedRoute = ({ element, token, ...props }) => {
return token ? (
<Route {...props} element={element} />
) : (
<Navigate to="/login" />
);
};
export default ProtectedRoute;
......@@ -52,6 +52,33 @@
<artifactId>jackson-databind</artifactId>
<version>2.14.2</version>
</dependency>
<!-- JAX-RS Implementation (for example, Jersey) -->
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
......
......@@ -7,11 +7,14 @@ import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
import java.util.HashMap;
import io.jsonwebtoken.Claims;
import com.example.accessingdatamysql.auth.AuthController;
import java.util.Optional;
@CrossOrigin
@RestController // This means that this class is a Controller
@RequestMapping(path="/user") // This means URL's start with /demo (after Application path)
@RequestMapping(path="/user") // This means URL's start with /user (after Application path)
public class MainController {
@Autowired // This means to get the bean called userRepository
// Which is auto-generated by Spring, we will use it to handle the data
......@@ -60,28 +63,48 @@ public class MainController {
return userRepository.findAll();
}
@GetMapping(path = "/user")
public @ResponseBody Optional<User> getUser(@RequestBody Map<String, String> json)
@PostMapping(path = "/user")
public @ResponseBody User getUser(@RequestBody Map<String, String> json)
{
String email = json.get("email");
return userRepository.findById(email);
User found = new User();
AuthController au = new AuthController();
Map<String, String> res = au.verify(json); // if the jwt token could not be verified
if (res.containsKey("login") && res.get("login").equals("failed"))
{
found.setEmail("failed");
return found;
}
Optional<User> usr = userRepository.findById(res.get("user"));
if (!usr.isPresent())
{
found.setEmail("not found");
return found;
}
return usr.get();
}
@DeleteMapping(path = "/delete")
public @ResponseBody User deleteUser(@RequestBody Map<String, String> json)
@PostMapping(path = "/delete")
@ResponseBody
public User deleteUser(@RequestBody Map<String, String> json)
{
User found = null;
String email = json.get("email");
User found = new User();
AuthController au = new AuthController();
Map<String, String> res = au.verify(json); // if the jwt token could not be verified
if (res.containsKey("login") && res.get("login").equals("failed"))
{
found.setEmail("failed");
return found;
}
String email = res.get("user");
Optional<User> optionalUser = userRepository.findById(email);
if (optionalUser.isPresent())
{
System.out.println("in if statement");
found = optionalUser.get();
userRepository.deleteById(email);
return found;
}
return null;
found.setEmail("not found");
return found;
}
}
\ No newline at end of file
package com.example.accessingdatamysql.auth;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.accessingdatamysql.auth.JWT;
import io.jsonwebtoken.Claims;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.Map;
import java.util.HashMap;
import java.util.Optional;
import javax.print.attribute.standard.Media;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import com.example.accessingdatamysql.UserRepository;
import com.example.accessingdatamysql.User;
@Controller
@CrossOrigin
@RequestMapping(path="/auth") // This means URL's start with /auth (after Application path)
public class AuthController {
@Autowired // This means to get the bean called userRepository
// Which is auto-generated by Spring, we will use it to handle the data
private UserRepository userRepository;
@PostMapping(path="/login")
public @ResponseBody Map<String, String> login(@RequestBody Map<String, String> json)
{
// Assuming you have a JSON library for Java, you can use it to build the response
Map<String, String> res = new HashMap<String, String>();
if (!json.containsKey("email") || !json.containsKey("password"))
{
res.put("result", "bad request");
return res;
}
Optional<User> user = userRepository.findById(json.get("email"));
if (user.isPresent())
{
User usr = user.get();
if (usr.getEmail().equals(json.get("email")) && usr.getPassword().equals(json.get("password")))
{
res.put("user", user.get().getEmail());
//give them a token
res.put("jwt", JWT.createJWT("id", "issuer", "sarthaks@vt.edu", 99999999));
res.put("result", "success");
return res;
}
res.put("result", "bad password");
return res;
}
res.put("result", "bad username");
return res;
}
@PostMapping(path="/verify")
public @ResponseBody Map<String, String> verify(@RequestBody Map<String, String> json)
{
Map<String, String> res = new HashMap<String, String>();
if (json.containsKey("jwt"))
{
Claims claim = JWT.decodeJWT(json.get("jwt"));
if (claim != null)
res.put("user", claim.getSubject());
}
else
{
res.put("login", "failed");
}
return res;
}
}
package com.example.accessingdatamysql.auth;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import io.jsonwebtoken.*;
import java.util.Date;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.Claims;
/*
A simple static class that is used to create and decode JWTs.
*/
public class JWT{
// The secret key. This should be in a property file NOT under source
// control and not hard coded in real life. We're putting it here for
// simplicity.
private static String SECRET_KEY = "secret dev key";
private static final long DEFAULT_TTL = 99999;
//Sample method to construct a JWT
public static String createJWT(String id, String issuer, String subject, long ttlMillis) {
//The JWT signature algorithm we will be using to sign the token
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
//We will sign our JWT with our ApiKey secret
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SECRET_KEY);
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
//Let's set the JWT Claims
JwtBuilder builder = Jwts.builder().setId(id)
.setIssuedAt(now)
.setSubject(subject)
.setIssuer(issuer)
.signWith(signatureAlgorithm, signingKey);
//if it has been specified, let's add the expiration
if (ttlMillis >= 0) {
long expMillis = nowMillis + ttlMillis + DEFAULT_TTL; // pad with default amount
Date exp = new Date(expMillis);
builder.setExpiration(exp);
}
//Builds the JWT and serializes it to a compact, URL-safe string
return builder.compact();
}
public static Claims decodeJWT(String jwt) {
//This line will throw an exception if it is not a signed JWS (as expected)
try
{
Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(SECRET_KEY))
.parseClaimsJws(jwt).getBody();
return claims;
}
catch (Exception e)
{
return null;
}
}
}
\ No newline at end of file
package com.example.accessingdatamysql.auth;
public class Token {
String token;
}
spring.jpa.hibernate.ddl-auto=create
spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/inventory
spring.datasource.username=root
spring.datasource.password=CSD@mysql-1872
spring.datasource.password=czarthak
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#spring.jpa.show-sql: true
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment