authbackendpackage 1.1.3 → 1.1.8

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 (3) hide show
  1. package/README.md +265 -263
  2. package/index.js +200 -196
  3. package/package.json +4 -2
package/README.md CHANGED
@@ -1,263 +1,265 @@
1
-
2
- ---
3
-
4
- # 🔐 AuthBackendPackage
5
-
6
- A flexible and plug-and-play authentication module for [Node.js](w) applications. Provides features such as [OTP](w)-based verification, [JWT](w) authentication, email verification, password reset, and user profile management.
7
-
8
- ✅ **Successfully tested and used in production at:**
9
- 🔗 [https://pulsetalk-6lrk.onrender.com](https://pulsetalk-6lrk.onrender.com)
10
-
11
- ---
12
-
13
- ## 🔧 Installation
14
-
15
- ```bash
16
- npm i authbackendpackage
17
- ```
18
-
19
- ---
20
-
21
- ## 📦 Module Setup
22
-
23
- ```js
24
- // index.js or app.js
25
- import express from "express";
26
- import { createAuthModule } from "authbackendpackage";
27
- import userModel from "./models/user.model.js";
28
- import cloudinary from "./lib/cloudinary.js";
29
-
30
- const app = express();
31
-
32
- const auth = createAuthModule({
33
- userModel,
34
- cloudinaryInstance: cloudinary,
35
- jwtSecret: process.env.JWT_SECRET,
36
- mailUser: process.env.MY_MAIL,
37
- mailPass: process.env.MY_PASSWORD,
38
- env: process.env.NODE_ENV,
39
- });
40
- ```
41
-
42
- ---
43
-
44
- ## ☁️ Cloudinary Configuration
45
-
46
- Create an account on [Cloudinary](https://cloudinary.com/), generate API credentials, and store them in your `.env` file.
47
-
48
- **Cloudinary Instance:**
49
-
50
- ```js
51
- import { config } from "dotenv";
52
- import { v2 as cloudinary } from "cloudinary";
53
- config();
54
-
55
- cloudinary.config({
56
- cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
57
- api_key: process.env.CLOUDINARY_API_KEY,
58
- api_secret: process.env.CLOUDINARY_API_SECRET,
59
- });
60
-
61
- export default cloudinary;
62
- ```
63
-
64
- ---
65
-
66
- ## 🔐 JWT Secret
67
-
68
- Set a secure `JWT_SECRET` string in your `.env` file.
69
-
70
- ---
71
-
72
- ## 📧 Mail Setup
73
-
74
- Generate an **App Password** from your Gmail settings and store it in `.env`.
75
-
76
- 👉 Follow this [Gmail App Password Guide](https://itsupport.umd.edu/itsupport?id=kb_article_view&sysparm_article=KB0015112)
77
-
78
- ---
79
-
80
- ## 👤 User Model Example
81
-
82
- ```js
83
- import mongoose from 'mongoose';
84
-
85
- const userSchema = new mongoose.Schema({
86
- email: { type: String, required: true, unique: true },
87
- name: { type: String, required: true },
88
- password: { type: String, required: true },
89
- profilePicture: { type: String, default: "" },
90
- }, { timestamps: true });
91
-
92
- const User = mongoose.model('User', userSchema);
93
- export default User;
94
- ```
95
-
96
- ---
97
-
98
- ## 🔀 Routes Setup
99
-
100
- ```js
101
- app.post("/api/send-otp", auth.sendOtp);
102
- app.post("/api/verify-otp", auth.verifyOTP);
103
- app.post("/api/signup", auth.signup);
104
- app.post("/api/login", auth.login);
105
- app.post("/api/logout", auth.logout);
106
- app.put("/api/update-profile", auth.updateProfile);
107
- app.get("/api/check-auth", auth.checkAuth);
108
- app.post("/api/forgot-password", auth.forgotPassword);
109
- ```
110
-
111
- ---
112
-
113
- ## 🛡️ Middleware: Protect Route
114
-
115
- ```js
116
- import jwt from "jsonwebtoken";
117
- import user from "../models/user.model.js";
118
- import dotenv from "dotenv";
119
- dotenv.config();
120
-
121
- export const protectRoute = async (req, res, next) => {
122
- try {
123
- const token = req.cookies.jwt;
124
- if (!token) {
125
- return res.status(401).json({ message: "Not authorized - No token provided" });
126
- }
127
-
128
- const decoded = jwt.verify(token, process.env.JWT_SECRET || "shreyash5506");
129
- const foundUser = await user.findById(decoded.userId).select("-password");
130
-
131
- if (!foundUser) {
132
- return res.status(401).json({ message: "Not authorized - User not found" });
133
- }
134
-
135
- req.user = foundUser;
136
- next();
137
- } catch (error) {
138
- console.error("Auth middleware error:", error);
139
- res.status(401).json({ message: "Not authorized - Invalid token" });
140
- }
141
- }
142
- ```
143
-
144
- ---
145
-
146
- ## 🧠 Features
147
-
148
- * ✅ OTP verification via email (SMTP)
149
- * ✅ Signup with verified OTP
150
- * ✅ Secure login with JWT
151
- * ✅ Profile update with image support (Cloudinary)
152
- * ✅ Forgot password with [bcrypt](w)
153
- * ✅ Cookie-based logout
154
- * ✅ Middleware-ready routes
155
-
156
- ---
157
-
158
- ## 🧪 Example `.env`
159
-
160
- ```env
161
- MY_MAIL=your-email@gmail.com
162
- MY_PASSWORD=your-app-password
163
- JWT_SECRET=your-secret-key
164
- NODE_ENV=development
165
- CLOUDINARY_CLOUD_NAME=your-cloud-name
166
- CLOUDINARY_API_KEY=your-api-key
167
- CLOUDINARY_API_SECRET=your-api-secret
168
- ```
169
-
170
- ---
171
-
172
- ## 📥 Request Examples
173
-
174
- ### 1. Send OTP
175
-
176
- ```http
177
- POST /api/send-otp
178
- Content-Type: application/json
179
- {
180
- "email": "user@example.com"
181
- }
182
- ```
183
-
184
- ### 2. Verify OTP
185
-
186
- ```http
187
- POST /api/verify-otp
188
- Content-Type: application/json
189
- {
190
- "email": "user@example.com",
191
- "otp": "123456"
192
- }
193
- ```
194
-
195
- ### 3. Signup
196
-
197
- ```http
198
- POST /api/signup
199
- Content-Type: application/json
200
- {
201
- "email": "user@example.com",
202
- "password": "your-password",
203
- "name": "User Name"
204
- }
205
- ```
206
-
207
- ### 4. Login
208
-
209
- ```http
210
- POST /api/login
211
- Content-Type: application/json
212
- {
213
- "email": "user@example.com",
214
- "password": "your-password"
215
- }
216
- ```
217
-
218
- ### 5. Update Profile
219
-
220
- ```http
221
- PUT /api/update-profile
222
- Content-Type: application/json
223
- {
224
- "name": "New Name",
225
- "profilePicture": "base64encodedImageOrUrl"
226
- }
227
- ```
228
-
229
- ### 6. Forgot Password
230
-
231
- ```http
232
- POST /api/forgot-password
233
- Content-Type: application/json
234
- {
235
- "email": "user@example.com",
236
- "newPassword": "new-secure-password"
237
- }
238
- ```
239
-
240
- ---
241
-
242
- ## 🔐 Cookie-Based JWT Auth
243
-
244
- Authentication is done using `httpOnly` cookies which automatically expire after 7 days for enhanced security.
245
-
246
- ---
247
-
248
- ## 🚀 Live Usage Demo
249
-
250
- **Successfully running on:**
251
- 🌐 [https://pulsetalk-6lrk.onrender.com](https://pulsetalk-6lrk.onrender.com)
252
-
253
- ---
254
-
255
- ## 📄 License
256
-
257
- Licensed under [Apache-2.0](w).
258
-
259
- ---
260
-
261
- Built with ❤️ by the **Shreyash Team**
262
-
263
- ---
1
+
2
+ ---
3
+
4
+ # 🔐 AuthBackendPackage
5
+
6
+ A flexible and plug-and-play authentication module for [Node.js](w) applications. Provides features such as [OTP](w)-based verification, [JWT](w) authentication, email verification, password reset, and user profile management.
7
+
8
+ ✅ **Successfully tested and used in production at:**
9
+ 🔗 [https://pulsetalk-6lrk.onrender.com](https://pulsetalk-6lrk.onrender.com)
10
+
11
+ ---
12
+
13
+ ## 🔧 Installation
14
+
15
+ ```bash
16
+ npm i authbackendpackage
17
+ ```
18
+
19
+ ---
20
+
21
+ ## 📦 Module Setup
22
+
23
+ ```js
24
+ // index.js or app.js
25
+ import express from "express";
26
+ import { createAuthModule } from "authbackendpackage";
27
+ import userModel from "./models/user.model.js";
28
+ import cloudinary from "./lib/cloudinary.js";
29
+
30
+ const app = express();
31
+
32
+ const auth = createAuthModule({
33
+ userModel,
34
+ cloudinaryInstance: cloudinary,
35
+ jwtSecret: process.env.JWT_SECRET,
36
+ BREVO_API_KEY=your_brevo_api_key_here
37
+ BREVO_SENDER_EMAIL=noreply@pulsetalk.com
38
+ BREVO_SENDER_NAME=PulseTalk
39
+ env: process.env.NODE_ENV,
40
+ });
41
+ ```
42
+
43
+ ---
44
+
45
+ ## ☁️ Cloudinary Configuration
46
+
47
+ Create an account on [Cloudinary](https://cloudinary.com/), generate API credentials, and store them in your `.env` file.
48
+
49
+ **Cloudinary Instance:**
50
+
51
+ ```js
52
+ import { config } from "dotenv";
53
+ import { v2 as cloudinary } from "cloudinary";
54
+ config();
55
+
56
+ cloudinary.config({
57
+ cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
58
+ api_key: process.env.CLOUDINARY_API_KEY,
59
+ api_secret: process.env.CLOUDINARY_API_SECRET,
60
+ });
61
+
62
+ export default cloudinary;
63
+ ```
64
+
65
+ ---
66
+
67
+ ## 🔐 JWT Secret
68
+
69
+ Set a secure `JWT_SECRET` string in your `.env` file.
70
+
71
+ ---
72
+
73
+ ## 📧 Mail Setup
74
+
75
+ Generate an **App Password** from your Gmail settings and store it in `.env`.
76
+
77
+ 👉 Follow this [Gmail App Password Guide](https://itsupport.umd.edu/itsupport?id=kb_article_view&sysparm_article=KB0015112)
78
+
79
+ ---
80
+
81
+ ## 👤 User Model Example
82
+
83
+ ```js
84
+ import mongoose from 'mongoose';
85
+
86
+ const userSchema = new mongoose.Schema({
87
+ email: { type: String, required: true, unique: true },
88
+ name: { type: String, required: true },
89
+ password: { type: String, required: true },
90
+ profilePicture: { type: String, default: "" },
91
+ }, { timestamps: true });
92
+
93
+ const User = mongoose.model('User', userSchema);
94
+ export default User;
95
+ ```
96
+
97
+ ---
98
+
99
+ ## 🔀 Routes Setup
100
+
101
+ ```js
102
+ app.post("/api/send-otp", auth.sendOtp);
103
+ app.post("/api/verify-otp", auth.verifyOTP);
104
+ app.post("/api/signup", auth.signup);
105
+ app.post("/api/login", auth.login);
106
+ app.post("/api/logout", auth.logout);
107
+ app.put("/api/update-profile", auth.updateProfile);
108
+ app.get("/api/check-auth", auth.checkAuth);
109
+ app.post("/api/forgot-password", auth.forgotPassword);
110
+ ```
111
+
112
+ ---
113
+
114
+ ## 🛡️ Middleware: Protect Route
115
+
116
+ ```js
117
+ import jwt from "jsonwebtoken";
118
+ import user from "../models/user.model.js";
119
+ import dotenv from "dotenv";
120
+ dotenv.config();
121
+
122
+ export const protectRoute = async (req, res, next) => {
123
+ try {
124
+ const token = req.cookies.jwt;
125
+ if (!token) {
126
+ return res.status(401).json({ message: "Not authorized - No token provided" });
127
+ }
128
+
129
+ const decoded = jwt.verify(token, process.env.JWT_SECRET || "shreyash5506");
130
+ const foundUser = await user.findById(decoded.userId).select("-password");
131
+
132
+ if (!foundUser) {
133
+ return res.status(401).json({ message: "Not authorized - User not found" });
134
+ }
135
+
136
+ req.user = foundUser;
137
+ next();
138
+ } catch (error) {
139
+ console.error("Auth middleware error:", error);
140
+ res.status(401).json({ message: "Not authorized - Invalid token" });
141
+ }
142
+ }
143
+ ```
144
+
145
+ ---
146
+
147
+ ## 🧠 Features
148
+
149
+ * ✅ OTP verification via email (SMTP)
150
+ * ✅ Signup with verified OTP
151
+ * ✅ Secure login with JWT
152
+ * ✅ Profile update with image support (Cloudinary)
153
+ * ✅ Forgot password with [bcrypt](w)
154
+ * ✅ Cookie-based logout
155
+ * ✅ Middleware-ready routes
156
+
157
+ ---
158
+
159
+ ## 🧪 Example `.env`
160
+
161
+ ```env
162
+ BREVO_API_KEY=your_brevo_api_key_here
163
+ BREVO_SENDER_EMAIL=noreply@pulsetalk.com
164
+ BREVO_SENDER_NAME=PulseTalk
165
+ JWT_SECRET=your-secret-key
166
+ NODE_ENV=development
167
+ CLOUDINARY_CLOUD_NAME=your-cloud-name
168
+ CLOUDINARY_API_KEY=your-api-key
169
+ CLOUDINARY_API_SECRET=your-api-secret
170
+ ```
171
+
172
+ ---
173
+
174
+ ## 📥 Request Examples
175
+
176
+ ### 1. Send OTP
177
+
178
+ ```http
179
+ POST /api/send-otp
180
+ Content-Type: application/json
181
+ {
182
+ "email": "user@example.com"
183
+ }
184
+ ```
185
+
186
+ ### 2. Verify OTP
187
+
188
+ ```http
189
+ POST /api/verify-otp
190
+ Content-Type: application/json
191
+ {
192
+ "email": "user@example.com",
193
+ "otp": "123456"
194
+ }
195
+ ```
196
+
197
+ ### 3. Signup
198
+
199
+ ```http
200
+ POST /api/signup
201
+ Content-Type: application/json
202
+ {
203
+ "email": "user@example.com",
204
+ "password": "your-password",
205
+ "name": "User Name"
206
+ }
207
+ ```
208
+
209
+ ### 4. Login
210
+
211
+ ```http
212
+ POST /api/login
213
+ Content-Type: application/json
214
+ {
215
+ "email": "user@example.com",
216
+ "password": "your-password"
217
+ }
218
+ ```
219
+
220
+ ### 5. Update Profile
221
+
222
+ ```http
223
+ PUT /api/update-profile
224
+ Content-Type: application/json
225
+ {
226
+ "name": "New Name",
227
+ "profilePicture": "base64encodedImageOrUrl"
228
+ }
229
+ ```
230
+
231
+ ### 6. Forgot Password
232
+
233
+ ```http
234
+ POST /api/forgot-password
235
+ Content-Type: application/json
236
+ {
237
+ "email": "user@example.com",
238
+ "newPassword": "new-secure-password"
239
+ }
240
+ ```
241
+
242
+ ---
243
+
244
+ ## 🔐 Cookie-Based JWT Auth
245
+
246
+ Authentication is done using `httpOnly` cookies which automatically expire after 7 days for enhanced security.
247
+
248
+ ---
249
+
250
+ ## 🚀 Live Usage Demo
251
+
252
+ ✅ **Successfully running on:**
253
+ 🌐 [https://pulsetalk-6lrk.onrender.com](https://pulsetalk-6lrk.onrender.com)
254
+
255
+ ---
256
+
257
+ ## 📄 License
258
+
259
+ Licensed under [Apache-2.0](w).
260
+
261
+ ---
262
+
263
+ Built with ❤️ by the **Shreyash Team**
264
+
265
+ ---
package/index.js CHANGED
@@ -1,196 +1,200 @@
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
- };
1
+ // File: index.js
2
+ import axios from "axios";
3
+ import dotenv from "dotenv";
4
+ dotenv.config();
5
+
6
+ import bcrypt from "bcryptjs";
7
+ import jwt from "jsonwebtoken";
8
+ import dns from "dns/promises";
9
+ import axios from "axios";
10
+
11
+ export const createAuthModule = ({
12
+ userModel,
13
+ cloudinaryInstance,
14
+ jwtSecret,
15
+ env = "development",
16
+ }) => {
17
+
18
+ const otpStorage = new Map();
19
+
20
+ // ================= TOKEN =================
21
+ const generateToken = (userId, res) => {
22
+ const token = jwt.sign({ userId }, jwtSecret, { expiresIn: "7d" });
23
+
24
+ res.cookie("jwt", token, {
25
+ httpOnly: true,
26
+ secure: env !== "development",
27
+ sameSite: "strict",
28
+ maxAge: 30 * 24 * 60 * 60 * 1000,
29
+ });
30
+
31
+ return token;
32
+ };
33
+
34
+ // ================= BREVO API =================
35
+ const sendBrevoEmail = async ({ to, subject, html }) => {
36
+ return axios.post(
37
+ "https://api.brevo.com/v3/smtp/email",
38
+ {
39
+ sender: {
40
+ name: process.env.BREVO_SENDER_NAME,
41
+ email: process.env.BREVO_SENDER_EMAIL,
42
+ },
43
+ to: [{ email: to }],
44
+ subject,
45
+ htmlContent: html,
46
+ },
47
+ {
48
+ headers: {
49
+ "api-key": process.env.BREVO_API_KEY,
50
+ "Content-Type": "application/json",
51
+ },
52
+ }
53
+ );
54
+ };
55
+
56
+ // ================= SEND OTP =================
57
+ const sendOtp = async (req, res) => {
58
+ const { email } = req.body;
59
+ if (!email) return res.status(400).json({ message: "Email required", success: false });
60
+
61
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
62
+ if (!emailRegex.test(email))
63
+ return res.status(422).json({ message: "Invalid email", success: false });
64
+
65
+ try {
66
+ const domain = email.split("@")[1];
67
+ const mx = await dns.resolveMx(domain);
68
+ if (!mx.length)
69
+ return res.status(452).json({ message: "Invalid email domain", success: false });
70
+ } catch {
71
+ return res.status(452).json({ message: "Email domain unreachable", success: false });
72
+ }
73
+
74
+ const otp = Math.floor(100000 + Math.random() * 900000).toString();
75
+
76
+ try {
77
+ await sendBrevoEmail({
78
+ to: email,
79
+ subject: "🔐 Your PulseTalk OTP",
80
+ html: `
81
+ <h2>OTP Verification</h2>
82
+ <p>Your OTP is:</p>
83
+ <h1>${otp}</h1>
84
+ <p>Valid for 10 minutes</p>
85
+ `,
86
+ });
87
+
88
+ otpStorage.set(email, {
89
+ otp,
90
+ verified: false,
91
+ createdAt: Date.now(),
92
+ });
93
+
94
+ return res.status(200).json({ message: "OTP sent", success: true });
95
+ } catch (error) {
96
+ return res.status(502).json({
97
+ message: "Brevo API email failed",
98
+ success: false,
99
+ });
100
+ }
101
+ };
102
+
103
+ // ================= VERIFY OTP =================
104
+ const verifyOTP = (req, res) => {
105
+ const { email, otp } = req.body;
106
+ const record = otpStorage.get(email);
107
+
108
+ if (record && record.otp === otp) {
109
+ otpStorage.set(email, { ...record, verified: true });
110
+ return res.status(200).json({ message: "OTP verified", success: true });
111
+ }
112
+
113
+ return res.status(400).json({ message: "Invalid OTP", success: false });
114
+ };
115
+
116
+ // ================= SIGNUP =================
117
+ const signup = async (req, res) => {
118
+ try {
119
+ const { email, password, name } = req.body;
120
+ const record = otpStorage.get(email);
121
+
122
+ if (!record || !record.verified)
123
+ return res.status(400).json({ message: "OTP not verified" });
124
+
125
+ if (!email || !password || !name)
126
+ return res.status(400).json({ message: "Missing fields" });
127
+
128
+ if (password.length < 6)
129
+ return res.status(400).json({ message: "Password too short" });
130
+
131
+ const exists = await userModel.findOne({ email });
132
+ if (exists)
133
+ return res.status(400).json({ message: "User already exists" });
134
+
135
+ const hash = await bcrypt.hash(password, 10);
136
+ const user = await userModel.create({ email, password: hash, name });
137
+
138
+ generateToken(user._id, res);
139
+ otpStorage.delete(email);
140
+
141
+ return res.status(201).json({ message: "User created", success: true, user });
142
+ } catch (err) {
143
+ return res.status(500).json({ message: err.message });
144
+ }
145
+ };
146
+
147
+ // ================= LOGIN =================
148
+ const login = async (req, res) => {
149
+ try {
150
+ const { email, password } = req.body;
151
+ const user = await userModel.findOne({ email });
152
+ if (!user) return res.status(400).json({ message: "User not found" });
153
+
154
+ const valid = await bcrypt.compare(password, user.password);
155
+ if (!valid) return res.status(400).json({ message: "Invalid password" });
156
+
157
+ generateToken(user._id, res);
158
+ return res.status(200).json({ message: "Login successful", success: true, user });
159
+ } catch (err) {
160
+ return res.status(500).json({ message: err.message });
161
+ }
162
+ };
163
+
164
+ const logout = (req, res) => {
165
+ res.cookie("jwt", "", { maxAge: 0 });
166
+ res.status(200).json({ message: "Logout successful" });
167
+ };
168
+
169
+ const checkAuth = (req, res) => {
170
+ res.status(200).json({ success: true, user: req.user });
171
+ };
172
+
173
+ const forgotPassword = async (req, res) => {
174
+ try {
175
+ const { email, newPassword } = req.body;
176
+ if (!email || !newPassword)
177
+ return res.status(400).json({ message: "Missing fields" });
178
+
179
+ const user = await userModel.findOne({ email });
180
+ if (!user) return res.status(400).json({ message: "User not found" });
181
+
182
+ const hash = await bcrypt.hash(newPassword, 10);
183
+ await userModel.findByIdAndUpdate(user._id, { password: hash });
184
+
185
+ res.status(200).json({ message: "Password updated", success: true });
186
+ } catch (err) {
187
+ res.status(500).json({ message: err.message });
188
+ }
189
+ };
190
+
191
+ return {
192
+ sendOtp,
193
+ verifyOTP,
194
+ signup,
195
+ login,
196
+ logout,
197
+ checkAuth,
198
+ forgotPassword,
199
+ };
200
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "authbackendpackage",
3
- "version": "1.1.3",
3
+ "version": "1.1.8",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "npm run test"
@@ -9,6 +9,7 @@
9
9
  "auth",
10
10
  "otp",
11
11
  "nodemailer",
12
+ "axios",
12
13
  "jwt",
13
14
  "bcrypt",
14
15
  "cloudinary"
@@ -25,10 +26,11 @@
25
26
  "homepage": "https://github.com/shreyashpatel5506/authbackendpackage#readme",
26
27
  "description": "",
27
28
  "dependencies": {
29
+ "axios": "^1.13.2",
28
30
  "bcryptjs": "^3.0.2",
29
31
  "dns": "^0.2.2",
30
32
  "dotenv": "^17.0.1",
31
33
  "jsonwebtoken": "^9.0.2",
32
34
  "nodemailer": "^7.0.4"
33
35
  }
34
- }
36
+ }