Skip to content

Commit 1c35d77

Browse files
author
vignesh-codes
committed
initial commit
1 parent 1dc26e1 commit 1c35d77

23 files changed

+6011
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
logs

nodemon.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"watch": ["src"],
3+
"ext": ".ts,.js",
4+
"exec": "ts-node ./src/index.ts"
5+
}

package-lock.json

+5,385
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"name": "typescript-node-auth",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"start": "nodemon",
8+
"test": "jest"
9+
},
10+
"keywords": [],
11+
"author": "vignesh-codes",
12+
"license": "ISC",
13+
"devDependencies": {
14+
"@types/body-parser": "^1.19.5",
15+
"@types/compression": "^1.7.5",
16+
"@types/cookie-parser": "^1.4.7",
17+
"@types/cors": "^2.8.17",
18+
"@types/express": "^4.17.21",
19+
"@types/jsonwebtoken": "^9.0.6",
20+
"@types/lodash": "^4.17.0",
21+
"@types/mongoose": "^5.11.97",
22+
"@types/morgan": "^1.9.9",
23+
"@types/supertest": "^6.0.2",
24+
"@types/winston": "^2.4.4",
25+
"nodemon": "^3.1.0",
26+
"ts-node": "^10.9.2",
27+
"typescript": "^5.4.3"
28+
},
29+
"dependencies": {
30+
"body-parser": "^1.20.2",
31+
"compression": "^1.7.4",
32+
"cookie-parser": "^1.4.6",
33+
"cors": "^2.8.5",
34+
"express": "^4.19.1",
35+
"jsonwebtoken": "^9.0.2",
36+
"lodash": "^4.17.21",
37+
"mongoose": "^8.2.3",
38+
"morgan": "^1.10.0",
39+
"supertest": "^6.3.4",
40+
"winston": "^3.12.0"
41+
}
42+
}

src/constants/constants.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const JWT_SECRET = "jlasfndafgnogbnsrijgbrgujobn";
2+
export const MONGOURL = "xxx"

src/controllers/users.ts

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import express from 'express'
2+
3+
import { UserDatabase } from "../db/users";
4+
import { Logger } from '../loggers/logger'
5+
export class UserController {
6+
private static userDB = UserDatabase.getInstance();
7+
8+
public static getAllUsers = async (req: express.Request, res: express.Response) => {
9+
try {
10+
const users = await UserController.userDB.getUsers();
11+
res.send(users)
12+
res.end()
13+
} catch (error) {
14+
Logger.Error(error.toString())
15+
return res.sendStatus(400)
16+
}
17+
}
18+
19+
public static updateUser = async (req: express.Request, res: express.Response) => {
20+
try {
21+
const { username } = req.body
22+
const { id } = req.params
23+
if (!username) {
24+
return res.sendStatus(400)
25+
}
26+
const userToUpdate = await UserController.userDB.getUserById(id);
27+
userToUpdate.username = username;
28+
await userToUpdate.save();
29+
return res.status(200).json(userToUpdate).end()
30+
} catch (error) {
31+
Logger.Error(error.toString())
32+
res.sendStatus(400)
33+
}
34+
}
35+
36+
public static deleteUser = async (req: express.Request, res: express.Response) => {
37+
try {
38+
const { id } = req.params;
39+
const deletedUser = await UserController.userDB.deleteUserById(id);
40+
return res.json(deletedUser)
41+
} catch (error) {
42+
Logger.Error(error.toString())
43+
res.sendStatus(400)
44+
}
45+
}
46+
}

