@bhandari88/express-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/INSTALLATION.md +71 -0
- package/README.md +425 -0
- package/dist/handlers/index.d.ts +2 -0
- package/dist/handlers/index.js +7 -0
- package/dist/handlers/local-auth.d.ts +12 -0
- package/dist/handlers/local-auth.js +182 -0
- package/dist/handlers/social-auth.d.ts +15 -0
- package/dist/handlers/social-auth.js +164 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.js +232 -0
- package/dist/middleware/auth.middleware.d.ts +11 -0
- package/dist/middleware/auth.middleware.js +102 -0
- package/dist/types/index.d.ts +84 -0
- package/dist/types/index.js +2 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/jwt.d.ts +15 -0
- package/dist/utils/jwt.js +120 -0
- package/dist/utils/password.d.ts +14 -0
- package/dist/utils/password.js +103 -0
- package/dist/utils/validator.d.ts +25 -0
- package/dist/utils/validator.js +63 -0
- package/package.json +60 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { UserRepository, AuthResult, AuthConfig } from '../types';
|
|
2
|
+
import { JwtService } from '../utils/jwt';
|
|
3
|
+
import passport from 'passport';
|
|
4
|
+
export declare class SocialAuthHandler {
|
|
5
|
+
private userRepository;
|
|
6
|
+
private jwtService;
|
|
7
|
+
constructor(userRepository: UserRepository, jwtService: JwtService, config: AuthConfig);
|
|
8
|
+
private initializePassport;
|
|
9
|
+
handleSocialLogin(provider: 'google' | 'facebook', providerId: string, profile: {
|
|
10
|
+
email?: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
photo?: string;
|
|
13
|
+
}): Promise<AuthResult>;
|
|
14
|
+
getPassport(): typeof passport;
|
|
15
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SocialAuthHandler = void 0;
|
|
7
|
+
const validator_1 = require("../utils/validator");
|
|
8
|
+
const passport_1 = __importDefault(require("passport"));
|
|
9
|
+
const passport_google_oauth20_1 = require("passport-google-oauth20");
|
|
10
|
+
const passport_facebook_1 = require("passport-facebook");
|
|
11
|
+
class SocialAuthHandler {
|
|
12
|
+
constructor(userRepository, jwtService, config) {
|
|
13
|
+
this.userRepository = userRepository;
|
|
14
|
+
this.jwtService = jwtService;
|
|
15
|
+
this.initializePassport(config);
|
|
16
|
+
}
|
|
17
|
+
initializePassport(config) {
|
|
18
|
+
// Serialize user (minimal implementation for Passport compatibility)
|
|
19
|
+
passport_1.default.serializeUser((user, done) => {
|
|
20
|
+
done(null, user);
|
|
21
|
+
});
|
|
22
|
+
passport_1.default.deserializeUser((user, done) => {
|
|
23
|
+
done(null, user);
|
|
24
|
+
});
|
|
25
|
+
// Google Strategy
|
|
26
|
+
if (config.socialAuth?.google) {
|
|
27
|
+
passport_1.default.use(new passport_google_oauth20_1.Strategy({
|
|
28
|
+
clientID: config.socialAuth.google.clientID,
|
|
29
|
+
clientSecret: config.socialAuth.google.clientSecret,
|
|
30
|
+
callbackURL: config.socialAuth.google.callbackURL,
|
|
31
|
+
}, async (accessToken, refreshToken, profile, done) => {
|
|
32
|
+
try {
|
|
33
|
+
const result = await this.handleSocialLogin('google', profile.id, {
|
|
34
|
+
email: profile.emails?.[0]?.value,
|
|
35
|
+
name: profile.displayName,
|
|
36
|
+
photo: profile.photos?.[0]?.value,
|
|
37
|
+
});
|
|
38
|
+
if (result.success && result.user) {
|
|
39
|
+
done(null, result.user);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
done(new Error(result.error || 'Google authentication failed'), false);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
done(error, false);
|
|
47
|
+
}
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
// Facebook Strategy
|
|
51
|
+
if (config.socialAuth?.facebook) {
|
|
52
|
+
passport_1.default.use(new passport_facebook_1.Strategy({
|
|
53
|
+
clientID: config.socialAuth.facebook.clientID,
|
|
54
|
+
clientSecret: config.socialAuth.facebook.clientSecret,
|
|
55
|
+
callbackURL: config.socialAuth.facebook.callbackURL,
|
|
56
|
+
profileFields: ['id', 'displayName', 'email', 'picture.type(large)'],
|
|
57
|
+
}, async (accessToken, refreshToken, profile, done) => {
|
|
58
|
+
try {
|
|
59
|
+
const result = await this.handleSocialLogin('facebook', profile.id, {
|
|
60
|
+
email: profile.emails?.[0]?.value,
|
|
61
|
+
name: profile.displayName,
|
|
62
|
+
photo: profile.photos?.[0]?.value,
|
|
63
|
+
});
|
|
64
|
+
if (result.success && result.user) {
|
|
65
|
+
done(null, result.user);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
done(new Error(result.error || 'Facebook authentication failed'), false);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
done(error, false);
|
|
73
|
+
}
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async handleSocialLogin(provider, providerId, profile) {
|
|
78
|
+
try {
|
|
79
|
+
// Find existing user by provider
|
|
80
|
+
let user = await this.userRepository.findByProvider(provider, providerId);
|
|
81
|
+
if (user) {
|
|
82
|
+
// User exists, generate tokens
|
|
83
|
+
const tokens = this.jwtService.generateTokens({
|
|
84
|
+
userId: user.id || user._id || '',
|
|
85
|
+
email: user.email,
|
|
86
|
+
username: user.username,
|
|
87
|
+
phone: user.phone,
|
|
88
|
+
});
|
|
89
|
+
const { password: _, ...userWithoutPassword } = user;
|
|
90
|
+
return {
|
|
91
|
+
success: true,
|
|
92
|
+
user: userWithoutPassword,
|
|
93
|
+
tokens,
|
|
94
|
+
message: 'Login successful',
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// Check if user exists by email
|
|
98
|
+
if (profile.email) {
|
|
99
|
+
const sanitizedEmail = validator_1.ValidatorService.sanitizeEmail(profile.email);
|
|
100
|
+
user = await this.userRepository.findByEmail(sanitizedEmail);
|
|
101
|
+
if (user) {
|
|
102
|
+
// Link social account to existing user
|
|
103
|
+
await this.userRepository.update(user.id || user._id || '', {
|
|
104
|
+
provider,
|
|
105
|
+
providerId,
|
|
106
|
+
verified: true,
|
|
107
|
+
});
|
|
108
|
+
const tokens = this.jwtService.generateTokens({
|
|
109
|
+
userId: user.id || user._id || '',
|
|
110
|
+
email: user.email,
|
|
111
|
+
username: user.username,
|
|
112
|
+
phone: user.phone,
|
|
113
|
+
});
|
|
114
|
+
const { password: _, ...userWithoutPassword } = user;
|
|
115
|
+
return {
|
|
116
|
+
success: true,
|
|
117
|
+
user: userWithoutPassword,
|
|
118
|
+
tokens,
|
|
119
|
+
message: 'Account linked successfully',
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Create new user
|
|
124
|
+
const userData = {
|
|
125
|
+
provider,
|
|
126
|
+
providerId,
|
|
127
|
+
verified: true,
|
|
128
|
+
};
|
|
129
|
+
if (profile.email) {
|
|
130
|
+
userData.email = validator_1.ValidatorService.sanitizeEmail(profile.email);
|
|
131
|
+
}
|
|
132
|
+
if (profile.name) {
|
|
133
|
+
userData.name = profile.name;
|
|
134
|
+
}
|
|
135
|
+
if (profile.photo) {
|
|
136
|
+
userData.photo = profile.photo;
|
|
137
|
+
}
|
|
138
|
+
user = await this.userRepository.create(userData);
|
|
139
|
+
const tokens = this.jwtService.generateTokens({
|
|
140
|
+
userId: user.id || user._id || '',
|
|
141
|
+
email: user.email,
|
|
142
|
+
username: user.username,
|
|
143
|
+
phone: user.phone,
|
|
144
|
+
});
|
|
145
|
+
const { password: _, ...userWithoutPassword } = user;
|
|
146
|
+
return {
|
|
147
|
+
success: true,
|
|
148
|
+
user: userWithoutPassword,
|
|
149
|
+
tokens,
|
|
150
|
+
message: 'Account created and logged in successfully',
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
return {
|
|
155
|
+
success: false,
|
|
156
|
+
error: error instanceof Error ? error.message : 'Social login failed',
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
getPassport() {
|
|
161
|
+
return passport_1.default;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
exports.SocialAuthHandler = SocialAuthHandler;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Express } from 'express';
|
|
2
|
+
import { AuthConfig, UserRepository, LoginCredentials, RegisterData, AuthResult } from './types';
|
|
3
|
+
import { JwtService } from './utils/jwt';
|
|
4
|
+
import { PasswordService } from './utils/password';
|
|
5
|
+
export declare class Auth {
|
|
6
|
+
private config;
|
|
7
|
+
private userRepository;
|
|
8
|
+
private jwtService;
|
|
9
|
+
private passwordService;
|
|
10
|
+
private localAuthHandler;
|
|
11
|
+
private socialAuthHandler?;
|
|
12
|
+
private middleware;
|
|
13
|
+
constructor(config: AuthConfig, userRepository: UserRepository);
|
|
14
|
+
/**
|
|
15
|
+
* Register a new user with email, username, or phone
|
|
16
|
+
*/
|
|
17
|
+
register(data: RegisterData): Promise<AuthResult>;
|
|
18
|
+
/**
|
|
19
|
+
* Login with email, username, or phone and password
|
|
20
|
+
*/
|
|
21
|
+
login(credentials: LoginCredentials): Promise<AuthResult>;
|
|
22
|
+
/**
|
|
23
|
+
* Change password for authenticated user
|
|
24
|
+
*/
|
|
25
|
+
changePassword(userId: string, oldPassword: string, newPassword: string): Promise<AuthResult>;
|
|
26
|
+
/**
|
|
27
|
+
* Refresh access token using refresh token
|
|
28
|
+
*/
|
|
29
|
+
refreshToken(refreshToken: string): Promise<AuthResult>;
|
|
30
|
+
/**
|
|
31
|
+
* Get authentication middleware for protected routes
|
|
32
|
+
*/
|
|
33
|
+
getAuthMiddleware(): import("express").RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
|
|
34
|
+
/**
|
|
35
|
+
* Get optional authentication middleware (doesn't fail if no token)
|
|
36
|
+
*/
|
|
37
|
+
getOptionalAuthMiddleware(): import("express").RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
|
|
38
|
+
/**
|
|
39
|
+
* Get role-based access control middleware
|
|
40
|
+
*/
|
|
41
|
+
requireRole(roles: string[]): import("express").RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
|
|
42
|
+
/**
|
|
43
|
+
* Setup Express routes for authentication
|
|
44
|
+
*/
|
|
45
|
+
setupRoutes(app: Express, basePath?: string): void;
|
|
46
|
+
/**
|
|
47
|
+
* Get the JWT service instance
|
|
48
|
+
*/
|
|
49
|
+
getJwtService(): JwtService;
|
|
50
|
+
/**
|
|
51
|
+
* Get the password service instance
|
|
52
|
+
*/
|
|
53
|
+
getPasswordService(): PasswordService;
|
|
54
|
+
}
|
|
55
|
+
export * from './types';
|
|
56
|
+
export { JwtService } from './utils/jwt';
|
|
57
|
+
export { PasswordService } from './utils/password';
|
|
58
|
+
export { ValidatorService } from './utils/validator';
|
|
59
|
+
export { AuthMiddleware } from './middleware/auth.middleware';
|
|
60
|
+
export { LocalAuthHandler } from './handlers/local-auth';
|
|
61
|
+
export { SocialAuthHandler } from './handlers/social-auth';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.SocialAuthHandler = exports.LocalAuthHandler = exports.AuthMiddleware = exports.ValidatorService = exports.PasswordService = exports.JwtService = exports.Auth = void 0;
|
|
18
|
+
const jwt_1 = require("./utils/jwt");
|
|
19
|
+
const password_1 = require("./utils/password");
|
|
20
|
+
const local_auth_1 = require("./handlers/local-auth");
|
|
21
|
+
const social_auth_1 = require("./handlers/social-auth");
|
|
22
|
+
const auth_middleware_1 = require("./middleware/auth.middleware");
|
|
23
|
+
class Auth {
|
|
24
|
+
constructor(config, userRepository) {
|
|
25
|
+
this.config = config;
|
|
26
|
+
this.userRepository = userRepository;
|
|
27
|
+
// Initialize services
|
|
28
|
+
this.jwtService = new jwt_1.JwtService(config);
|
|
29
|
+
this.passwordService = new password_1.PasswordService(config);
|
|
30
|
+
this.localAuthHandler = new local_auth_1.LocalAuthHandler(this.userRepository, this.jwtService, this.passwordService);
|
|
31
|
+
this.middleware = new auth_middleware_1.AuthMiddleware(this.jwtService, this.userRepository);
|
|
32
|
+
// Initialize social auth if configured
|
|
33
|
+
if (config.socialAuth) {
|
|
34
|
+
this.socialAuthHandler = new social_auth_1.SocialAuthHandler(this.userRepository, this.jwtService, config);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Register a new user with email, username, or phone
|
|
39
|
+
*/
|
|
40
|
+
async register(data) {
|
|
41
|
+
return this.localAuthHandler.register(data);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Login with email, username, or phone and password
|
|
45
|
+
*/
|
|
46
|
+
async login(credentials) {
|
|
47
|
+
return this.localAuthHandler.login(credentials);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Change password for authenticated user
|
|
51
|
+
*/
|
|
52
|
+
async changePassword(userId, oldPassword, newPassword) {
|
|
53
|
+
return this.localAuthHandler.changePassword(userId, oldPassword, newPassword);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Refresh access token using refresh token
|
|
57
|
+
*/
|
|
58
|
+
async refreshToken(refreshToken) {
|
|
59
|
+
try {
|
|
60
|
+
const decoded = this.jwtService.verifyRefreshToken(refreshToken);
|
|
61
|
+
const user = await this.userRepository.findById(decoded.userId);
|
|
62
|
+
if (!user) {
|
|
63
|
+
return { success: false, error: 'User not found' };
|
|
64
|
+
}
|
|
65
|
+
const tokens = this.jwtService.generateTokens({
|
|
66
|
+
userId: user.id || user._id || '',
|
|
67
|
+
email: user.email,
|
|
68
|
+
username: user.username,
|
|
69
|
+
phone: user.phone,
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
success: true,
|
|
73
|
+
tokens,
|
|
74
|
+
message: 'Token refreshed successfully',
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
error: error instanceof Error ? error.message : 'Token refresh failed',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Get authentication middleware for protected routes
|
|
86
|
+
*/
|
|
87
|
+
getAuthMiddleware() {
|
|
88
|
+
return this.middleware.authenticate;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get optional authentication middleware (doesn't fail if no token)
|
|
92
|
+
*/
|
|
93
|
+
getOptionalAuthMiddleware() {
|
|
94
|
+
return this.middleware.optional;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get role-based access control middleware
|
|
98
|
+
*/
|
|
99
|
+
requireRole(roles) {
|
|
100
|
+
return this.middleware.requireRole(roles);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Setup Express routes for authentication
|
|
104
|
+
*/
|
|
105
|
+
setupRoutes(app, basePath = '/api/auth') {
|
|
106
|
+
// Local authentication routes
|
|
107
|
+
app.post(`${basePath}/register`, async (req, res) => {
|
|
108
|
+
const result = await this.register(req.body);
|
|
109
|
+
if (result.success) {
|
|
110
|
+
res.status(201).json(result);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
res.status(400).json(result);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
app.post(`${basePath}/login`, async (req, res) => {
|
|
117
|
+
const result = await this.login(req.body);
|
|
118
|
+
if (result.success) {
|
|
119
|
+
res.status(200).json(result);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
res.status(401).json(result);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
app.post(`${basePath}/refresh`, async (req, res) => {
|
|
126
|
+
const { refreshToken } = req.body;
|
|
127
|
+
if (!refreshToken) {
|
|
128
|
+
res.status(400).json({ success: false, error: 'Refresh token is required' });
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const result = await this.refreshToken(refreshToken);
|
|
132
|
+
if (result.success) {
|
|
133
|
+
res.status(200).json(result);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
res.status(401).json(result);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
app.post(`${basePath}/change-password`, this.getAuthMiddleware(), async (req, res) => {
|
|
140
|
+
const authReq = req;
|
|
141
|
+
const { oldPassword, newPassword } = req.body;
|
|
142
|
+
if (!oldPassword || !newPassword) {
|
|
143
|
+
res.status(400).json({
|
|
144
|
+
success: false,
|
|
145
|
+
error: 'Old password and new password are required',
|
|
146
|
+
});
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const userId = authReq.user?.id || authReq.user?._id || '';
|
|
150
|
+
const result = await this.changePassword(userId, oldPassword, newPassword);
|
|
151
|
+
if (result.success) {
|
|
152
|
+
res.status(200).json(result);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
res.status(400).json(result);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
// Social authentication routes
|
|
159
|
+
if (this.socialAuthHandler) {
|
|
160
|
+
const passport = this.socialAuthHandler.getPassport();
|
|
161
|
+
// Google routes
|
|
162
|
+
if (this.config.socialAuth?.google) {
|
|
163
|
+
app.get(`${basePath}/google`, passport.authenticate('google', { scope: ['profile', 'email'] }));
|
|
164
|
+
app.get(`${basePath}/google/callback`, passport.authenticate('google', { session: false }), async (req, res) => {
|
|
165
|
+
const user = req.user;
|
|
166
|
+
if (user && (user.id || user._id)) {
|
|
167
|
+
const tokens = this.jwtService.generateTokens({
|
|
168
|
+
userId: user.id || user._id || '',
|
|
169
|
+
email: user.email,
|
|
170
|
+
username: user.username,
|
|
171
|
+
phone: user.phone,
|
|
172
|
+
});
|
|
173
|
+
// Remove password from user response
|
|
174
|
+
const { password: _, ...userWithoutPassword } = user;
|
|
175
|
+
res.json({ success: true, user: userWithoutPassword, tokens });
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
res.status(401).json({ success: false, error: 'Google authentication failed' });
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
// Facebook routes
|
|
183
|
+
if (this.config.socialAuth?.facebook) {
|
|
184
|
+
app.get(`${basePath}/facebook`, passport.authenticate('facebook', { scope: ['email'] }));
|
|
185
|
+
app.get(`${basePath}/facebook/callback`, passport.authenticate('facebook', { session: false }), async (req, res) => {
|
|
186
|
+
const user = req.user;
|
|
187
|
+
if (user && (user.id || user._id)) {
|
|
188
|
+
const tokens = this.jwtService.generateTokens({
|
|
189
|
+
userId: user.id || user._id || '',
|
|
190
|
+
email: user.email,
|
|
191
|
+
username: user.username,
|
|
192
|
+
phone: user.phone,
|
|
193
|
+
});
|
|
194
|
+
// Remove password from user response
|
|
195
|
+
const { password: _, ...userWithoutPassword } = user;
|
|
196
|
+
res.json({ success: true, user: userWithoutPassword, tokens });
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
res.status(401).json({ success: false, error: 'Facebook authentication failed' });
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Get the JWT service instance
|
|
207
|
+
*/
|
|
208
|
+
getJwtService() {
|
|
209
|
+
return this.jwtService;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Get the password service instance
|
|
213
|
+
*/
|
|
214
|
+
getPasswordService() {
|
|
215
|
+
return this.passwordService;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
exports.Auth = Auth;
|
|
219
|
+
// Export types and classes
|
|
220
|
+
__exportStar(require("./types"), exports);
|
|
221
|
+
var jwt_2 = require("./utils/jwt");
|
|
222
|
+
Object.defineProperty(exports, "JwtService", { enumerable: true, get: function () { return jwt_2.JwtService; } });
|
|
223
|
+
var password_2 = require("./utils/password");
|
|
224
|
+
Object.defineProperty(exports, "PasswordService", { enumerable: true, get: function () { return password_2.PasswordService; } });
|
|
225
|
+
var validator_1 = require("./utils/validator");
|
|
226
|
+
Object.defineProperty(exports, "ValidatorService", { enumerable: true, get: function () { return validator_1.ValidatorService; } });
|
|
227
|
+
var auth_middleware_2 = require("./middleware/auth.middleware");
|
|
228
|
+
Object.defineProperty(exports, "AuthMiddleware", { enumerable: true, get: function () { return auth_middleware_2.AuthMiddleware; } });
|
|
229
|
+
var local_auth_2 = require("./handlers/local-auth");
|
|
230
|
+
Object.defineProperty(exports, "LocalAuthHandler", { enumerable: true, get: function () { return local_auth_2.LocalAuthHandler; } });
|
|
231
|
+
var social_auth_2 = require("./handlers/social-auth");
|
|
232
|
+
Object.defineProperty(exports, "SocialAuthHandler", { enumerable: true, get: function () { return social_auth_2.SocialAuthHandler; } });
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { RequestHandler } from 'express';
|
|
2
|
+
import { UserRepository } from '../types';
|
|
3
|
+
import { JwtService } from '../utils/jwt';
|
|
4
|
+
export declare class AuthMiddleware {
|
|
5
|
+
private jwtService;
|
|
6
|
+
private userRepository;
|
|
7
|
+
constructor(jwtService: JwtService, userRepository: UserRepository);
|
|
8
|
+
authenticate: RequestHandler;
|
|
9
|
+
optional: RequestHandler;
|
|
10
|
+
requireRole: (roles: string[]) => RequestHandler;
|
|
11
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AuthMiddleware = void 0;
|
|
4
|
+
class AuthMiddleware {
|
|
5
|
+
constructor(jwtService, userRepository) {
|
|
6
|
+
this.jwtService = jwtService;
|
|
7
|
+
this.userRepository = userRepository;
|
|
8
|
+
this.authenticate = async (req, res, next) => {
|
|
9
|
+
const authReq = req;
|
|
10
|
+
try {
|
|
11
|
+
// Get token from Authorization header
|
|
12
|
+
const authHeader = req.headers.authorization;
|
|
13
|
+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
14
|
+
res.status(401).json({
|
|
15
|
+
success: false,
|
|
16
|
+
error: 'No token provided. Please include a valid JWT token in the Authorization header.',
|
|
17
|
+
});
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const token = authHeader.substring(7); // Remove 'Bearer ' prefix
|
|
21
|
+
// Verify token
|
|
22
|
+
let decoded;
|
|
23
|
+
try {
|
|
24
|
+
decoded = this.jwtService.verifyAccessToken(token);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
const message = error instanceof Error ? error.message : 'Invalid token';
|
|
28
|
+
res.status(401).json({
|
|
29
|
+
success: false,
|
|
30
|
+
error: message,
|
|
31
|
+
});
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// Find user
|
|
35
|
+
const user = await this.userRepository.findById(decoded.userId);
|
|
36
|
+
if (!user) {
|
|
37
|
+
res.status(401).json({
|
|
38
|
+
success: false,
|
|
39
|
+
error: 'User not found. Token may be invalid.',
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// Attach user to request
|
|
44
|
+
authReq.user = user;
|
|
45
|
+
next();
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
res.status(500).json({
|
|
49
|
+
success: false,
|
|
50
|
+
error: 'Authentication error',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
this.optional = async (req, res, next) => {
|
|
55
|
+
try {
|
|
56
|
+
const authReq = req;
|
|
57
|
+
const authHeader = req.headers.authorization;
|
|
58
|
+
if (authHeader && authHeader.startsWith('Bearer ')) {
|
|
59
|
+
const token = authHeader.substring(7);
|
|
60
|
+
try {
|
|
61
|
+
const decoded = this.jwtService.verifyAccessToken(token);
|
|
62
|
+
const user = await this.userRepository.findById(decoded.userId);
|
|
63
|
+
if (user) {
|
|
64
|
+
authReq.user = user;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
// Token invalid, but continue without user
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
next();
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
// Continue even if there's an error
|
|
75
|
+
next();
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
// Role-based access control helper
|
|
79
|
+
this.requireRole = (roles) => {
|
|
80
|
+
return (req, res, next) => {
|
|
81
|
+
const authReq = req;
|
|
82
|
+
if (!authReq.user) {
|
|
83
|
+
res.status(401).json({
|
|
84
|
+
success: false,
|
|
85
|
+
error: 'Authentication required',
|
|
86
|
+
});
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const userRole = authReq.user.role || 'user';
|
|
90
|
+
if (!roles.includes(userRole)) {
|
|
91
|
+
res.status(403).json({
|
|
92
|
+
success: false,
|
|
93
|
+
error: 'Insufficient permissions',
|
|
94
|
+
});
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
next();
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
exports.AuthMiddleware = AuthMiddleware;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Request } from 'express';
|
|
2
|
+
export interface AuthConfig {
|
|
3
|
+
jwtSecret: string;
|
|
4
|
+
jwtExpiresIn?: string;
|
|
5
|
+
refreshTokenSecret?: string;
|
|
6
|
+
refreshTokenExpiresIn?: string;
|
|
7
|
+
bcryptRounds?: number;
|
|
8
|
+
enableRefreshTokens?: boolean;
|
|
9
|
+
socialAuth?: {
|
|
10
|
+
google?: {
|
|
11
|
+
clientID: string;
|
|
12
|
+
clientSecret: string;
|
|
13
|
+
callbackURL: string;
|
|
14
|
+
};
|
|
15
|
+
facebook?: {
|
|
16
|
+
clientID: string;
|
|
17
|
+
clientSecret: string;
|
|
18
|
+
callbackURL: string;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface User {
|
|
23
|
+
id: string;
|
|
24
|
+
email?: string;
|
|
25
|
+
username?: string;
|
|
26
|
+
phone?: string;
|
|
27
|
+
password?: string;
|
|
28
|
+
provider?: 'local' | 'google' | 'facebook';
|
|
29
|
+
providerId?: string;
|
|
30
|
+
verified?: boolean;
|
|
31
|
+
createdAt?: Date;
|
|
32
|
+
updatedAt?: Date;
|
|
33
|
+
[key: string]: any;
|
|
34
|
+
}
|
|
35
|
+
export interface UserDocument extends User {
|
|
36
|
+
_id?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface AuthTokens {
|
|
39
|
+
accessToken: string;
|
|
40
|
+
refreshToken?: string;
|
|
41
|
+
expiresIn: number;
|
|
42
|
+
}
|
|
43
|
+
export interface LoginCredentials {
|
|
44
|
+
email?: string;
|
|
45
|
+
username?: string;
|
|
46
|
+
phone?: string;
|
|
47
|
+
password: string;
|
|
48
|
+
}
|
|
49
|
+
export interface RegisterData {
|
|
50
|
+
email?: string;
|
|
51
|
+
username?: string;
|
|
52
|
+
phone?: string;
|
|
53
|
+
password: string;
|
|
54
|
+
[key: string]: any;
|
|
55
|
+
}
|
|
56
|
+
export interface AuthResult {
|
|
57
|
+
success: boolean;
|
|
58
|
+
user?: User;
|
|
59
|
+
tokens?: AuthTokens;
|
|
60
|
+
message?: string;
|
|
61
|
+
error?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface UserRepository {
|
|
64
|
+
findById(id: string): Promise<UserDocument | null>;
|
|
65
|
+
findByEmail(email: string): Promise<UserDocument | null>;
|
|
66
|
+
findByUsername(username: string): Promise<UserDocument | null>;
|
|
67
|
+
findByPhone(phone: string): Promise<UserDocument | null>;
|
|
68
|
+
findByProvider(provider: string, providerId: string): Promise<UserDocument | null>;
|
|
69
|
+
create(userData: Partial<User>): Promise<UserDocument>;
|
|
70
|
+
update(id: string, updateData: Partial<User>): Promise<UserDocument | null>;
|
|
71
|
+
delete(id: string): Promise<boolean>;
|
|
72
|
+
}
|
|
73
|
+
export interface AuthRequest extends Request {
|
|
74
|
+
user?: UserDocument;
|
|
75
|
+
}
|
|
76
|
+
export interface JwtPayload {
|
|
77
|
+
userId: string;
|
|
78
|
+
email?: string;
|
|
79
|
+
username?: string;
|
|
80
|
+
phone?: string;
|
|
81
|
+
type?: 'access' | 'refresh';
|
|
82
|
+
iat?: number;
|
|
83
|
+
exp?: number;
|
|
84
|
+
}
|