simple-auth-cli
Version:
An implementation of authentication system supporting multiple providers ready to be used with a single command.
365 lines (351 loc) • 14.3 kB
text/typescript
import asyncHandler from '../utils/asyncHandler.ts';
import {apiError} from '../utils/apiError.ts';
import { User } from '../models/user.model.ts';
import { apiResponse } from '../utils/apiResponse.ts';
import { sendEmail } from '../helpers/sendEmail.ts';
import { nanoid } from 'nanoid';
import { APPURL, VERIFICATIONTOKENEXPIRYTIME ,APPNAME} from '../constants.ts';
import bcrypt from 'bcrypt';
import { deleteOnCloudinary, uploadOnCloudinary } from '../services/cloudinary.service.ts';
const cookieOptions={
httpOnly:true,
secure:true,
}
const generateAccessTokenAndRefreshToken =async(user) => {
const accessToken=await user.generateAccessToken();
const refreshToken=await user.generateRefreshToken();
user.refreshToken=refreshToken;
await user.save({validateBeforeSave:false});
return {accessToken,refreshToken};
}
const registerUser=asyncHandler(async(req,res)=>{
const{username,email,password}=req.body;
if([username,email,password].some((field)=>field?.trim()===""))
{
throw new apiError(400,"All fields are required")
}
else if(password===email||password===username){
throw new apiError(400,"Password should not be same as username or email")
}
else{
const existingUser=await User.findOne({$or:[{email},{username}]});
console.log(existingUser)
if (existingUser) {
if(existingUser.username===username && existingUser.isVerified==true){
throw new apiError(409,"User Username already taken")
}
throw new apiError(409,"User email already exists")
}
else{
const verificationToken=nanoid(10);
const verificationTokenExpiryDate=Date.now()+VERIFICATIONTOKENEXPIRYTIME*1000;
const avatar=await uploadOnCloudinary(`https://ui-avatars.com/api/?name=${username.replace(' ','+')}&background=random&rounded=true&format=png&size=128`)
const user=await User.create({
username,
email,
password,
verificationToken,
verificationTokenExpiryDate,
avatar:avatar?.url,
});
const createdUser=await User.findById(user._id).select(
"-password -refreshToken "
);
if(!createdUser){
throw new apiError(500,"User not created something went wrong while registering the user")
}
await sendEmail(createdUser.email, "verify", {
username: createdUser.username,
token: createdUser.verificationToken
});
const {accessToken,refreshToken}= await generateAccessTokenAndRefreshToken(user);
return res.status(200)
.cookie("accessToken",accessToken,cookieOptions)
.cookie("refreshToken",refreshToken,cookieOptions)
.json(new apiResponse(200,createdUser,"User registered successfully"))
}
}
})
const loginUser=asyncHandler(async(req,res)=>{
const{email,password}=req.body;
if([email,password].some((field)=>field?.trim()===""))
{
throw new apiError(400,"All fields are required")
}
else{
const user=await User.findOne({email});
if(!user){
throw new apiError(404,"User not found")
}
const isPasswordCorrect=await user.isPasswordCorrect(password);
if(!isPasswordCorrect){
throw new apiError(401,"Invalid credentials");
}
const {accessToken,refreshToken}= await generateAccessTokenAndRefreshToken(user);
await user.save({validateBeforeSave:false});
const logginedUser=await User.findById(user._id).select("-password -refreshToken");
if(!logginedUser){
throw new apiError(500,"Failed to login the user")
}
return res.status(200)
.cookie("accessToken",accessToken,cookieOptions)
.cookie("refreshToken",refreshToken,cookieOptions)
.json(new apiResponse(200,{user:logginedUser,accessToken,refreshToken},"User logged in successfully"))
}
});
const logoutUser = asyncHandler(async(req, res) => {
await User.findByIdAndUpdate(
req.user._id,
{
$unset: {
refreshToken: 1
}
},
{
new: true
}
)
return res
.status(200)
.clearCookie("accessToken", cookieOptions)
.clearCookie("refreshToken", cookieOptions)
.json(new apiResponse(200, {}, "User logged Out"))
})
const generateNewTokens = asyncHandler(async(req, res) => {
const oldRefreshToken = req.cookies.refreshToken|| req.body.refreshToken;
if (!oldRefreshToken) {
throw new apiError(401, "Unauthorized request")
}
const user = await User.findOne({ refreshToken:oldRefreshToken });
if (!user) {
throw new apiError(401, "Unauthorized request user not found")
}
const { accessToken, refreshToken } = await generateAccessTokenAndRefreshToken(user);
user.refreshToken = refreshToken;
await user.save({ validateBeforeSave: false });
return res
.status(200)
.cookie("accessToken", accessToken, cookieOptions)
.cookie("refreshToken", refreshToken, cookieOptions)
.json(new apiResponse(200, { accessToken, refreshToken }, "Tokens generated successfully"))
})
const verifyUser= asyncHandler(async(req,res)=>{
const token=req.body.verificationToken;
if(!token){
throw new apiError(400,"Verification token is required")
}
const user=await User.findOne({verificationToken:token}).select(
"-password -refreshToken"
);
if(!user){
throw new apiError(404,"User not found")
}
if(user.verificationTokenExpiryDate !=null || user.verificationTokenExpiryDate !=undefined){
const currDate=new Date(Date.now());
if(user.verificationTokenExpiryDate<currDate){
throw new apiError(400,"Verification token expired")
}
else{
user.isVerified=true;
user.verificationToken=undefined;
user.verificationTokenExpiryDate=undefined;
await user.save({validateBeforeSave:false});
return res.status(200).json(new apiResponse(200,{},"User verified successfully"))
}
}
else{
if(user.isVerified){
throw new apiError(400,"User is already verified")
}
else{
throw new apiError(500,"Verification token not found")
}
}
})
const forgotPassword=asyncHandler(async(req,res)=>{
const user=await User.findById(
req.user._id
).select("-password -refreshToken -verificationToken -verificationTokenExpiryDate");
if(!user){
throw new apiError(404,"User not found")
}
const verificationToken=nanoid(10);
user.verificationToken=verificationToken;
user.verificationTokenExpiryDate=new Date(Date.now()+VERIFICATIONTOKENEXPIRYTIME*1000);
await user.save({validateBeforeSave:false});
await sendEmail(user.email, "forgotPassword", {
username: user.username,
token: user.verificationToken
});
return res.status(200).json(new apiResponse(200,{}, "Password reset link sent to your email"))
})
const changePassword=asyncHandler(async(req,res)=>{
const {verificationToken,newPassword,oldPassword}=req.body;
if(!verificationToken){
throw new apiError(400,"Verification token is required")
}
const user=await User.findOne({verificationToken:verificationToken});
if(!user){
throw new apiError(404,"User not found")
}
if(user.verificationTokenExpiryDate !=null || user.verificationTokenExpiryDate !=undefined){
if(user.verificationTokenExpiryDate<new Date(Date.now())){
throw new apiError(400,"Verification token expired")
}
if(!bcrypt.compareSync(oldPassword,user.password)){
throw new apiError(400,"Old password is incorrect")
}
user.password=newPassword;
user.verificationToken=undefined;
user.verificationTokenExpiryDate=undefined;
await user.save({validateBeforeSave:false});
return res.status(200).json(new apiResponse(200,user,"Password changed successfully"))
}
else{
throw new apiError(500,"Verification token not found as user didnot request for password change")
}
})
const resendVerificationToken=asyncHandler(async(req,res)=>{
const user=await User.findById(req.user._id).select("-password -refreshToken ");
if(!user){
throw new apiError(404,"User not found")
}
if(user.verificationToken==undefined){
throw new apiError(400,"cannot find verification token to resend")
}
if(user.isVerified){
throw new apiError(400,"User is already verified")
}
const verificationToken=nanoid(10);
user.verificationToken=verificationToken;
user.verificationTokenExpiryDate=new Date(Date.now()+VERIFICATIONTOKENEXPIRYTIME*1000);
await user.save({validateBeforeSave:false});
await sendEmail(user.email, "verify", {
username: user.username,
token: user.verificationToken
});
return res.status(200).json(new apiResponse(200,{}, "Verification token sent to your email"))
})
const changeEmail=asyncHandler(async(req,res)=>{
const user=await User.findById(req.user._id).select("-password -refreshToken ");
if(!user){
throw new apiError(404,"User not found")
}
const verificationToken=nanoid(10);
user.verificationToken=verificationToken;
user.verificationTokenExpiryDate=new Date(Date.now()+VERIFICATIONTOKENEXPIRYTIME*1000);
await user.save({validateBeforeSave:false});
await sendEmail(user.email, "emailChangeVerification", {
username: user.username,
token: user.verificationToken
});
return res.status(200).json(new apiResponse(200,{}, "Email change link sent to your email"))
})
const updateEmail=asyncHandler(async(req,res)=>{
const user=await User.findById(req.user._id).select("-password -refreshToken ");
if(!user){
throw new apiError(404,"User not found")
}
if(user.verificationTokenExpiryDate !=null || user.verificationTokenExpiryDate !=undefined){
if(user.verificationTokenExpiryDate<new Date(Date.now())){
throw new apiError(400,"Verification token expired")
}
else{
const oldEmail=user.email;
user.email=req.body.email;
user.verificationToken=undefined;
user.verificationTokenExpiryDate=undefined;
await user.save({validateBeforeSave:false});
await sendEmail(oldEmail, "emailChange", {
username: user.username,
email:user.email
});
return res.status(200).json(new apiResponse(200,user,"Email updated successfully"))
}
}
else{
throw new apiError(500,"Verification token not found as user didnot request for email change")
}
})
const forgotUserName=asyncHandler(async(req, res) => {
const user = await User.findById(req.user._id).select("-password -refreshToken -verificationToken -verificationTokenExpiryDate");
if (!user) {
throw new apiError(404, "User not found")
}
if(user.email===req.body.email){
return res.status(200).json(new apiResponse(200,user.username, "username found successfully"))
}
else{
throw new apiError(404,"Email not found")
}
})
const forgotEmail=asyncHandler(async(req, res) => {
const user = await User.findById(req.user._id).select("-password -refreshToken -verificationToken -verificationTokenExpiryDate");
if (!user) {
throw new apiError(404, "User not found")
}
if(user.username===req.body.username){
return res.status(200).json(new apiResponse(200,user.email, "email found successfully"))
}
else{
throw new apiError(404,"Username not found")
}
})
const changeUserName=asyncHandler(async(req,res)=>{
const user=await User.findById(req.user._id).select("-password -refreshToken ");
if(!user){
throw new apiError(404,"User not found")
}
const userWithSameUsername=await User.findOne({username:req.body.username});
if(userWithSameUsername){
throw new apiError(400,"Username already exists")
}
else{
user.username=req.body.username;
await user.save({validateBeforeSave:false});
return res.status(200).json(new apiResponse(200,user,"Username updated successfully"))
}
})
const updateAvatar=asyncHandler(async(req,res)=>{
const user=await User.findById(req.user._id).select("-password -refreshToken ");
if(!user){
throw new apiError(404,"User not found")
}
const oldAvatarUrl=user.avatar;
const avatarLocalFilePath=req.file?.path;
if(!avatarLocalFilePath){
throw new apiError(400,"Avatar is required")
}
const avatar=await uploadOnCloudinary(avatarLocalFilePath)
if(!avatar?.url){
throw new apiError(500,"Failed to upload avatar to cloudinary")
}
user.avatar=avatar.url;
await user.save({validateBeforeSave:false});
await deleteOnCloudinary(oldAvatarUrl);
return res.status(200).json(new apiResponse(200,user,"Avatar updated successfully"))
})
const getUserDetails=asyncHandler(async(req,res)=>{
const user=await User.findById(req.user._id).select("-password -refreshToken -verificationToken -verificationTokenExpiryDate");
if(!user){
throw new apiError(404,"User not found")
}
return res.status(200).json(new apiResponse(200,user,"User details fetched successfully"))
})
export {
registerUser,
loginUser,
logoutUser,
generateNewTokens,
verifyUser,
forgotPassword,
changePassword,
resendVerificationToken,
changeEmail,
updateEmail,
forgotUserName,
forgotEmail,
changeUserName,
updateAvatar,
getUserDetails
}