src/controllers/v1/authentication.ts

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import express from 'express';
2+
3+
import { UserDatabase } from '../../db/users';
4+
import { authentication, random } from '../../helpers'
5+
import { Logger } from '../../loggers/logger'
6+
export class V1AuthController {
7+
private static userDB = UserDatabase.getInstance();
8+
9+
public static v1login = async (req: express.Request, res: express.Response) => {
10+
try {
11+
const { email, password} = req.body;
12+
if (!email || !password ) {
13+
return res.sendStatus(400)
14+
}
15+
const user = await V1AuthController.userDB.getUserByEmail(email).select("+authentication.salt +authentication.password");
16+
17+
if (!user) {
18+
return res.sendStatus(400)
19+
}
20+
21+
const expectedHash = authentication(user.authentication.salt, password);
22+
if (user.authentication.password != expectedHash) {
23+
return res.sendStatus(401)
24+
}
25+
26+
const salt = random();
27+
user.authentication.sessionToken = authentication(salt, user._id.toString());
28+
await user.save()
29+
30+
res.cookie("VWS-AUTH", user.authentication.sessionToken, {
31+
domain: 'localhost',
32+
path: "/"
33+
});
34+
return res.status(200).json(user).end()
35+
36+
} catch (error) {
37+
Logger.Error(error.toString())
38+
return res.sendStatus(400)
39+
}
40+
}
41+
42+
public static v1register = async (req: express.Request, res: express.Response) => {
43+
try {
44+
const { email, password, username } = req.body;
45+
46+
if (!email || !password || !username) {
47+
return res.sendStatus(400);
48+
};
49+
50+
const existingUser = await V1AuthController.userDB.getUserByEmail(email);
51+
if (existingUser) {
52+
return res.sendStatus(400);
53+
};
54+
55+
const salt = random();
56+
const user = await V1AuthController.userDB.createUser({
57+
email,
58+
username,
59+
authentication: {
60+
salt,
61+
password: authentication(salt, password)
62+
},
63+
});
64+
65+
return res.status(200).json(user).end()
66+
67+
} catch (error) {
68+
Logger.Error(error.toString());
69+
return res.sendStatus(400);
70+
}
71+
}
72+
73+
public static v1logSuccessMsg = async (req: express.Request, res: express.Response, next: express.NextFunction) => {
74+
Logger.Info("successfully registered");
75+
next();
76+
}
77+
}

src/controllers/v2/authentication.ts

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import express from 'express';
2+
3+
import { UserDatabase } from '../../db/users';
4+
import { authentication, random } from '../../helpers'
5+
import { generateJwt } from '../../middlewares/v2/jwt';
6+
import { Logger } from '../../loggers/logger'
7+
export class V2AuthController {
8+
private static userDB = UserDatabase.getInstance();
9+
10+
public static v2login = async (req: express.Request, res: express.Response) => {
11+
try {
12+
const { email, password } = req.body;
13+
if (!email || !password) {
14+
return res.sendStatus(400)
15+
}
16+
const user = await V2AuthController.userDB.getUserByEmail(email).select("+authentication.salt +authentication.password");
17+
18+
if (!user) {
19+
return res.sendStatus(400)
20+
}
21+
22+
const expectedHash = authentication(user.authentication.salt, password);
23+
if (user.authentication.password != expectedHash) {
24+
return res.sendStatus(401)
25+
}
26+
let jwtToken = ""
27+
try {
28+
jwtToken = await generateJwt(user);
29+
} catch (error) {
30+
Logger.Error("Error generating JWT token:" + error.toString());
31+
return res.sendStatus(401)
32+
}
33+
let userObj = user.toObject()
34+
userObj.authentication.jwtToken = jwtToken;
35+
return res.status(200).json(userObj).end()
36+
37+
} catch (error) {
38+
Logger.Error(error.toString())
39+
return res.sendStatus(400)
40+
}
41+
}
42+
43+
44+
public static v2register = async (req: express.Request, res: express.Response) => {
45+
try {
46+
const { email, password, username } = req.body;
47+
48+
if (!email || !password || !username) {
49+
return res.sendStatus(400);
50+
};
51+
52+
const existingUser = await V2AuthController.userDB.getUserByEmail(email);
53+
if (existingUser) {
54+
return res.sendStatus(400);
55+
};
56+
57+
const salt = random();
58+
const user = await V2AuthController.userDB.createUser({
59+
email,
60+
username,
61+
authentication: {
62+
salt,
63+
password: authentication(salt, password)
64+
},
65+
});
66+
67+
return res.status(200).json(user).end()
68+
69+
} catch (error) {
70+
Logger.Error(error.toString());
71+
return res.sendStatus(400);
72+
}
73+
}
74+
}

