-
Notifications
You must be signed in to change notification settings - Fork 446
project-auth #330
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
project-auth #330
Changes from all commits
986d1cb
0256451
8e4d19d
6138e9d
476f39a
2dcd3c2
9a147df
3981a8b
ae3d227
de6637d
f3985b1
e386d78
ce3103e
54bda1b
3482796
6435e86
4b74bee
c22d173
9fc07a3
8998200
1e8acb2
db8a464
a4238ac
a5cb66f
85205ed
9ec31ff
d068231
134ce3d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,17 @@ | ||
| # Project Auth API | ||
|
|
||
| Replace this readme with your own information about your project. | ||
|
|
||
| Start by briefly describing the assignment in a sentence or two. Keep it short and to the point. | ||
| This weeks assignment was to build a backend API and a React frontend. The backend handles user authentication, while the frontend includes a registration form, token storage, and access to restricted (secret) content after login. | ||
|
|
||
| ## The problem | ||
|
|
||
| Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next? | ||
| Backend: | ||
| I followed the codealong and added more specific error messages. | ||
|
|
||
| Frontend: | ||
| I started by creating separate components for login and signup functionalities. These components communicate with the backend API for user authentication and account creation. Using styled components for styling and React Router for navigation. Error handling was implemented to provide clear feedback to users. You should get errors if you try and create an account that already exists, if you try to log in without creating a user, if you write the wrong username/password, you should get a success message when you've succesfully created a user which will be displayed on the login page ect. If I had more time, I would refine error messages and enhance user experience with features like form validation. I also intended to create a separate component named AccountPage.jsx to handle account-related functions and content. However, due to issues with the login, I the the code intended for the "Account Page" within LoginPage.jsx. This decision was made to resolve the login issues. | ||
|
|
||
| ## View it live | ||
|
|
||
| Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about. | ||
| Backend: https://project-auth-0pi0.onrender.com/ | ||
|
|
||
| Frontend: https://cheerful-froyo-159f59.netlify.app/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,24 +1,104 @@ | ||
| import cors from "cors"; | ||
| import express from "express"; | ||
| import mongoose from "mongoose"; | ||
| import crypto from "crypto"; | ||
| import bcrypt from "bcrypt-nodejs"; | ||
| import expressListEndpoints from "express-list-endpoints"; | ||
|
|
||
| const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-mongo"; | ||
| const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/auth"; | ||
| mongoose.connect(mongoUrl); | ||
| mongoose.Promise = Promise; | ||
|
|
||
| // Defines the port the app will run on. Defaults to 8080, but can be overridden | ||
| // when starting the server. Example command to overwrite PORT env variable value: | ||
| // PORT=9000 npm start | ||
| const port = process.env.PORT || 8080; | ||
| // Model | ||
| const User = mongoose.model("User", { | ||
| name: { | ||
| type: String, | ||
| unique: true, | ||
| }, | ||
| email: { | ||
| type: String, | ||
| unique: true, | ||
| }, | ||
| password: { | ||
| type: String, | ||
| required: true, | ||
| }, | ||
| accessToken: { | ||
| type: String, | ||
| default: () => crypto.randomBytes(128).toString("hex"), | ||
| }, | ||
| }); | ||
|
|
||
| // Middleware to authenticate user with access token | ||
| const authenticateUser = async (req, res, next) => { | ||
| const accessToken = req.header("Authorization"); | ||
| if (!accessToken) { | ||
| return res.status(401).json({ error: "Unauthorized: Access token is missing" }); | ||
| } | ||
|
|
||
| const user = await User.findOne({ accessToken }); | ||
| if (!user) { | ||
| return res.status(401).json({ error: "Unauthorized: Access token is invalid" }); | ||
| } | ||
|
|
||
| req.user = user; | ||
| next(); | ||
| }; | ||
|
|
||
| const port = process.env.PORT || 8082; | ||
| const app = express(); | ||
|
|
||
| // Add middlewares to enable cors and json body parsing | ||
| app.use(cors()); | ||
| app.use(cors({ | ||
| origin: 'https://cheerful-froyo-159f59.netlify.app' | ||
| })); | ||
| app.use(express.json()); | ||
|
|
||
| // Start defining your routes here | ||
| // Routes | ||
| app.get("/", (req, res) => { | ||
| res.send("Hello Technigo!"); | ||
| const endpoints = expressListEndpoints(app); | ||
| res.json(endpoints); | ||
| }); | ||
|
|
||
| // Registration endpoint to create a new user | ||
| app.post("/users", async (req, res) => { | ||
| try { | ||
| const { name, email, password } = req.body; | ||
| if (!name) { | ||
| return res.status(400).json({ error: "Name is required" }); | ||
| } | ||
| if (!email) { | ||
| return res.status(400).json({ error: "Email is required" }); | ||
| } | ||
| if (!password) { | ||
| return res.status(400).json({ error: "Password is required" }); | ||
| } | ||
|
|
||
| const user = new User({ name, email, password: bcrypt.hashSync(password) }); | ||
| await user.save(); | ||
| res.status(201).json({ id: user._id, accessToken: user.accessToken }); | ||
| } catch (err) { | ||
| console.error("User creation failed:", err); | ||
| res.status(400).json({ error: "Could not create user. Something went wrong." }); | ||
| } | ||
| }); | ||
|
|
||
| // Login endpoint to authenticate a user | ||
| // Find the user by email, if user is found and password is correct, generate and return an access token | ||
| app.post("/login", async (req, res) => { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be named /sessions to follow REST naming principles |
||
| const { email, password } = req.body; | ||
|
|
||
| const user = await User.findOne({ email }); | ||
| if (!user || !bcrypt.compareSync(password, user.password)) { | ||
| return res.status(401).json({ message: "Invalid email or password" }); | ||
| } | ||
|
|
||
| const accessToken = user.accessToken; | ||
| res.status(200).json({ accessToken }); | ||
| }); | ||
|
|
||
| // Route to access secrets after authentication | ||
| app.get("/secrets", authenticateUser, (req, res) => { | ||
| res.json({ secret: "This is a super secret message" }); | ||
| }); | ||
|
|
||
| // Start the server | ||
|
|
||
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,16 @@ | ||
| import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; | ||
| import { StartPage } from "./components/StartPage.jsx"; | ||
| import { LoginPage } from "./components/LoginPage.jsx"; | ||
| import { SignUpPage } from "./components/SignUpPage.jsx"; | ||
|
|
||
| export const App = () => { | ||
| return <div>Find me in src/app.jsx!</div>; | ||
| }; | ||
| return ( | ||
| <Router> | ||
| <Routes> | ||
| <Route path="/" element={<StartPage />} /> | ||
| <Route path="/login" element={<LoginPage />} /> | ||
| <Route path="/signup" element={<SignUpPage />} /> | ||
| </Routes> | ||
| </Router> | ||
| ); | ||
| }; |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| import { useState } from "react"; | ||
|
|
||
| import styled from "styled-components"; | ||
| import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; | ||
| import { faEye, faEyeSlash } from "@fortawesome/free-solid-svg-icons"; | ||
|
|
||
| export const FormContainer = styled.div` | ||
| text-align: center; | ||
| `; | ||
|
|
||
| export const Form = styled.form` | ||
| display: flex; | ||
| flex-direction: column; | ||
| align-items: center; | ||
| margin-top: 2em; | ||
| `; | ||
|
|
||
| const InputContainer = styled.div` | ||
| position: relative; | ||
| `; | ||
|
|
||
| const InputField = styled.input` | ||
| font-size: 1em; | ||
| padding: 0.75em; | ||
| padding-right: 2.5em; | ||
| margin: 0.25em; | ||
| width: 295px; | ||
| border: 1.5px solid #ffffff; | ||
| border-radius: 0.5em; | ||
| background-color: rgba(255, 255, 255, 0.13); | ||
| `; | ||
|
|
||
| const EyeIcon = styled(FontAwesomeIcon)` | ||
| position: absolute; | ||
| top: 50%; | ||
| right: 0.75em; | ||
| transform: translateY(-50%); | ||
| cursor: pointer; | ||
| color: #fffffff; | ||
| `; | ||
|
|
||
| export const Input = ({ type, placeholder, value, onChange }) => { | ||
| const [showPassword, setShowPassword] = useState(false); | ||
|
|
||
| const toggleShowPassword = () => { | ||
| setShowPassword((prevShowPassword) => !prevShowPassword); | ||
| }; | ||
|
|
||
| return ( | ||
| <InputContainer> | ||
| <InputField | ||
| type={showPassword ? "text" : type} | ||
| placeholder={placeholder} | ||
| value={value} | ||
| onChange={onChange} | ||
| required | ||
| /> | ||
| {type === "password" && ( | ||
| <EyeIcon | ||
| icon={showPassword ? faEyeSlash : faEye} | ||
| onClick={toggleShowPassword} | ||
| /> | ||
| )} | ||
| </InputContainer> | ||
| ); | ||
| }; | ||
|
|
||
| export const AuthForm = ({ onSubmit, children }) => { | ||
| return ( | ||
| <FormContainer> | ||
| <Form onSubmit={onSubmit}>{children}</Form> | ||
| </FormContainer> | ||
| ); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice error handling ⭐