authbackendpackage 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.js +196 -0
  2. package/package.json +33 -0
package/index.js ADDED
@@ -0,0 +1,196 @@
1
+ // File: index.js
2
+
3
+ import dotenv from "dotenv";
4
+ dotenv.config();
5
+
6
+ import bcrypt from "bcryptjs";
7
+ import jwt from "jsonwebtoken";
8
+ import nodemailer from "nodemailer";
9
+ import dns from "dns/promises";
10
+
11
+ export const createAuthModule = ({ userModel, cloudinaryInstance, jwtSecret, mailUser, mailPass, env = "development" }) => {
12
+
13
+ const otpStorage = new Map();
14
+
15
+ const generateToken = (userId, res) => {
16
+ const token = jwt.sign({ userId }, jwtSecret, {
17
+ expiresIn: "7d",
18
+ });
19
+
20
+ res.cookie("jwt", token, {
21
+ httpOnly: true,
22
+ secure: env !== "development",
23
+ sameSite: "strict",
24
+ maxAge: 30 * 24 * 60 * 60 * 1000,
25
+ });
26
+
27
+ return token;
28
+ };
29
+
30
+ const transporter = nodemailer.createTransport({
31
+ service: "gmail",
32
+ auth: {
33
+ user: mailUser,
34
+ pass: mailPass,
35
+ },
36
+ });
37
+
38
+ const sendOtp = async (req, res) => {
39
+ const { email } = req.body;
40
+ if (!email) return res.status(400).json({ message: "Email is required", success: false });
41
+
42
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
43
+ if (!emailRegex.test(email)) return res.status(422).json({ message: "Invalid email format", success: false });
44
+
45
+ try {
46
+ const domain = email.split("@")[1];
47
+ const mxRecords = await dns.resolveMx(domain);
48
+ if (!mxRecords || mxRecords.length === 0) {
49
+ return res.status(452).json({ message: "Email domain does not accept mail", success: false });
50
+ }
51
+ } catch (dnsError) {
52
+ return res.status(452).json({ message: "Invalid or unreachable email domain", success: false });
53
+ }
54
+
55
+ const otp = Math.floor(100000 + Math.random() * 900000).toString();
56
+ const mailOptions = {
57
+ from: `PulseTalk <${mailUser}>`,
58
+ to: email,
59
+ subject: "🔐 Your PulseTalk OTP Code",
60
+ html: `Your OTP code is: <strong>${otp}</strong> (valid for 10 minutes)`
61
+ };
62
+
63
+ try {
64
+ const info = await transporter.sendMail(mailOptions);
65
+ if (info.accepted.includes(email)) {
66
+ otpStorage.set(email, { otp, verified: false });
67
+ return res.status(200).json({ message: "OTP sent", success: true });
68
+ } else {
69
+ return res.status(452).json({ message: "SMTP did not accept the email", success: false });
70
+ }
71
+ } catch (err) {
72
+ return res.status(502).json({ message: "Failed to send email", success: false });
73
+ }
74
+ };
75
+
76
+ const verifyOTP = (req, res) => {
77
+ const { email, otp } = req.body;
78
+ const record = otpStorage.get(email);
79
+ if (record && record.otp === otp) {
80
+ otpStorage.set(email, { ...record, verified: true });
81
+ return res.status(200).json({ message: "OTP verified", success: true });
82
+ }
83
+ return res.status(400).json({ message: "Invalid OTP", success: false });
84
+ };
85
+
86
+ const signup = async (req, res) => {
87
+ try {
88
+ const { email, password, name } = req.body;
89
+ const { profilePicture } = req.files || {};
90
+ const record = otpStorage.get(email);
91
+
92
+ if (!record || !record.verified) return res.status(400).json({ message: "OTP not verified for this email" });
93
+ if (!email || !password || !name) return res.status(400).json({ message: "Missing required fields" });
94
+ if (password.length < 6) return res.status(401).json({ message: "Password too short" });
95
+
96
+ const existingUser = await userModel.findOne({ email });
97
+ if (existingUser) return res.status(400).json({ message: "User already exists" });
98
+
99
+ const hashPassword = await bcrypt.hash(password, 10);
100
+ const newUser = new userModel({ email, password: hashPassword, name });
101
+ await newUser.save();
102
+ generateToken(newUser._id, res);
103
+ otpStorage.delete(email);
104
+
105
+ return res.status(201).json({
106
+ message: "User created",
107
+ success: true,
108
+ user: newUser,
109
+ });
110
+ } catch (err) {
111
+ return res.status(500).json({ message: err.message });
112
+ }
113
+ };
114
+
115
+ const login = async (req, res) => {
116
+ try {
117
+ const { email, password } = req.body;
118
+ const user = await userModel.findOne({ email });
119
+ if (!user) return res.status(400).json({ message: "User does not exist" });
120
+
121
+ const validPassword = await bcrypt.compare(password, user.password);
122
+ if (!validPassword) return res.status(400).json({ message: "Invalid password" });
123
+
124
+ generateToken(user._id, res);
125
+ return res.status(200).json({ message: "Login successful", success: true, user });
126
+ } catch (error) {
127
+ return res.status(500).json({ message: error.message });
128
+ }
129
+ };
130
+
131
+ const logout = async (req, res) => {
132
+ res.cookie("jwt", "", { maxAge: 0 });
133
+ return res.status(200).json({ message: "Logout successful" });
134
+ };
135
+
136
+ const updateProfile = async (req, res) => {
137
+ try {
138
+ const userId = req.user._id;
139
+ const { name, profilePicture } = req.body;
140
+
141
+ const updatedFields = {};
142
+ if (name) updatedFields.name = name;
143
+
144
+ if (profilePicture && profilePicture.trim() !== '') {
145
+ try {
146
+ const pic = await cloudinaryInstance.uploader.upload(profilePicture);
147
+ updatedFields.profilePicture = pic.secure_url;
148
+ } catch {
149
+ return res.status(400).json({ message: "Invalid profile picture format" });
150
+ }
151
+ }
152
+
153
+ const user = await userModel.findByIdAndUpdate(userId, updatedFields, { new: true });
154
+ return res.status(200).json({ message: "Profile updated", success: true, user });
155
+ } catch (error) {
156
+ return res.status(500).json({ message: error.message });
157
+ }
158
+ };
159
+
160
+ const checkAuth = async (req, res) => {
161
+ try {
162
+ const user = req.user;
163
+ return res.status(200).json({ message: "User authenticated", success: true, user });
164
+ } catch (error) {
165
+ return res.status(500).json({ message: error.message });
166
+ }
167
+ };
168
+
169
+ const forgotPassword = async (req, res) => {
170
+ try {
171
+ const { email, newPassword } = req.body;
172
+ if (!email || !newPassword) return res.status(400).json({ message: "Email and new password are required" });
173
+
174
+ const user = await userModel.findOne({ email });
175
+ if (!user) return res.status(400).json({ message: "User does not exist" });
176
+
177
+ const hashPassword = await bcrypt.hash(newPassword, 10);
178
+ await userModel.findByIdAndUpdate(user._id, { password: hashPassword });
179
+
180
+ return res.status(200).json({ message: "Password updated", success: true });
181
+ } catch (error) {
182
+ return res.status(500).json({ message: error.message });
183
+ }
184
+ };
185
+
186
+ return {
187
+ sendOtp,
188
+ verifyOTP,
189
+ signup,
190
+ login,
191
+ logout,
192
+ updateProfile,
193
+ checkAuth,
194
+ forgotPassword,
195
+ };
196
+ };
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "authbackendpackage",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "test": "npm run test"
7
+ },
8
+ "keywords": [
9
+ "auth",
10
+ "otp",
11
+ "nodemailer",
12
+ "jwt",
13
+ "bcrypt",
14
+ "cloudinary"
15
+ ],
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/shreyashpatel5506/authbackendpackage.git"
19
+ },
20
+ "author": "shreyash patel",
21
+ "license": "Apache-2.0",
22
+ "bugs": {
23
+ "url": "https://github.com/shreyashpatel5506/authbackendpackage/issues"
24
+ },
25
+ "homepage": "https://github.com/shreyashpatel5506/authbackendpackage#readme",
26
+ "description": "",
27
+ "dependencies": {
28
+ "bcryptjs": "^3.0.2",
29
+ "dotenv": "^17.0.1",
30
+ "jsonwebtoken": "^9.0.2",
31
+ "nodemailer": "^7.0.4"
32
+ }
33
+ }