@dax-crafta/auth 1.2.4 → 1.2.6

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/package.json +9 -1
  2. package/src/index.js +165 -145
package/package.json CHANGED
@@ -1,13 +1,21 @@
1
1
  {
2
2
  "name": "@dax-crafta/auth",
3
- "version": "1.2.4",
3
+ "version": "1.2.6",
4
4
  "description": "Authentication plugin for crafta framework",
5
5
  "main": "src/index.js",
6
6
  "private": false,
7
7
  "dependencies": {
8
8
  "bcryptjs": "^2.4.3",
9
+ "express-rate-limit": "^7.1.4",
10
+ "google-auth-library": "^9.2.0",
9
11
  "jsonwebtoken": "^9.0.2",
10
12
  "mongoose": "^7.6.3",
13
+ "nodemailer": "^6.9.7",
14
+ "otplib": "^12.0.1",
15
+ "passport": "^0.6.0",
16
+ "passport-facebook": "^3.0.0",
17
+ "passport-github2": "^0.1.12",
18
+ "passport-google-oauth20": "^2.0.0",
11
19
  "validator": "^13.11.0"
12
20
  },
13
21
  "peerDependencies": {
package/src/index.js CHANGED
@@ -1,204 +1,224 @@
1
- const jwt = require('jsonwebtoken');
2
- const bcrypt = require('bcryptjs');
3
- const mongoose = require('mongoose');
4
- const validator = require('validator');
1
+ const passport = require('passport');
2
+ const GoogleStrategy = require('passport-google-oauth20').Strategy;
3
+ const FacebookStrategy = require('passport-facebook').Strategy;
4
+ const GitHubStrategy = require('passport-github2').Strategy;
5
+ const AuthService = require('./services/auth.service');
6
+ const createAuthMiddleware = require('./middlewares/auth.middleware');
5
7
 
6
- // Default configuration
7
8
  const defaultConfig = {
8
9
  strategy: 'jwt',
9
10
  fields: ['email', 'password'],
10
11
  routes: {
11
12
  register: '/register',
12
13
  login: '/login',
13
- verify: '/verify'
14
+ verify: '/verify',
15
+ forgotPassword: '/forgot-password',
16
+ resetPassword: '/reset-password',
17
+ refreshToken: '/refresh-token',
18
+ profile: '/profile',
19
+ twoFactor: '/2fa'
14
20
  },
15
21
  mongoUrl: process.env.MONGODB_URI || 'mongodb://localhost:27017/forge-auth',
16
- sendVerificationEmail: null,
17
- db: null
22
+ maxLoginAttempts: 5,
23
+ emailVerification: true,
24
+ loginAlerts: true,
25
+ smtp: null,
26
+ social: {
27
+ google: null,
28
+ facebook: null,
29
+ github: null
30
+ }
18
31
  };
19
32
 
20
- // Create dynamic Mongoose schema based on fields
21
- function createUserSchema(fields) {
22
- const schemaFields = fields.reduce((acc, field) => {
23
- acc[field] = {
24
- type: String,
25
- required: true,
26
- ...(field === 'email' && {
27
- validate: {
28
- validator: validator.isEmail,
29
- message: 'Invalid email format'
30
- }
31
- })
32
- };
33
- return acc;
34
- }, {});
35
-
36
- return new mongoose.Schema(schemaFields, { timestamps: true });
37
- }
38
-
39
33
  function auth(config = {}) {
40
34
  const finalConfig = { ...defaultConfig, ...config };
41
35
 
42
- // Validate JWT secret
43
36
  if (!finalConfig.env?.JWT_SECRET) {
44
37
  throw new Error('JWT_SECRET is required in environment configuration');
45
38
  }
46
39
 
47
- // Initialize MongoDB connection if custom DB handlers aren't provided
48
- let User;
49
- if (!finalConfig.db) {
50
- mongoose.connect(finalConfig.mongoUrl, {
51
- useNewUrlParser: true,
52
- useUnifiedTopology: true
53
- });
54
-
55
- const userSchema = createUserSchema(finalConfig.fields);
56
- User = mongoose.model('User', userSchema);
40
+ if (finalConfig.emailVerification && !finalConfig.smtp) {
41
+ throw new Error('SMTP configuration is required for email verification');
57
42
  }
58
43
 
59
- // Database operations
60
- const dbOperations = finalConfig.db || {
61
- saveUser: async (userData) => {
62
- const user = new User(userData);
63
- return await user.save();
64
- },
65
- getUserByEmail: async (email) => {
66
- return await User.findOne({ email });
67
- }
68
- };
44
+ const authService = new AuthService(finalConfig);
45
+ const { rateLimiter, verifyToken, checkRole } = createAuthMiddleware(finalConfig);
46
+
47
+ // Configure social login strategies
48
+ if (finalConfig.social.google) {
49
+ passport.use(new GoogleStrategy(finalConfig.social.google,
50
+ async (accessToken, refreshToken, profile, done) => {
51
+ try {
52
+ // Handle Google login
53
+ const user = await authService.handleSocialLogin('google', profile);
54
+ done(null, user);
55
+ } catch (error) {
56
+ done(error);
57
+ }
58
+ }
59
+ ));
60
+ }
69
61
 
70
62
  return function(app) {
63
+ app.use(passport.initialize());
64
+ app.use(rateLimiter);
65
+
71
66
  // Register route
72
67
  app.post(finalConfig.routes.register, async (req, res) => {
73
68
  try {
74
- // Validate required fields
75
- const userData = {};
76
- for (const field of finalConfig.fields) {
77
- if (!req.body[field]) {
78
- return res.status(400).json({
79
- success: false,
80
- error: `${field} is required`
81
- });
82
- }
83
- userData[field] = req.body[field];
84
- }
85
-
86
- // Hash password
87
- if (userData.password) {
88
- userData.password = await bcrypt.hash(userData.password, 10);
89
- }
90
-
91
- // Save user
92
- const savedUser = await dbOperations.saveUser(userData);
69
+ const user = await authService.register(req.body);
70
+ res.status(201).json({
71
+ success: true,
72
+ message: 'Registration successful. Please verify your email.'
73
+ });
74
+ } catch (error) {
75
+ res.status(400).json({
76
+ success: false,
77
+ error: error.message
78
+ });
79
+ }
80
+ });
93
81
 
94
- // Generate JWT
95
- const token = jwt.sign(
96
- { id: savedUser._id, email: savedUser.email },
97
- finalConfig.env.JWT_SECRET,
98
- { expiresIn: '24h' }
82
+ // Login route with device detection
83
+ app.post(finalConfig.routes.login, async (req, res) => {
84
+ try {
85
+ const deviceInfo = {
86
+ browser: req.headers['user-agent'],
87
+ ip: req.ip
88
+ };
89
+
90
+ const result = await authService.login(
91
+ req.body.email,
92
+ req.body.password,
93
+ deviceInfo
99
94
  );
100
95
 
101
- // Send verification email if configured
102
- if (finalConfig.sendVerificationEmail) {
103
- try {
104
- await finalConfig.sendVerificationEmail(savedUser);
105
- } catch (emailError) {
106
- console.error('Email verification failed:', emailError);
107
- }
108
- }
109
-
110
- res.status(201).json({
96
+ res.json({
111
97
  success: true,
112
- token,
113
- user: { ...savedUser.toJSON(), password: undefined }
98
+ ...result
114
99
  });
115
100
  } catch (error) {
116
- if (error.code === 11000) {
117
- return res.status(400).json({
118
- success: false,
119
- error: 'Email already exists'
120
- });
121
- }
122
- res.status(400).json({
123
- success: false,
124
- error: error.message
101
+ res.status(401).json({
102
+ success: false,
103
+ error: error.message
125
104
  });
126
105
  }
127
106
  });
128
107
 
129
- // Login route
130
- app.post(finalConfig.routes.login, async (req, res) => {
108
+ // 2FA verification
109
+ app.post(finalConfig.routes.twoFactor, async (req, res) => {
131
110
  try {
132
- const { email, password } = req.body;
133
-
134
- if (!email || !password) {
135
- return res.status(400).json({
136
- success: false,
137
- error: 'Email and password are required'
138
- });
139
- }
140
-
141
- const user = await dbOperations.getUserByEmail(email);
142
-
143
- if (!user) {
144
- return res.status(404).json({
145
- success: false,
146
- error: 'User not found'
147
- });
148
- }
111
+ const result = await authService.verify2FA(
112
+ req.body.userId,
113
+ req.body.code
114
+ );
149
115
 
150
- const isValid = await bcrypt.compare(password, user.password);
151
-
152
- if (!isValid) {
153
- return res.status(401).json({
154
- success: false,
155
- error: 'Invalid credentials'
156
- });
157
- }
116
+ res.json({
117
+ success: true,
118
+ ...result
119
+ });
120
+ } catch (error) {
121
+ res.status(401).json({
122
+ success: false,
123
+ error: error.message
124
+ });
125
+ }
126
+ });
158
127
 
159
- const token = jwt.sign(
160
- { id: user._id, email: user.email },
161
- finalConfig.env.JWT_SECRET,
162
- { expiresIn: '24h' }
163
- );
128
+ // Refresh token
129
+ app.post(finalConfig.routes.refreshToken, async (req, res) => {
130
+ try {
131
+ const result = await authService.refreshToken(req.body.refreshToken);
132
+ res.json({
133
+ success: true,
134
+ ...result
135
+ });
136
+ } catch (error) {
137
+ res.status(401).json({
138
+ success: false,
139
+ error: error.message
140
+ });
141
+ }
142
+ });
164
143
 
165
- res.json({
144
+ // Forgot password
145
+ app.post(finalConfig.routes.forgotPassword, async (req, res) => {
146
+ try {
147
+ await authService.forgotPassword(req.body.email);
148
+ res.json({
166
149
  success: true,
167
- token,
168
- user: { ...user.toJSON(), password: undefined }
150
+ message: 'Password reset instructions sent to your email'
169
151
  });
170
152
  } catch (error) {
171
- res.status(400).json({
172
- success: false,
173
- error: error.message
153
+ res.status(400).json({
154
+ success: false,
155
+ error: error.message
174
156
  });
175
157
  }
176
158
  });
177
159
 
178
- // Verify route
179
- app.get(finalConfig.routes.verify, (req, res) => {
180
- const token = req.headers.authorization?.split(' ')[1];
181
-
182
- if (!token) {
183
- return res.status(401).json({
184
- success: false,
185
- error: 'No token provided'
160
+ // Reset password
161
+ app.post(finalConfig.routes.resetPassword, async (req, res) => {
162
+ try {
163
+ await authService.resetPassword(
164
+ req.body.token,
165
+ req.body.newPassword
166
+ );
167
+ res.json({
168
+ success: true,
169
+ message: 'Password reset successful'
170
+ });
171
+ } catch (error) {
172
+ res.status(400).json({
173
+ success: false,
174
+ error: error.message
186
175
  });
187
176
  }
177
+ });
188
178
 
179
+ // Update profile (protected route)
180
+ app.put(finalConfig.routes.profile, verifyToken, async (req, res) => {
189
181
  try {
190
- const decoded = jwt.verify(token, finalConfig.env.JWT_SECRET);
191
- res.json({
182
+ const user = await authService.updateProfile(req.user.id, req.body);
183
+ res.json({
192
184
  success: true,
193
- user: decoded
185
+ user
194
186
  });
195
187
  } catch (error) {
196
- res.status(401).json({
197
- success: false,
198
- error: 'Invalid token'
188
+ res.status(400).json({
189
+ success: false,
190
+ error: error.message
199
191
  });
200
192
  }
201
193
  });
194
+
195
+ // Social login routes
196
+ if (finalConfig.social.google) {
197
+ app.get('/auth/google',
198
+ passport.authenticate('google', { scope: ['profile', 'email'] })
199
+ );
200
+
201
+ app.get('/auth/google/callback',
202
+ passport.authenticate('google', { session: false }),
203
+ (req, res) => {
204
+ const { accessToken, refreshToken } = authService.generateTokens(req.user);
205
+ res.json({
206
+ success: true,
207
+ accessToken,
208
+ refreshToken,
209
+ user: req.user
210
+ });
211
+ }
212
+ );
213
+ }
214
+
215
+ // Admin-only route example
216
+ app.get('/admin', verifyToken, checkRole(['admin']), (req, res) => {
217
+ res.json({
218
+ success: true,
219
+ message: 'Admin access granted'
220
+ });
221
+ });
202
222
  };
203
223
  }
204
224