Authentication has become a Necessary part of most application, many a times you will build apps that require user authentication. The standard go to solution most developers use is to rely on a third party authentication system, like firebase, superbase this is not bad, I'd personally advice you to use one except you have a compelling reason not to. If however your reason is compelling enough then we are going to build a simple authentication system using NodeJS.
Dependencies
We need to spin up a new node project, create an empty folder and open it up in your editor, we will be using MongoDB and Mongoose to persist our user data so we will install the dependencies to handle that, first create a package.json
file.
npm init --y
Next we install Mongoose and other dependencies our application is going to rely on, let me give a brief list of the dependencies we will be installing;
- Bcrypt for hashing user password
- Cors for cross origin resourse sharing
- Express for http
- jsonwebtoken for user authorization
to install all of the above dependencies run the following command
npm i bcrypt express cors jsonwebtoken
To read more articles like this visit Netcreed
User Schema
Since we are working with Mongoose we need to create a user schema, a schema is just a logical structure that all object in a collection will conform to.
// UserSchema.ts
import { Schema } from "mongoose";
export const UserSchema = new Schema({
fullName: {
type: String
},
email: {
type: String
},
password: {
type: String
}
}, {
timestamps: true
})
We have a basic schema to represent our user object. We just declare three properties for simplicity sake, I am not going to dive into Mongoose here, We are just concerned with the auth system, if you don't understand then it's best you visit the Mongoose documentation to familiarize yourself with the library. We will now proceed to our define our user model.
/// Users.ts
import { UserSchema } from "./UserSchema";
import * as bcrypt from "bcrypt";
import * as jwt from "jsonwebtoken";
import { model } from "mongoose";
function createJWT = (email: string) => {
return jwt.sign(email, 'secrete', { expiresIn: 60*20 });
}
function verifyToken = (token: string) => {
const email = jwt.verify(token, "secrete");
if (email) {
return email;
}
}
UserSchema.statics.createAccount = async function (name: string, email: string, password: string) {
const hashedPassword = await bcrypt.hash(password.trim(), 12);
const user = await this.create({
name: name.trim(),
email: email.toLowerCase().trim(),
password: hashedPassword
});
const token = createJWT(user.email);
user.password = '';
return [user, token];
}
UserSchema.statics.login = async function (email: stirng, password: string) {
const user = await this.findOne({ email: email.toLowerCase() });
if (!user) {
throw Error("No user with that email");
}
const verifyPassword = await bcrypt.compare(password, user.password);
if (!verifyPassword) {
throw Error("Incorrect Password");
};
const token = createJWT(user.email);
user.password = '';
return [user, token];
}
UserSchema.statics.getUser = async function(token: string) {
const email = verifyToken(token);
if (email) {
const user = await this.findOne({ email });
return user;
}
return false;
}
export const Users = model("user", UserSchema);
Now we have implemented the function for creating an account and to login the user, We declared them on the Schema so we could make our codebase easy to manage. We need to define controllers that will handle the request we will make to the server, on the controller we will register each function to a request route.
// controller.ts
import { Users } from "./Users";
import { Request, Response } from "express";
interface user {
fullName: string
email: string
password: string
}
export const createAccount = async (req: Request, res: Response) => {
const { fullName, email, password }: user = req.body;
try {
const [user, token] = await Users.createAccount(fullName, email, password);
res.json({ user, token })
} catch (error: any) {
res.status(500).json({ message: error.message });
}
}
export const login = async (req: Request, res: Response) => {
const { email, password }: Pick<user, "email" | "password"> = req.body;
try {
const [user, token] = await Users.login(email, password);
res.json({ user, token });
} catch (error: any) {
if (error.message.includes("Incorrect Password") ||
error.message.includes("No user with that email")) {
res.status(404).json({ message: error.message });
}
res.status(500).json({ message: error.message });
}
}
export const getCurrentUser = async (req: Request, res: Response) => {
const { token } = req.params;
try {
const user = await Users.getUser(token);
if (user) {
res.json({ user });
}
} catch (error: any) {
res.status(500).json({ message: error.message });
}
}
We have defined some controller functions that will process user requests, we now register each controller function to a route, the controller function will serve as the route handler.
// router.ts
import { Router } from "express";
import { createAccount, login, getCurrentUser } from "./controller";
const router = Router();
router.post('/login', login);
router.post('/create-account', createAccount);
router.get('/current-user/:token', getCurrentUser);
export default router
Everything is all set and done, all we need to do now is create our server and register this route as a middleware.
// app.ts
import * as express from "express";
import * as cors from "cors";
import * as router from './router';
const app = express();
app.use(cors());
app.use(router);
const url = 'mongodb://localhost:27017/plastic';
mongoose.connect(url).then((d: any) => {
app.listen(PORT, () => console.log(`App running on PORT ${PORT}`));
})
app.get('/', (req:any, res:any) => {
res.end('Hello World!');
})
To read more articles like this visit Netcreed
Top comments (0)