src/db/initdb.ts

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import mongoose from 'mongoose';
2+
import { MONGOURL } from "../constants/constants"
3+
import {Logger} from "../loggers/logger"
4+
5+
export class MongoDB {
6+
public static async initDb() {
7+
try {
8+
Logger.Info("Trying to connect to MongoDB")
9+
await mongoose.connect(MONGOURL);
10+
Logger.Info("Connected to MongoDB");
11+
} catch (error) {
12+
Logger.Error("Error connecting to MongoDB:" + error.toString());
13+
}
14+
this.connectToDb()
15+
}
16+
17+
private static connectToDb() {
18+
mongoose.connection.on('error', (error: Error) => {
19+
Logger.Error("MongoDB connection error:"+ error.toString());
20+
});
21+
}
22+
}

src/db/users.ts

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import mongoose, { Schema, Document } from 'mongoose';
2+
import { User } from '../models/user';
3+
4+
export class UserDatabase {
5+
private static instance: UserDatabase;
6+
private userModel: mongoose.Model<User & Document>;
7+
8+
private constructor() {
9+
const userSchemaFields: Record<keyof User, any> = {
10+
username: { type: String, required: true },
11+
email: { type: String, required: true },
12+
authentication: {
13+
password: { type: String, required: true, select: false },
14+
salt: { type: String, select: false },
15+
sessionToken: { type: String, select: false }
16+
},
17+
};
18+
19+
const userSchema: Schema<User & Document> = new Schema(userSchemaFields);
20+
this.userModel = mongoose.models['User'] || mongoose.model<User & Document>('User', userSchema);
21+
}
22+
23+
public static getInstance(): UserDatabase {
24+
if (!UserDatabase.instance) {
25+
UserDatabase.instance = new UserDatabase();
26+
}
27+
return UserDatabase.instance;
28+
}
29+
30+
public getUsers() {
31+
return this.userModel.find();
32+
}
33+
34+
public getUserByEmail(email: string) {
35+
return this.userModel.findOne({ email });
36+
}
37+
38+
public getUserBySessionToken(sessionToken: string) {
39+
return this.userModel.findOne({ 'authentication.sessionToken': sessionToken });
40+
}
41+
42+
public getUserById(id: string) {
43+
return this.userModel.findById(id);
44+
}
45+
46+
public createUser(values: Record<string, any>) {
47+
return new this.userModel(values).save().then((user) => user.toObject());
48+
}
49+
50+
public deleteUserById(id: string) {
51+
return this.userModel.findOneAndDelete({ _id: id });
52+
}
53+
54+
public updateUserById(id: string, values: Record<string, any>) {
55+
return this.userModel.findByIdAndUpdate(id, values);
56+
}
57+
}

src/helpers/index.ts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import crypto from 'crypto';
2+
const SECRET = "VERY-STRONG-SECRET"
3+
4+
5+
export const random = () => crypto.randomBytes(128).toString('base64');
6+
export const authentication = (salt: string, password: string) => {
7+
return crypto.createHmac('sha256', [salt, password].join('/')).update(SECRET).digest('hex')
8+
}

src/index.ts

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import express from "express";
2+
import http from "http";
3+
import bodyParser from 'body-parser';
4+
import cookieParser from 'cookie-parser';
5+
import compression from 'compression';
6+
import cors from 'cors';
7+
import routers from "./router";
8+
import { accessLogger, Logger } from "./loggers/logger";
9+
import { MongoDB } from "./db/initdb";
10+
11+
MongoDB.initDb()
12+
const app = express();
13+
14+
app.use(accessLogger);
15+
16+
app.use(cors({
17+
credentials: true,
18+
}))
19+
20+
app.use(compression());
21+
app.use(cookieParser());
22+
app.use(bodyParser.json());
23+
24+
const server = http.createServer(app);
25+
26+
server.listen(8080, () => {
27+
Logger.Info(`Server is running on port 8080`);
28+
console.log("server running on http://localhost:8080");
29+
});
30+
31+
32+
const { v1router, v2router } = routers();
33+
34+
app.use("/v1", v1router);
35+
app.use("/v2", v2router);
36+
37+
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
38+
Logger.Error(err.stack || err.message || JSON.stringify(err));
39+
res.status(500).send('Something went wrong!');
40+
});

0 commit comments

Comments
 (0)