webortex-auth 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.
- package/.env.example +15 -0
- package/PUBLISHING.md +303 -0
- package/README.md +391 -0
- package/example-usage.js +70 -0
- package/package.json +37 -0
- package/src/controllers/auth.controller.js +156 -0
- package/src/index.js +9 -0
- package/src/models/User.model.js +70 -0
- package/src/routes/auth.routes.js +20 -0
- package/webortex-auth-1.0.0.tgz +0 -0
package/.env.example
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Example .env file for consuming application
|
|
2
|
+
# Copy this to .env and update with your values
|
|
3
|
+
|
|
4
|
+
# MongoDB Connection
|
|
5
|
+
MONGO_URI=mongodb://localhost:27017/your-database-name
|
|
6
|
+
|
|
7
|
+
# JWT Configuration (REQUIRED)
|
|
8
|
+
JWT_SECRET=your-super-secret-jwt-key-change-this-to-something-secure
|
|
9
|
+
|
|
10
|
+
# JWT Token Expiration (Optional, default: 24h)
|
|
11
|
+
JWT_EXPIRES_IN=24h
|
|
12
|
+
|
|
13
|
+
# Server Configuration
|
|
14
|
+
PORT=3000
|
|
15
|
+
NODE_ENV=development
|
package/PUBLISHING.md
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# Publishing webortex-auth to npm
|
|
2
|
+
|
|
3
|
+
## Prerequisites
|
|
4
|
+
|
|
5
|
+
Before publishing, ensure you have:
|
|
6
|
+
- ✅ An npm account (create one at https://www.npmjs.com/signup)
|
|
7
|
+
- ✅ Node.js and npm installed
|
|
8
|
+
- ✅ Package is ready and tested
|
|
9
|
+
|
|
10
|
+
## Step-by-Step Publishing Guide
|
|
11
|
+
|
|
12
|
+
### 1. Create npm Account (if you don't have one)
|
|
13
|
+
|
|
14
|
+
Visit https://www.npmjs.com/signup and create an account.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
### 2. Login to npm
|
|
19
|
+
|
|
20
|
+
Open your terminal in the package directory and login:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm login
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
You'll be prompted for:
|
|
27
|
+
- Username
|
|
28
|
+
- Password
|
|
29
|
+
- Email (this will be public)
|
|
30
|
+
- One-time password (if 2FA is enabled)
|
|
31
|
+
|
|
32
|
+
**Verify login:**
|
|
33
|
+
```bash
|
|
34
|
+
npm whoami
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
### 3. Check Package Name Availability
|
|
40
|
+
|
|
41
|
+
Before publishing, verify the package name is available:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm search webortex-auth
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If the name is taken, you'll need to:
|
|
48
|
+
- Choose a different name, OR
|
|
49
|
+
- Use a scoped package: `@your-username/webortex-auth`
|
|
50
|
+
|
|
51
|
+
**To use a scoped package**, update `package.json`:
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"name": "@your-username/webortex-auth",
|
|
55
|
+
...
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
### 4. Verify Package Contents
|
|
62
|
+
|
|
63
|
+
Check what files will be published:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm pack --dry-run
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
This shows you exactly what will be included in the package. Verify:
|
|
70
|
+
- ✅ `src/` directory is included
|
|
71
|
+
- ✅ `README.md` is included
|
|
72
|
+
- ✅ `package.json` is included
|
|
73
|
+
- ❌ `node_modules/` is NOT included
|
|
74
|
+
- ❌ `.env` files are NOT included
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
### 5. Test Package Locally (Recommended)
|
|
79
|
+
|
|
80
|
+
Before publishing, test the package locally:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# In webortex-auth directory
|
|
84
|
+
npm pack
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
This creates a `.tgz` file (e.g., `webortex-auth-1.0.0.tgz`).
|
|
88
|
+
|
|
89
|
+
**Test in another project:**
|
|
90
|
+
```bash
|
|
91
|
+
# Create test project
|
|
92
|
+
mkdir test-webortex-auth
|
|
93
|
+
cd test-webortex-auth
|
|
94
|
+
npm init -y
|
|
95
|
+
|
|
96
|
+
# Install the local package
|
|
97
|
+
npm install ../webortex-auth/webortex-auth-1.0.0.tgz
|
|
98
|
+
|
|
99
|
+
# Test it works
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
### 6. Update Package Version (for future releases)
|
|
105
|
+
|
|
106
|
+
For the first publish, version `1.0.0` is fine. For future updates:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# Patch release (1.0.0 -> 1.0.1) - bug fixes
|
|
110
|
+
npm version patch
|
|
111
|
+
|
|
112
|
+
# Minor release (1.0.0 -> 1.1.0) - new features
|
|
113
|
+
npm version minor
|
|
114
|
+
|
|
115
|
+
# Major release (1.0.0 -> 2.0.0) - breaking changes
|
|
116
|
+
npm version major
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
### 7. Publish to npm
|
|
122
|
+
|
|
123
|
+
**For public package (free):**
|
|
124
|
+
```bash
|
|
125
|
+
npm publish
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**For scoped package (public):**
|
|
129
|
+
```bash
|
|
130
|
+
npm publish --access public
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**For private package (requires paid account):**
|
|
134
|
+
```bash
|
|
135
|
+
npm publish --access restricted
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
### 8. Verify Publication
|
|
141
|
+
|
|
142
|
+
After publishing, verify your package:
|
|
143
|
+
|
|
144
|
+
1. **Visit npm website:**
|
|
145
|
+
```
|
|
146
|
+
https://www.npmjs.com/package/webortex-auth
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
2. **Install from npm:**
|
|
150
|
+
```bash
|
|
151
|
+
npm install webortex-auth
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
3. **Check package info:**
|
|
155
|
+
```bash
|
|
156
|
+
npm info webortex-auth
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Common Issues & Solutions
|
|
162
|
+
|
|
163
|
+
### Issue: Package name already exists
|
|
164
|
+
|
|
165
|
+
**Solution:** Use a scoped package name:
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"name": "@your-username/webortex-auth"
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Issue: "You do not have permission to publish"
|
|
173
|
+
|
|
174
|
+
**Solutions:**
|
|
175
|
+
- Verify you're logged in: `npm whoami`
|
|
176
|
+
- Use scoped package with `--access public`
|
|
177
|
+
- Check if package name is already taken
|
|
178
|
+
|
|
179
|
+
### Issue: "npm ERR! 402 Payment Required"
|
|
180
|
+
|
|
181
|
+
**Solution:** You're trying to publish a private scoped package. Either:
|
|
182
|
+
- Publish as public: `npm publish --access public`
|
|
183
|
+
- Upgrade to npm Pro for private packages
|
|
184
|
+
|
|
185
|
+
### Issue: Files missing in published package
|
|
186
|
+
|
|
187
|
+
**Solution:** Check your `.npmignore` and ensure important files aren't excluded.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Post-Publication Checklist
|
|
192
|
+
|
|
193
|
+
After successful publication:
|
|
194
|
+
|
|
195
|
+
- [ ] Verify package appears on npm website
|
|
196
|
+
- [ ] Test installation: `npm install webortex-auth`
|
|
197
|
+
- [ ] Check README renders correctly on npm
|
|
198
|
+
- [ ] Add repository URL to package.json (if using GitHub)
|
|
199
|
+
- [ ] Add badges to README (version, downloads, license)
|
|
200
|
+
- [ ] Share with the community!
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Updating Your Package
|
|
205
|
+
|
|
206
|
+
When you make changes and want to publish an update:
|
|
207
|
+
|
|
208
|
+
1. **Make your changes**
|
|
209
|
+
2. **Update version:**
|
|
210
|
+
```bash
|
|
211
|
+
npm version patch # or minor/major
|
|
212
|
+
```
|
|
213
|
+
3. **Publish:**
|
|
214
|
+
```bash
|
|
215
|
+
npm publish
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Unpublishing (Use with Caution)
|
|
221
|
+
|
|
222
|
+
You can unpublish within 72 hours:
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
# Unpublish specific version
|
|
226
|
+
npm unpublish webortex-auth@1.0.0
|
|
227
|
+
|
|
228
|
+
# Unpublish entire package (only within 72 hours)
|
|
229
|
+
npm unpublish webortex-auth --force
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
⚠️ **Warning:** Unpublishing is discouraged as it breaks dependencies for others.
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Optional: Add to package.json
|
|
237
|
+
|
|
238
|
+
Consider adding these fields before publishing:
|
|
239
|
+
|
|
240
|
+
```json
|
|
241
|
+
{
|
|
242
|
+
"repository": {
|
|
243
|
+
"type": "git",
|
|
244
|
+
"url": "https://github.com/your-username/webortex-auth.git"
|
|
245
|
+
},
|
|
246
|
+
"bugs": {
|
|
247
|
+
"url": "https://github.com/your-username/webortex-auth/issues"
|
|
248
|
+
},
|
|
249
|
+
"homepage": "https://github.com/your-username/webortex-auth#readme"
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Quick Command Reference
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
# Login
|
|
259
|
+
npm login
|
|
260
|
+
|
|
261
|
+
# Check login
|
|
262
|
+
npm whoami
|
|
263
|
+
|
|
264
|
+
# Preview package contents
|
|
265
|
+
npm pack --dry-run
|
|
266
|
+
|
|
267
|
+
# Test package locally
|
|
268
|
+
npm pack
|
|
269
|
+
|
|
270
|
+
# Publish
|
|
271
|
+
npm publish
|
|
272
|
+
|
|
273
|
+
# Publish scoped package as public
|
|
274
|
+
npm publish --access public
|
|
275
|
+
|
|
276
|
+
# Update version
|
|
277
|
+
npm version patch|minor|major
|
|
278
|
+
|
|
279
|
+
# View package info
|
|
280
|
+
npm info webortex-auth
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## 🎉 Ready to Publish?
|
|
286
|
+
|
|
287
|
+
Run these commands in order:
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
# 1. Navigate to package directory
|
|
291
|
+
cd "d:\Prem Secure Solutions\webortex-auth"
|
|
292
|
+
|
|
293
|
+
# 2. Login to npm
|
|
294
|
+
npm login
|
|
295
|
+
|
|
296
|
+
# 3. Preview what will be published
|
|
297
|
+
npm pack --dry-run
|
|
298
|
+
|
|
299
|
+
# 4. Publish!
|
|
300
|
+
npm publish
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Good luck! 🚀
|
package/README.md
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
# webortex-auth
|
|
2
|
+
|
|
3
|
+
A production-ready, reusable authentication package for Node.js applications using Express and MongoDB (Mongoose) with dependency injection pattern.
|
|
4
|
+
|
|
5
|
+
## ✨ Features
|
|
6
|
+
|
|
7
|
+
- 🔐 **Secure Authentication** - JWT-based signup and login
|
|
8
|
+
- 💉 **Dependency Injection** - No internal MongoDB connections
|
|
9
|
+
- 🚀 **Zero Configuration** - Works out of the box
|
|
10
|
+
- 🔒 **Password Hashing** - Automatic bcrypt hashing
|
|
11
|
+
- ⚡ **ES Modules** - Modern JavaScript support
|
|
12
|
+
- 🎯 **Type Safe** - Clean API with factory functions
|
|
13
|
+
- 🔄 **Reusable** - Use across multiple projects
|
|
14
|
+
- 🛡️ **Production Ready** - Comprehensive error handling
|
|
15
|
+
|
|
16
|
+
## 📦 Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install webortex-auth
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 🚀 Quick Start
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
import express from 'express';
|
|
26
|
+
import mongoose from 'mongoose';
|
|
27
|
+
import { authRoutes } from 'webortex-auth';
|
|
28
|
+
|
|
29
|
+
const app = express();
|
|
30
|
+
app.use(express.json());
|
|
31
|
+
|
|
32
|
+
// 1. Connect to MongoDB FIRST
|
|
33
|
+
await mongoose.connect(process.env.MONGO_URI);
|
|
34
|
+
|
|
35
|
+
// 2. Pass connected mongoose instance to the package
|
|
36
|
+
app.use('/api/auth', authRoutes(mongoose));
|
|
37
|
+
|
|
38
|
+
app.listen(3000, () => {
|
|
39
|
+
console.log('Server running on port 3000');
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 🔧 Environment Variables
|
|
44
|
+
|
|
45
|
+
Create a `.env` file in your project root:
|
|
46
|
+
|
|
47
|
+
```env
|
|
48
|
+
MONGO_URI=mongodb://localhost:27017/your-database
|
|
49
|
+
JWT_SECRET=your-super-secret-jwt-key-change-this
|
|
50
|
+
JWT_EXPIRES_IN=24h
|
|
51
|
+
NODE_ENV=development
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Required:**
|
|
55
|
+
- `JWT_SECRET` - Secret key for JWT token generation
|
|
56
|
+
|
|
57
|
+
**Optional:**
|
|
58
|
+
- `JWT_EXPIRES_IN` - Token expiration time (default: `24h`)
|
|
59
|
+
- `NODE_ENV` - Environment mode (shows detailed errors in `development`)
|
|
60
|
+
|
|
61
|
+
## 📡 API Endpoints
|
|
62
|
+
|
|
63
|
+
### POST `/signup`
|
|
64
|
+
|
|
65
|
+
Register a new user.
|
|
66
|
+
|
|
67
|
+
**Request Body:**
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"name": "John Doe",
|
|
71
|
+
"email": "john@example.com",
|
|
72
|
+
"password": "securePassword123"
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Success Response (201):**
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"success": true,
|
|
80
|
+
"message": "User registered successfully",
|
|
81
|
+
"data": {
|
|
82
|
+
"user": {
|
|
83
|
+
"id": "507f1f77bcf86cd799439011",
|
|
84
|
+
"name": "John Doe",
|
|
85
|
+
"email": "john@example.com",
|
|
86
|
+
"createdAt": "2025-12-28T08:30:00.000Z"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Error Response (409 - Duplicate Email):**
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"success": false,
|
|
96
|
+
"message": "User with this email already exists"
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
### POST `/login`
|
|
103
|
+
|
|
104
|
+
Authenticate a user and receive JWT token.
|
|
105
|
+
|
|
106
|
+
**Request Body:**
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"email": "john@example.com",
|
|
110
|
+
"password": "securePassword123"
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Success Response (200):**
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"success": true,
|
|
118
|
+
"message": "Login successful",
|
|
119
|
+
"data": {
|
|
120
|
+
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
121
|
+
"user": {
|
|
122
|
+
"id": "507f1f77bcf86cd799439011",
|
|
123
|
+
"name": "John Doe",
|
|
124
|
+
"email": "john@example.com"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Error Response (401 - Invalid Credentials):**
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"success": false,
|
|
134
|
+
"message": "Invalid email or password"
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## 💡 Usage Examples
|
|
139
|
+
|
|
140
|
+
### Complete Express App
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
import express from 'express';
|
|
144
|
+
import mongoose from 'mongoose';
|
|
145
|
+
import dotenv from 'dotenv';
|
|
146
|
+
import { authRoutes } from 'webortex-auth';
|
|
147
|
+
|
|
148
|
+
dotenv.config();
|
|
149
|
+
|
|
150
|
+
const app = express();
|
|
151
|
+
app.use(express.json());
|
|
152
|
+
|
|
153
|
+
// Connect to MongoDB
|
|
154
|
+
const connectDB = async () => {
|
|
155
|
+
try {
|
|
156
|
+
await mongoose.connect(process.env.MONGO_URI);
|
|
157
|
+
console.log('MongoDB connected successfully');
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.error('MongoDB connection error:', error);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
await connectDB();
|
|
165
|
+
|
|
166
|
+
// Use auth routes
|
|
167
|
+
app.use('/api/auth', authRoutes(mongoose));
|
|
168
|
+
|
|
169
|
+
// Health check
|
|
170
|
+
app.get('/health', (req, res) => {
|
|
171
|
+
res.json({ status: 'ok' });
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const PORT = process.env.PORT || 3000;
|
|
175
|
+
app.listen(PORT, () => {
|
|
176
|
+
console.log(`Server running on port ${PORT}`);
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Using with Custom Routes
|
|
181
|
+
|
|
182
|
+
```javascript
|
|
183
|
+
import { authRoutes } from 'webortex-auth';
|
|
184
|
+
|
|
185
|
+
// Mount on different path
|
|
186
|
+
app.use('/auth', authRoutes(mongoose));
|
|
187
|
+
|
|
188
|
+
// Or with route prefix
|
|
189
|
+
app.use('/v1/authentication', authRoutes(mongoose));
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Testing with cURL
|
|
193
|
+
|
|
194
|
+
**Signup:**
|
|
195
|
+
```bash
|
|
196
|
+
curl -X POST http://localhost:3000/api/auth/signup \
|
|
197
|
+
-H "Content-Type: application/json" \
|
|
198
|
+
-d '{
|
|
199
|
+
"name": "John Doe",
|
|
200
|
+
"email": "john@example.com",
|
|
201
|
+
"password": "securePassword123"
|
|
202
|
+
}'
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Login:**
|
|
206
|
+
```bash
|
|
207
|
+
curl -X POST http://localhost:3000/api/auth/login \
|
|
208
|
+
-H "Content-Type: application/json" \
|
|
209
|
+
-d '{
|
|
210
|
+
"email": "john@example.com",
|
|
211
|
+
"password": "securePassword123"
|
|
212
|
+
}'
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## 🔍 Advanced Usage
|
|
216
|
+
|
|
217
|
+
### Accessing User Model
|
|
218
|
+
|
|
219
|
+
If you need direct access to the User model:
|
|
220
|
+
|
|
221
|
+
```javascript
|
|
222
|
+
import { createUserModel } from 'webortex-auth';
|
|
223
|
+
|
|
224
|
+
const User = createUserModel(mongoose);
|
|
225
|
+
|
|
226
|
+
// Now you can use the User model
|
|
227
|
+
const users = await User.find();
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Custom Middleware
|
|
231
|
+
|
|
232
|
+
Add middleware before auth routes:
|
|
233
|
+
|
|
234
|
+
```javascript
|
|
235
|
+
import rateLimit from 'express-rate-limit';
|
|
236
|
+
|
|
237
|
+
const limiter = rateLimit({
|
|
238
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
239
|
+
max: 5 // limit each IP to 5 requests per windowMs
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
app.use('/api/auth/login', limiter);
|
|
243
|
+
app.use('/api/auth', authRoutes(mongoose));
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## ⚠️ Common Errors & Fixes
|
|
247
|
+
|
|
248
|
+
### Error: `buffering timed out after 10000ms`
|
|
249
|
+
|
|
250
|
+
**Cause:** Mongoose is not connected before using the package.
|
|
251
|
+
|
|
252
|
+
**Fix:** Ensure you connect to MongoDB before using `authRoutes`:
|
|
253
|
+
|
|
254
|
+
```javascript
|
|
255
|
+
// ✅ Correct
|
|
256
|
+
await mongoose.connect(process.env.MONGO_URI);
|
|
257
|
+
app.use('/api/auth', authRoutes(mongoose));
|
|
258
|
+
|
|
259
|
+
// ❌ Wrong
|
|
260
|
+
app.use('/api/auth', authRoutes(mongoose));
|
|
261
|
+
await mongoose.connect(process.env.MONGO_URI);
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
### Error: `JWT_SECRET is not defined`
|
|
267
|
+
|
|
268
|
+
**Cause:** Missing `JWT_SECRET` environment variable.
|
|
269
|
+
|
|
270
|
+
**Fix:** Add to your `.env` file:
|
|
271
|
+
|
|
272
|
+
```env
|
|
273
|
+
JWT_SECRET=your-secret-key-here
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
### Error: `E11000 duplicate key error`
|
|
279
|
+
|
|
280
|
+
**Cause:** User with the email already exists.
|
|
281
|
+
|
|
282
|
+
**Fix:** This is expected behavior. The API returns a 409 status code. Handle it in your client:
|
|
283
|
+
|
|
284
|
+
```javascript
|
|
285
|
+
const response = await fetch('/api/auth/signup', {
|
|
286
|
+
method: 'POST',
|
|
287
|
+
body: JSON.stringify(userData)
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
if (response.status === 409) {
|
|
291
|
+
console.log('Email already registered');
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
### Error: `Cannot find module 'mongoose'`
|
|
298
|
+
|
|
299
|
+
**Cause:** Mongoose is a peer dependency and must be installed separately.
|
|
300
|
+
|
|
301
|
+
**Fix:**
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
npm install mongoose
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## 🏗️ Package Structure
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
webortex-auth/
|
|
311
|
+
├── src/
|
|
312
|
+
│ ├── controllers/
|
|
313
|
+
│ │ └── auth.controller.js # Signup & Login logic
|
|
314
|
+
│ ├── models/
|
|
315
|
+
│ │ └── User.model.js # User schema with password hashing
|
|
316
|
+
│ ├── routes/
|
|
317
|
+
│ │ └── auth.routes.js # Express routes
|
|
318
|
+
│ └── index.js # Public API exports
|
|
319
|
+
├── package.json
|
|
320
|
+
├── README.md
|
|
321
|
+
└── .npmignore
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## 🛡️ Security Best Practices
|
|
325
|
+
|
|
326
|
+
1. **Never commit `.env` files** - Add to `.gitignore`
|
|
327
|
+
2. **Use strong JWT secrets** - Generate with: `node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"`
|
|
328
|
+
3. **Enable HTTPS in production** - Use SSL/TLS certificates
|
|
329
|
+
4. **Add rate limiting** - Prevent brute force attacks
|
|
330
|
+
5. **Validate input** - Consider adding validation middleware
|
|
331
|
+
6. **Use environment variables** - Never hardcode secrets
|
|
332
|
+
|
|
333
|
+
## 📝 User Schema
|
|
334
|
+
|
|
335
|
+
The package creates a User model with the following schema:
|
|
336
|
+
|
|
337
|
+
```javascript
|
|
338
|
+
{
|
|
339
|
+
name: String, // Required, trimmed
|
|
340
|
+
email: String, // Required, unique, lowercase, validated
|
|
341
|
+
password: String, // Required, hashed with bcrypt, min 6 chars
|
|
342
|
+
createdAt: Date, // Auto-generated
|
|
343
|
+
updatedAt: Date // Auto-generated
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## 🔄 How It Works
|
|
348
|
+
|
|
349
|
+
1. **Dependency Injection**: The package receives a connected mongoose instance from your app
|
|
350
|
+
2. **Model Creation**: User model is created using the injected mongoose instance
|
|
351
|
+
3. **No Internal Connections**: Package never calls `mongoose.connect()`
|
|
352
|
+
4. **Factory Pattern**: All exports are factory functions that accept mongoose
|
|
353
|
+
5. **Single Instance**: Prevents multiple mongoose instances and buffering issues
|
|
354
|
+
|
|
355
|
+
## 🚀 Why Dependency Injection?
|
|
356
|
+
|
|
357
|
+
**Traditional packages** create their own mongoose connection:
|
|
358
|
+
```javascript
|
|
359
|
+
// ❌ Creates separate connection
|
|
360
|
+
import auth from 'some-auth-package';
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
**This package** uses your existing connection:
|
|
364
|
+
```javascript
|
|
365
|
+
// ✅ Uses your connection
|
|
366
|
+
import { authRoutes } from 'webortex-auth';
|
|
367
|
+
app.use('/api/auth', authRoutes(mongoose));
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**Benefits:**
|
|
371
|
+
- ✅ No buffering timeout errors
|
|
372
|
+
- ✅ No multiple mongoose instances
|
|
373
|
+
- ✅ No peerDependency conflicts
|
|
374
|
+
- ✅ Works in monorepos
|
|
375
|
+
- ✅ Full control over connection
|
|
376
|
+
|
|
377
|
+
## 📄 License
|
|
378
|
+
|
|
379
|
+
MIT
|
|
380
|
+
|
|
381
|
+
## 🤝 Contributing
|
|
382
|
+
|
|
383
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
384
|
+
|
|
385
|
+
## 📧 Support
|
|
386
|
+
|
|
387
|
+
For issues and questions, please open an issue on GitHub.
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
**Made with ❤️ by WebOrtex**
|
package/example-usage.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import mongoose from 'mongoose';
|
|
3
|
+
import dotenv from 'dotenv';
|
|
4
|
+
import { authRoutes } from 'webortex-auth';
|
|
5
|
+
|
|
6
|
+
// Load environment variables
|
|
7
|
+
dotenv.config();
|
|
8
|
+
|
|
9
|
+
const app = express();
|
|
10
|
+
|
|
11
|
+
// Middleware
|
|
12
|
+
app.use(express.json());
|
|
13
|
+
app.use(express.urlencoded({ extended: true }));
|
|
14
|
+
|
|
15
|
+
// Connect to MongoDB
|
|
16
|
+
const connectDB = async () => {
|
|
17
|
+
try {
|
|
18
|
+
await mongoose.connect(process.env.MONGO_URI, {
|
|
19
|
+
useNewUrlParser: true,
|
|
20
|
+
useUnifiedTopology: true,
|
|
21
|
+
});
|
|
22
|
+
console.log('✅ MongoDB connected successfully');
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error('❌ MongoDB connection error:', error.message);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Initialize database connection
|
|
30
|
+
await connectDB();
|
|
31
|
+
|
|
32
|
+
// Health check endpoint
|
|
33
|
+
app.get('/health', (req, res) => {
|
|
34
|
+
res.json({
|
|
35
|
+
status: 'ok',
|
|
36
|
+
timestamp: new Date().toISOString(),
|
|
37
|
+
database: mongoose.connection.readyState === 1 ? 'connected' : 'disconnected',
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Use webortex-auth routes
|
|
42
|
+
// IMPORTANT: Pass the connected mongoose instance
|
|
43
|
+
app.use('/api/auth', authRoutes(mongoose));
|
|
44
|
+
|
|
45
|
+
// 404 handler
|
|
46
|
+
app.use((req, res) => {
|
|
47
|
+
res.status(404).json({
|
|
48
|
+
success: false,
|
|
49
|
+
message: 'Route not found',
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Error handler
|
|
54
|
+
app.use((err, req, res, next) => {
|
|
55
|
+
console.error('Error:', err);
|
|
56
|
+
res.status(500).json({
|
|
57
|
+
success: false,
|
|
58
|
+
message: 'Internal server error',
|
|
59
|
+
error: process.env.NODE_ENV === 'development' ? err.message : undefined,
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Start server
|
|
64
|
+
const PORT = process.env.PORT || 3000;
|
|
65
|
+
app.listen(PORT, () => {
|
|
66
|
+
console.log(`🚀 Server running on http://localhost:${PORT}`);
|
|
67
|
+
console.log(`📡 Auth endpoints available at http://localhost:${PORT}/api/auth`);
|
|
68
|
+
console.log(` - POST /api/auth/signup`);
|
|
69
|
+
console.log(` - POST /api/auth/login`);
|
|
70
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "webortex-auth",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Reusable authentication package for Express + MongoDB with dependency injection",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"authentication",
|
|
15
|
+
"express",
|
|
16
|
+
"mongoose",
|
|
17
|
+
"jwt",
|
|
18
|
+
"auth",
|
|
19
|
+
"login",
|
|
20
|
+
"signup",
|
|
21
|
+
"mongodb"
|
|
22
|
+
],
|
|
23
|
+
"author": "WebOrtex",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"express": "^4.18.0",
|
|
27
|
+
"mongoose": "^7.0.0 || ^8.0.0"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"bcryptjs": "^2.4.3",
|
|
31
|
+
"jsonwebtoken": "^9.0.2"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=16.0.0",
|
|
35
|
+
"npm": ">=9.0.0"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import jwt from 'jsonwebtoken';
|
|
2
|
+
import { createUserModel } from '../models/User.model.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creates authentication controllers with injected mongoose instance
|
|
6
|
+
* @param {Object} mongoose - Connected mongoose instance from consuming app
|
|
7
|
+
* @returns {Object} Authentication controllers (signup, login)
|
|
8
|
+
*/
|
|
9
|
+
export const createAuthController = (mongoose) => {
|
|
10
|
+
const User = createUserModel(mongoose);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Signup Controller
|
|
14
|
+
* Creates a new user account
|
|
15
|
+
*/
|
|
16
|
+
const signup = async (req, res) => {
|
|
17
|
+
try {
|
|
18
|
+
const { name, email, password } = req.body;
|
|
19
|
+
|
|
20
|
+
// Validate required fields
|
|
21
|
+
if (!name || !email || !password) {
|
|
22
|
+
return res.status(400).json({
|
|
23
|
+
success: false,
|
|
24
|
+
message: 'Please provide name, email, and password',
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Check if user already exists
|
|
29
|
+
const existingUser = await User.findOne({ email });
|
|
30
|
+
if (existingUser) {
|
|
31
|
+
return res.status(409).json({
|
|
32
|
+
success: false,
|
|
33
|
+
message: 'User with this email already exists',
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Create new user (password will be hashed by pre-save hook)
|
|
38
|
+
const user = await User.create({
|
|
39
|
+
name,
|
|
40
|
+
email,
|
|
41
|
+
password,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Return success response without password
|
|
45
|
+
return res.status(201).json({
|
|
46
|
+
success: true,
|
|
47
|
+
message: 'User registered successfully',
|
|
48
|
+
data: {
|
|
49
|
+
user: {
|
|
50
|
+
id: user._id,
|
|
51
|
+
name: user.name,
|
|
52
|
+
email: user.email,
|
|
53
|
+
createdAt: user.createdAt,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
} catch (error) {
|
|
58
|
+
// Handle duplicate key error (E11000)
|
|
59
|
+
if (error.code === 11000) {
|
|
60
|
+
return res.status(409).json({
|
|
61
|
+
success: false,
|
|
62
|
+
message: 'User with this email already exists',
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Handle validation errors
|
|
67
|
+
if (error.name === 'ValidationError') {
|
|
68
|
+
const messages = Object.values(error.errors).map((err) => err.message);
|
|
69
|
+
return res.status(400).json({
|
|
70
|
+
success: false,
|
|
71
|
+
message: messages.join(', '),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Generic error
|
|
76
|
+
return res.status(500).json({
|
|
77
|
+
success: false,
|
|
78
|
+
message: 'Error creating user',
|
|
79
|
+
error: process.env.NODE_ENV === 'development' ? error.message : undefined,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Login Controller
|
|
86
|
+
* Authenticates user and returns JWT token
|
|
87
|
+
*/
|
|
88
|
+
const login = async (req, res) => {
|
|
89
|
+
try {
|
|
90
|
+
const { email, password } = req.body;
|
|
91
|
+
|
|
92
|
+
// Validate required fields
|
|
93
|
+
if (!email || !password) {
|
|
94
|
+
return res.status(400).json({
|
|
95
|
+
success: false,
|
|
96
|
+
message: 'Please provide email and password',
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Find user and include password field
|
|
101
|
+
const user = await User.findOne({ email }).select('+password');
|
|
102
|
+
if (!user) {
|
|
103
|
+
return res.status(401).json({
|
|
104
|
+
success: false,
|
|
105
|
+
message: 'Invalid email or password',
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Compare passwords
|
|
110
|
+
const isPasswordValid = await user.comparePassword(password);
|
|
111
|
+
if (!isPasswordValid) {
|
|
112
|
+
return res.status(401).json({
|
|
113
|
+
success: false,
|
|
114
|
+
message: 'Invalid email or password',
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Check if JWT_SECRET is defined
|
|
119
|
+
if (!process.env.JWT_SECRET) {
|
|
120
|
+
throw new Error('JWT_SECRET is not defined in environment variables');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Generate JWT token
|
|
124
|
+
const token = jwt.sign(
|
|
125
|
+
{ userId: user._id, email: user.email },
|
|
126
|
+
process.env.JWT_SECRET,
|
|
127
|
+
{ expiresIn: process.env.JWT_EXPIRES_IN || '24h' }
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
// Return success response with token
|
|
131
|
+
return res.status(200).json({
|
|
132
|
+
success: true,
|
|
133
|
+
message: 'Login successful',
|
|
134
|
+
data: {
|
|
135
|
+
token,
|
|
136
|
+
user: {
|
|
137
|
+
id: user._id,
|
|
138
|
+
name: user.name,
|
|
139
|
+
email: user.email,
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
} catch (error) {
|
|
144
|
+
return res.status(500).json({
|
|
145
|
+
success: false,
|
|
146
|
+
message: 'Error during login',
|
|
147
|
+
error: process.env.NODE_ENV === 'development' ? error.message : undefined,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
signup,
|
|
154
|
+
login,
|
|
155
|
+
};
|
|
156
|
+
};
|
package/src/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* webortex-auth - Reusable authentication package
|
|
3
|
+
*
|
|
4
|
+
* Main entry point for the package
|
|
5
|
+
* Exports factory functions that accept injected mongoose instance
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { createAuthRoutes as authRoutes } from './routes/auth.routes.js';
|
|
9
|
+
export { createUserModel } from './models/User.model.js';
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import bcrypt from 'bcryptjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates User model with injected mongoose instance
|
|
5
|
+
* @param {Object} mongoose - Connected mongoose instance from consuming app
|
|
6
|
+
* @returns {Model} User model
|
|
7
|
+
*/
|
|
8
|
+
export const createUserModel = (mongoose) => {
|
|
9
|
+
// Prevent model recompilation error
|
|
10
|
+
if (mongoose.models.User) {
|
|
11
|
+
return mongoose.models.User;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const userSchema = new mongoose.Schema(
|
|
15
|
+
{
|
|
16
|
+
name: {
|
|
17
|
+
type: String,
|
|
18
|
+
required: [true, 'Name is required'],
|
|
19
|
+
trim: true,
|
|
20
|
+
},
|
|
21
|
+
email: {
|
|
22
|
+
type: String,
|
|
23
|
+
required: [true, 'Email is required'],
|
|
24
|
+
unique: true,
|
|
25
|
+
lowercase: true,
|
|
26
|
+
trim: true,
|
|
27
|
+
match: [
|
|
28
|
+
/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/,
|
|
29
|
+
'Please provide a valid email',
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
password: {
|
|
33
|
+
type: String,
|
|
34
|
+
required: [true, 'Password is required'],
|
|
35
|
+
minlength: [6, 'Password must be at least 6 characters'],
|
|
36
|
+
select: false, // Don't return password by default
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
timestamps: true,
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// Hash password before saving
|
|
45
|
+
userSchema.pre('save', async function (next) {
|
|
46
|
+
// Only hash if password is modified
|
|
47
|
+
if (!this.isModified('password')) {
|
|
48
|
+
return next();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const salt = await bcrypt.genSalt(10);
|
|
53
|
+
this.password = await bcrypt.hash(this.password, salt);
|
|
54
|
+
next();
|
|
55
|
+
} catch (error) {
|
|
56
|
+
next(error);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Method to compare passwords
|
|
61
|
+
userSchema.methods.comparePassword = async function (candidatePassword) {
|
|
62
|
+
try {
|
|
63
|
+
return await bcrypt.compare(candidatePassword, this.password);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
throw new Error('Password comparison failed');
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return mongoose.model('User', userSchema);
|
|
70
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { createAuthController } from '../controllers/auth.controller.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creates authentication routes with injected mongoose instance
|
|
6
|
+
* @param {Object} mongoose - Connected mongoose instance from consuming app
|
|
7
|
+
* @returns {Router} Express router with auth routes
|
|
8
|
+
*/
|
|
9
|
+
export const createAuthRoutes = (mongoose) => {
|
|
10
|
+
const router = express.Router();
|
|
11
|
+
const { signup, login } = createAuthController(mongoose);
|
|
12
|
+
|
|
13
|
+
// POST /signup - Register new user
|
|
14
|
+
router.post('/signup', signup);
|
|
15
|
+
|
|
16
|
+
// POST /login - Authenticate user
|
|
17
|
+
router.post('/login', login);
|
|
18
|
+
|
|
19
|
+
return router;
|
|
20
|
+
};
|
|
Binary file
|