diff --git a/backend/.gitignore b/backend/.gitignore index 25c8fdbab..8f5e467c8 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,2 +1,3 @@ node_modules -package-lock.json \ No newline at end of file +package-lock.json +.env \ No newline at end of file diff --git a/backend/package.json b/backend/package.json index 8de5c4ce0..3d9bb40be 100644 --- a/backend/package.json +++ b/backend/package.json @@ -12,9 +12,12 @@ "@babel/core": "^7.17.9", "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.11", + "bcrypt": "^5.1.1", "cors": "^2.8.5", + "dotenv": "^16.4.5", "express": "^4.17.3", - "mongoose": "^8.0.0", - "nodemon": "^3.0.1" + "express-list-endpoints": "^7.1.0", + "mongoose": "^8.4.0", + "nodemon": "^3.1.1" } } diff --git a/backend/server.js b/backend/server.js index dfe86fb8e..d6be56a82 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,14 +1,46 @@ import cors from "cors"; +import crypto from "crypto"; +import bcrypt from "bcrypt"; import express from "express"; import mongoose from "mongoose"; +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"; +const mongoUrl = "mongodb://localhost/auth"; mongoose.connect(mongoUrl); mongoose.Promise = Promise; +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 function +const authenticateUser = async (req, res, next) => { + const user = await User.findOne({ accessToken: req.header("Authorization") }); + if (user) { + req.user = user; + next(); + } else { + res.status(401).json({ loggedOut: true }); + } +}; + // 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; const app = express(); @@ -16,9 +48,65 @@ const app = express(); app.use(cors()); app.use(express.json()); -// Start defining your routes here +// Documentation endpoint app.get("/", (req, res) => { - res.send("Hello Technigo!"); + const endpoints = expressListEndpoints(app); + const documentation = { + Welcome: "This is the Authentication API!", + Endpoints: { + "/": "Get API documentation", + "/users": { + POST: "Create a new user", + }, + "/sessions": { + POST: "Authenticate a returning user", + }, + "/secrets": { + GET: "Get secret content (requires authentication)", + }, + }, + }; + res.json(documentation); +}); + +app.post("/users", async (req, res) => { + try { + const { name, email, password } = req.body; + // DO NOT STORE PLAINTEXT PASSWORD + const salt = bcrypt.genSaltSync(); + const user = new User({ + name, + email, + password: bcrypt.hashSync(password, salt), + }); + await user.save(); // Await the save operation + res.status(201).json({ + success: true, + message: "User created", + id: user._id, + accessToken: user.accessToken, + }); + } catch (error) { + res.status(400).json({ success: false, message: "Could not create user", errors: error }); + } +}); + +app.post("/sessions", async (req, res) => { + const user = await User.findOne({ email: req.body.email }); + if (user && bcrypt.compareSync(req.body.password, user.password)) { + res.status(200).json({ + success: true, + message: "User authenticated", + id: user._id, + accessToken: user.accessToken, + }); + } else { + res.status(401).json({ success: false, message: "Invalid email or password" }); + } +}); + +app.get("/secrets", authenticateUser, (req, res) => { + res.json({ secret: "This is a super secret!" }); }); // Start the server diff --git a/frontend/index.html b/frontend/index.html index 0c589eccd..2bb7b743e 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,10 +4,12 @@ -