├── .env ├── .gitignore ├── README.md ├── controllers └── auth.js ├── middlewares └── index.js ├── models └── user.js ├── package.json ├── routes └── auth.js ├── server.js └── utils └── auth.js /.env: -------------------------------------------------------------------------------- 1 | DATABASE="mongodb://localhost:27017/apiDatabase" 2 | PORT=8000 3 | JWT_SECRET=fjksdlkfjje24klj23lkejsjljfs@lkdjfsdj34j3.,/DSFSDFK222 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # create-api-app 2 | 3 | ### It is a REST-API right-of-way utility that uses Express and for authenticate use JWT and cookies and is secure against CSRF attacks. 4 | 5 | ###### * have build in login / register / authorize 6 | 7 | for start use this command 8 | ``` 9 | npm start 10 | // or 11 | yarn start 12 | ``` -------------------------------------------------------------------------------- /controllers/auth.js: -------------------------------------------------------------------------------- 1 | import User from "../models/user"; 2 | import { hashPassword, comparePassword } from "../utils/auth"; 3 | import jwt from "jsonwebtoken"; 4 | 5 | export const register = async (req, res) => { 6 | try { 7 | //* get data and validations 8 | const { name, email, password } = req.body; 9 | if (!name || !email || !password) { 10 | res.status(422).json({ status: 422, message: "invalid inputs!" }); 11 | return; 12 | } 13 | 14 | //* checking for similar email 15 | const isExistUser = await User.findOne({ email }); 16 | if (isExistUser) { 17 | res.status(422).json({ status: 422, message: "email is taken!" }); 18 | return; 19 | } 20 | 21 | //* hashing password 22 | const hashedPassword = await hashPassword(password); 23 | 24 | //* add a new user 25 | const user = await new User({ name, email, password: hashedPassword }).save(); 26 | 27 | res.status(201).json({ status: 201, message: "ok!" }); 28 | return; 29 | } catch (e) { 30 | res.status(500).json({ status: 500, message: "server error!", errorMessage: e }); 31 | return; 32 | } 33 | }; 34 | 35 | export const login = async (req, res) => { 36 | try { 37 | const { email, password } = req.body; 38 | 39 | //* check for user exist 40 | const user = await User.findOne({ email }).exec(); 41 | if (!user) { 42 | return res.status(404).json({ status: 404, message: "user not found!" }); 43 | } 44 | 45 | //* check password 46 | const isMatch = await comparePassword(password, user.password); 47 | if (!isMatch) { 48 | return res.status(422).json({ status: 422, message: "password mismatch!" }); 49 | } 50 | 51 | //* make token 52 | const token = jwt.sign({ _id: user._id, role: user.role }, process.env.JWT_SECRET, { 53 | expiresIn: "7d", 54 | }); 55 | 56 | //* set token in cookie 57 | res.cookie("token", token, { 58 | httpOnly: true, 59 | // secure: true //* only in https 60 | }); 61 | 62 | //* clear password for response 63 | user.password = undefined; 64 | 65 | return res.status(200).json({ status: 200, message: "success full login!", data: user }); 66 | } catch (e) { 67 | console.log(e); 68 | return; 69 | } 70 | }; 71 | 72 | export const logout = async (req, res) => { 73 | try { 74 | res.clearCookie("token"); 75 | return res.status(200).json({ message: "logout successfull!" }); 76 | } catch (e) { 77 | console.log(e); 78 | return; 79 | } 80 | }; 81 | 82 | export const currentUser = async (req, res) => { 83 | try{ 84 | const user = await User.findById(req.user._id).select("-password").exec(); 85 | console.log("CURRENT_USER", user); 86 | return res.status(200).json({status: 200, data: user}); 87 | } 88 | catch(e){ 89 | console.log(e); 90 | return; 91 | } 92 | } -------------------------------------------------------------------------------- /middlewares/index.js: -------------------------------------------------------------------------------- 1 | import expressJwt from "express-jwt"; 2 | 3 | export const requireSignIn = expressJwt({ 4 | getToken: (req, res) => req.cookies.token, 5 | secret: process.env.JWT_SECRET, 6 | algorithms: ["HS256"], 7 | }); 8 | -------------------------------------------------------------------------------- /models/user.js: -------------------------------------------------------------------------------- 1 | import { Schema, model } from "mongoose"; 2 | 3 | const userSchema = new Schema( 4 | { 5 | name: { 6 | type: String, 7 | trim: true, 8 | required: true, 9 | }, 10 | email: { 11 | type: String, 12 | trim: true, 13 | required: true, 14 | unique: true, 15 | }, 16 | password: { 17 | type: String, 18 | trim: true, 19 | required: true, 20 | min: 6, 21 | max: 64, 22 | }, 23 | picture: { 24 | type: String, 25 | default: "./avatar.png", 26 | }, 27 | role: { 28 | type: [String], 29 | default: ["Subscriber"], 30 | enum: ["Subscriber", "Admin"], 31 | }, 32 | stripe_account_id: "", 33 | stripe_seller: {}, 34 | stripeSession: {}, 35 | }, 36 | { timestamps: true } 37 | ); 38 | 39 | export default model("User", userSchema); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-api-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon -r esm server.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcrypt": "^5.0.1", 14 | "cookie-parser": "^1.4.6", 15 | "cors": "^2.8.5", 16 | "csurf": "^1.11.0", 17 | "dotenv": "^16.0.0", 18 | "esm": "^3.2.25", 19 | "express": "^4.17.3", 20 | "express-jwt": "^6.1.1", 21 | "jsonwebtoken": "^8.5.1", 22 | "mongoose": "^6.2.2", 23 | "morgan": "^1.10.0", 24 | "nodemon": "^2.0.15" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /routes/auth.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import {register, login, logout, currentUser} from "../controllers/auth"; 3 | import { requireSignIn } from "../middlewares"; 4 | 5 | //* make router middleware 6 | const router = express.Router(); 7 | 8 | //* routes 9 | router.post("/register", register); 10 | router.post("/login", login); 11 | router.get("/logout", logout); 12 | router.get("/current-user", requireSignIn, currentUser); 13 | 14 | module.exports = router; -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import cors from "cors"; 3 | import dotenv from "dotenv"; 4 | import { readdirSync } from "fs"; 5 | import mongoose from "mongoose"; 6 | import csurf from "csurf"; 7 | import cookieParser from "cookie-parser"; 8 | const morgan = require("morgan"); 9 | 10 | // * add support env 11 | dotenv.config(); 12 | 13 | //* initial csrf protection 14 | const csrfProtection = csurf({ cookie: true }); 15 | 16 | //* create app 17 | const app = express(); 18 | 19 | //* connect to database 20 | mongoose 21 | .connect(process.env.DATABASE) 22 | .then(() => { 23 | console.log("connected to db!"); 24 | }) 25 | .catch((e) => { 26 | console.log("error connecting to db : ", e); 27 | }); 28 | 29 | //* apply middlewares 30 | app.use(cors()); 31 | app.use(express.json()); 32 | app.use(morgan("dev")); 33 | app.use(cookieParser()); 34 | app.use(csrfProtection); 35 | app.use((req, res, next) => { 36 | next(); 37 | }); 38 | 39 | //* add routes 40 | readdirSync("./routes").map((route) => { 41 | app.use("/api", require(`./routes/${route}`)); 42 | }); 43 | 44 | //* add csrf 45 | app.get("/api/csrf-token", (req, res) => { 46 | res.json({ csrfToken: req.csrfToken() }); 47 | }); 48 | 49 | //* initialing port start server 50 | const PORT = process.env.PORT || 8000; 51 | app.listen(PORT, () => { 52 | console.log(`server start on port ${PORT}`); 53 | }); 54 | 55 | //* notes 56 | //? this is for mongoose options 57 | // { 58 | // userNewUrlParser: true, 59 | // useFindAndModify: false, 60 | // useUnifiedTopology: true, 61 | // useCreateIndex: true, 62 | // } 63 | -------------------------------------------------------------------------------- /utils/auth.js: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcrypt"; 2 | 3 | export const hashPassword = (password) => { 4 | return new Promise((resolve, reject) => { 5 | bcrypt.genSalt(12, (err, salt) => { 6 | if(err){ 7 | reject(err); 8 | } 9 | bcrypt.hash(password, salt, (err, hashedPassword) => { 10 | if(err){ 11 | reject(err); 12 | } 13 | resolve(hashedPassword); 14 | }); 15 | }); 16 | }); 17 | } 18 | 19 | export const comparePassword = (password, hashPassword) => { 20 | return bcrypt.compare(password, hashPassword); 21 | } 22 | 23 | 24 | --------------------------------------------------------------------------------