@lenan-soft/auth 1.0.1
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/README.md +591 -0
- package/dist/index.cjs +45 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +129 -0
- package/dist/index.d.ts +129 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/nestjs/index.cjs +738 -0
- package/dist/nestjs/index.cjs.map +1 -0
- package/dist/nestjs/index.d.cts +510 -0
- package/dist/nestjs/index.d.ts +510 -0
- package/dist/nestjs/index.js +709 -0
- package/dist/nestjs/index.js.map +1 -0
- package/dist/react/index.cjs +560 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +285 -0
- package/dist/react/index.d.ts +285 -0
- package/dist/react/index.js +531 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/native/index.cjs +60 -0
- package/dist/react/native/index.cjs.map +1 -0
- package/dist/react/native/index.d.cts +50 -0
- package/dist/react/native/index.d.ts +50 -0
- package/dist/react/native/index.js +41 -0
- package/dist/react/native/index.js.map +1 -0
- package/dist/shared/index.cjs +45 -0
- package/dist/shared/index.cjs.map +1 -0
- package/dist/shared/index.d.cts +129 -0
- package/dist/shared/index.d.ts +129 -0
- package/dist/shared/index.js +17 -0
- package/dist/shared/index.js.map +1 -0
- package/package.json +151 -0
|
@@ -0,0 +1,738 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
30
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
31
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
32
|
+
if (decorator = decorators[i])
|
|
33
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
34
|
+
if (kind && result) __defProp(target, key, result);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
38
|
+
|
|
39
|
+
// src/nestjs/index.ts
|
|
40
|
+
var index_exports = {};
|
|
41
|
+
__export(index_exports, {
|
|
42
|
+
AuthController: () => AuthController,
|
|
43
|
+
AuthService: () => AuthService,
|
|
44
|
+
CurrentUser: () => CurrentUser,
|
|
45
|
+
IS_PUBLIC_KEY: () => IS_PUBLIC_KEY,
|
|
46
|
+
JwtAuthGuard: () => JwtAuthGuard,
|
|
47
|
+
JwtStrategy: () => JwtStrategy,
|
|
48
|
+
JwtTokenService: () => JwtTokenService,
|
|
49
|
+
LENAN_AUTH_OPTIONS: () => LENAN_AUTH_OPTIONS,
|
|
50
|
+
LENAN_USER_SERVICE: () => LENAN_USER_SERVICE,
|
|
51
|
+
LenanAuthModule: () => LenanAuthModule,
|
|
52
|
+
LoginDto: () => LoginDto,
|
|
53
|
+
MessageResponseDto: () => MessageResponseDto,
|
|
54
|
+
PasswordService: () => PasswordService,
|
|
55
|
+
Public: () => Public,
|
|
56
|
+
RefreshTokenDto: () => RefreshTokenDto,
|
|
57
|
+
RefreshTokenGuard: () => RefreshTokenGuard,
|
|
58
|
+
RefreshTokenStrategy: () => RefreshTokenStrategy,
|
|
59
|
+
RegisterDto: () => RegisterDto,
|
|
60
|
+
TokensResponseDto: () => TokensResponseDto
|
|
61
|
+
});
|
|
62
|
+
module.exports = __toCommonJS(index_exports);
|
|
63
|
+
|
|
64
|
+
// src/nestjs/lenan-auth.module.ts
|
|
65
|
+
var import_common10 = require("@nestjs/common");
|
|
66
|
+
var import_jwt2 = require("@nestjs/jwt");
|
|
67
|
+
var import_passport5 = require("@nestjs/passport");
|
|
68
|
+
|
|
69
|
+
// src/nestjs/controllers/auth.controller.ts
|
|
70
|
+
var import_common4 = require("@nestjs/common");
|
|
71
|
+
|
|
72
|
+
// src/nestjs/decorators/index.ts
|
|
73
|
+
var import_common = require("@nestjs/common");
|
|
74
|
+
var IS_PUBLIC_KEY = "isPublic";
|
|
75
|
+
var Public = () => (0, import_common.SetMetadata)(IS_PUBLIC_KEY, true);
|
|
76
|
+
var CurrentUser = (0, import_common.createParamDecorator)(
|
|
77
|
+
(data, ctx) => {
|
|
78
|
+
const request = ctx.switchToHttp().getRequest();
|
|
79
|
+
const user = request.user;
|
|
80
|
+
if (!user) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return data ? user[data] : user;
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
// src/nestjs/dto/index.ts
|
|
88
|
+
var import_class_validator = require("class-validator");
|
|
89
|
+
var LoginDto = class {
|
|
90
|
+
email;
|
|
91
|
+
password;
|
|
92
|
+
};
|
|
93
|
+
__decorateClass([
|
|
94
|
+
(0, import_class_validator.IsEmail)({}, { message: "Please provide a valid email address" })
|
|
95
|
+
], LoginDto.prototype, "email", 2);
|
|
96
|
+
__decorateClass([
|
|
97
|
+
(0, import_class_validator.IsString)(),
|
|
98
|
+
(0, import_class_validator.MinLength)(1, { message: "Password is required" })
|
|
99
|
+
], LoginDto.prototype, "password", 2);
|
|
100
|
+
var RegisterDto = class {
|
|
101
|
+
email;
|
|
102
|
+
password;
|
|
103
|
+
confirmPassword;
|
|
104
|
+
};
|
|
105
|
+
__decorateClass([
|
|
106
|
+
(0, import_class_validator.IsEmail)({}, { message: "Please provide a valid email address" })
|
|
107
|
+
], RegisterDto.prototype, "email", 2);
|
|
108
|
+
__decorateClass([
|
|
109
|
+
(0, import_class_validator.IsString)(),
|
|
110
|
+
(0, import_class_validator.MinLength)(8, { message: "Password must be at least 8 characters long" }),
|
|
111
|
+
(0, import_class_validator.MaxLength)(128, { message: "Password must be at most 128 characters long" }),
|
|
112
|
+
(0, import_class_validator.Matches)(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, {
|
|
113
|
+
message: "Password must contain at least one lowercase letter, one uppercase letter, and one number"
|
|
114
|
+
})
|
|
115
|
+
], RegisterDto.prototype, "password", 2);
|
|
116
|
+
__decorateClass([
|
|
117
|
+
(0, import_class_validator.IsString)(),
|
|
118
|
+
(0, import_class_validator.MinLength)(1, { message: "Password confirmation is required" })
|
|
119
|
+
], RegisterDto.prototype, "confirmPassword", 2);
|
|
120
|
+
var RefreshTokenDto = class {
|
|
121
|
+
refreshToken;
|
|
122
|
+
};
|
|
123
|
+
__decorateClass([
|
|
124
|
+
(0, import_class_validator.IsString)(),
|
|
125
|
+
(0, import_class_validator.MinLength)(1, { message: "Refresh token is required" })
|
|
126
|
+
], RefreshTokenDto.prototype, "refreshToken", 2);
|
|
127
|
+
var TokensResponseDto = class {
|
|
128
|
+
accessToken;
|
|
129
|
+
refreshToken;
|
|
130
|
+
constructor(accessToken, refreshToken) {
|
|
131
|
+
this.accessToken = accessToken;
|
|
132
|
+
this.refreshToken = refreshToken;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
var MessageResponseDto = class {
|
|
136
|
+
message;
|
|
137
|
+
constructor(message) {
|
|
138
|
+
this.message = message;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// src/nestjs/guards/jwt-auth.guard.ts
|
|
143
|
+
var import_common2 = require("@nestjs/common");
|
|
144
|
+
var import_passport = require("@nestjs/passport");
|
|
145
|
+
var JwtAuthGuard = class extends (0, import_passport.AuthGuard)("lenan-jwt") {
|
|
146
|
+
constructor(reflector) {
|
|
147
|
+
super();
|
|
148
|
+
this.reflector = reflector;
|
|
149
|
+
}
|
|
150
|
+
canActivate(context) {
|
|
151
|
+
const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [
|
|
152
|
+
context.getHandler(),
|
|
153
|
+
context.getClass()
|
|
154
|
+
]);
|
|
155
|
+
if (isPublic) {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
return super.canActivate(context);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
JwtAuthGuard = __decorateClass([
|
|
162
|
+
(0, import_common2.Injectable)()
|
|
163
|
+
], JwtAuthGuard);
|
|
164
|
+
|
|
165
|
+
// src/nestjs/guards/refresh-token.guard.ts
|
|
166
|
+
var import_common3 = require("@nestjs/common");
|
|
167
|
+
var import_passport2 = require("@nestjs/passport");
|
|
168
|
+
var RefreshTokenGuard = class extends (0, import_passport2.AuthGuard)("lenan-jwt-refresh") {
|
|
169
|
+
};
|
|
170
|
+
RefreshTokenGuard = __decorateClass([
|
|
171
|
+
(0, import_common3.Injectable)()
|
|
172
|
+
], RefreshTokenGuard);
|
|
173
|
+
|
|
174
|
+
// src/nestjs/controllers/auth.controller.ts
|
|
175
|
+
var AuthController = class {
|
|
176
|
+
constructor(authService) {
|
|
177
|
+
this.authService = authService;
|
|
178
|
+
}
|
|
179
|
+
async register(dto) {
|
|
180
|
+
const tokens = await this.authService.register(dto);
|
|
181
|
+
return new TokensResponseDto(tokens.accessToken, tokens.refreshToken);
|
|
182
|
+
}
|
|
183
|
+
async login(dto) {
|
|
184
|
+
const tokens = await this.authService.login(dto);
|
|
185
|
+
return new TokensResponseDto(tokens.accessToken, tokens.refreshToken);
|
|
186
|
+
}
|
|
187
|
+
async refresh(user) {
|
|
188
|
+
const tokens = await this.authService.refreshTokens(
|
|
189
|
+
user.id,
|
|
190
|
+
user.refreshToken
|
|
191
|
+
);
|
|
192
|
+
return new TokensResponseDto(tokens.accessToken, tokens.refreshToken);
|
|
193
|
+
}
|
|
194
|
+
async logout(userId) {
|
|
195
|
+
await this.authService.logout(userId);
|
|
196
|
+
return new MessageResponseDto("Logged out successfully");
|
|
197
|
+
}
|
|
198
|
+
async me(user) {
|
|
199
|
+
return user;
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
__decorateClass([
|
|
203
|
+
Public(),
|
|
204
|
+
(0, import_common4.Post)("register"),
|
|
205
|
+
(0, import_common4.HttpCode)(import_common4.HttpStatus.CREATED),
|
|
206
|
+
__decorateParam(0, (0, import_common4.Body)())
|
|
207
|
+
], AuthController.prototype, "register", 1);
|
|
208
|
+
__decorateClass([
|
|
209
|
+
Public(),
|
|
210
|
+
(0, import_common4.Post)("login"),
|
|
211
|
+
(0, import_common4.HttpCode)(import_common4.HttpStatus.OK),
|
|
212
|
+
__decorateParam(0, (0, import_common4.Body)())
|
|
213
|
+
], AuthController.prototype, "login", 1);
|
|
214
|
+
__decorateClass([
|
|
215
|
+
Public(),
|
|
216
|
+
(0, import_common4.UseGuards)(RefreshTokenGuard),
|
|
217
|
+
(0, import_common4.Post)("refresh"),
|
|
218
|
+
(0, import_common4.HttpCode)(import_common4.HttpStatus.OK),
|
|
219
|
+
__decorateParam(0, CurrentUser())
|
|
220
|
+
], AuthController.prototype, "refresh", 1);
|
|
221
|
+
__decorateClass([
|
|
222
|
+
(0, import_common4.UseGuards)(JwtAuthGuard),
|
|
223
|
+
(0, import_common4.Post)("logout"),
|
|
224
|
+
(0, import_common4.HttpCode)(import_common4.HttpStatus.OK),
|
|
225
|
+
__decorateParam(0, CurrentUser("id"))
|
|
226
|
+
], AuthController.prototype, "logout", 1);
|
|
227
|
+
__decorateClass([
|
|
228
|
+
(0, import_common4.UseGuards)(JwtAuthGuard),
|
|
229
|
+
(0, import_common4.Get)("me"),
|
|
230
|
+
__decorateParam(0, CurrentUser())
|
|
231
|
+
], AuthController.prototype, "me", 1);
|
|
232
|
+
AuthController = __decorateClass([
|
|
233
|
+
(0, import_common4.Controller)("auth")
|
|
234
|
+
], AuthController);
|
|
235
|
+
|
|
236
|
+
// src/nestjs/interfaces/index.ts
|
|
237
|
+
var LENAN_USER_SERVICE = /* @__PURE__ */ Symbol("LENAN_USER_SERVICE");
|
|
238
|
+
var LENAN_AUTH_OPTIONS = /* @__PURE__ */ Symbol("LENAN_AUTH_OPTIONS");
|
|
239
|
+
|
|
240
|
+
// src/nestjs/services/auth.service.ts
|
|
241
|
+
var import_common5 = require("@nestjs/common");
|
|
242
|
+
var AuthService = class {
|
|
243
|
+
constructor(userService, passwordService, jwtTokenService) {
|
|
244
|
+
this.userService = userService;
|
|
245
|
+
this.passwordService = passwordService;
|
|
246
|
+
this.jwtTokenService = jwtTokenService;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Register a new user
|
|
250
|
+
* @throws ConflictException if email already exists
|
|
251
|
+
*/
|
|
252
|
+
async register(dto) {
|
|
253
|
+
const existingUser = await this.userService.findByEmail(dto.email);
|
|
254
|
+
if (existingUser) {
|
|
255
|
+
throw new import_common5.ConflictException("Email already registered");
|
|
256
|
+
}
|
|
257
|
+
if (dto.password !== dto.confirmPassword) {
|
|
258
|
+
throw new import_common5.BadRequestException("Passwords do not match");
|
|
259
|
+
}
|
|
260
|
+
const passwordHash = await this.passwordService.hash(dto.password);
|
|
261
|
+
const user = await this.userService.createUser({
|
|
262
|
+
email: dto.email,
|
|
263
|
+
passwordHash
|
|
264
|
+
});
|
|
265
|
+
const tokens = await this.jwtTokenService.generateTokens(
|
|
266
|
+
user.id,
|
|
267
|
+
user.email
|
|
268
|
+
);
|
|
269
|
+
const hashedRefreshToken = await this.passwordService.hashToken(
|
|
270
|
+
tokens.refreshToken
|
|
271
|
+
);
|
|
272
|
+
await this.userService.updateRefreshToken(user.id, hashedRefreshToken);
|
|
273
|
+
return tokens;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Authenticate a user with email and password
|
|
277
|
+
* @throws UnauthorizedException if credentials are invalid
|
|
278
|
+
*/
|
|
279
|
+
async login(dto) {
|
|
280
|
+
const user = await this.userService.findByEmail(dto.email);
|
|
281
|
+
if (!user) {
|
|
282
|
+
throw new import_common5.UnauthorizedException("Invalid credentials");
|
|
283
|
+
}
|
|
284
|
+
const isPasswordValid = await this.passwordService.compare(
|
|
285
|
+
dto.password,
|
|
286
|
+
user.passwordHash
|
|
287
|
+
);
|
|
288
|
+
if (!isPasswordValid) {
|
|
289
|
+
throw new import_common5.UnauthorizedException("Invalid credentials");
|
|
290
|
+
}
|
|
291
|
+
const tokens = await this.jwtTokenService.generateTokens(
|
|
292
|
+
user.id,
|
|
293
|
+
user.email
|
|
294
|
+
);
|
|
295
|
+
const hashedRefreshToken = await this.passwordService.hashToken(
|
|
296
|
+
tokens.refreshToken
|
|
297
|
+
);
|
|
298
|
+
await this.userService.updateRefreshToken(user.id, hashedRefreshToken);
|
|
299
|
+
return tokens;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Refresh tokens using a valid refresh token
|
|
303
|
+
* @throws UnauthorizedException if refresh token is invalid
|
|
304
|
+
*/
|
|
305
|
+
async refreshTokens(userId, refreshToken) {
|
|
306
|
+
const user = await this.userService.findById(userId);
|
|
307
|
+
if (!user || !user.hashedRefreshToken) {
|
|
308
|
+
throw new import_common5.UnauthorizedException("Invalid refresh token");
|
|
309
|
+
}
|
|
310
|
+
const isValid = await this.passwordService.compareToken(
|
|
311
|
+
refreshToken,
|
|
312
|
+
user.hashedRefreshToken
|
|
313
|
+
);
|
|
314
|
+
if (!isValid) {
|
|
315
|
+
throw new import_common5.UnauthorizedException("Invalid refresh token");
|
|
316
|
+
}
|
|
317
|
+
const tokens = await this.jwtTokenService.generateTokens(
|
|
318
|
+
user.id,
|
|
319
|
+
user.email
|
|
320
|
+
);
|
|
321
|
+
const hashedRefreshToken = await this.passwordService.hashToken(
|
|
322
|
+
tokens.refreshToken
|
|
323
|
+
);
|
|
324
|
+
await this.userService.updateRefreshToken(user.id, hashedRefreshToken);
|
|
325
|
+
return tokens;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Log out a user by clearing their refresh token
|
|
329
|
+
*/
|
|
330
|
+
async logout(userId) {
|
|
331
|
+
await this.userService.updateRefreshToken(userId, null);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Validate a user exists by ID
|
|
335
|
+
* Used by JWT strategy to validate token payloads
|
|
336
|
+
*/
|
|
337
|
+
async validateUser(userId) {
|
|
338
|
+
return this.userService.findById(userId);
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Get current user by ID (without sensitive fields)
|
|
342
|
+
*/
|
|
343
|
+
async getCurrentUser(userId) {
|
|
344
|
+
const user = await this.userService.findById(userId);
|
|
345
|
+
if (!user) {
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
const { passwordHash, hashedRefreshToken, ...safeUser } = user;
|
|
349
|
+
return safeUser;
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
AuthService = __decorateClass([
|
|
353
|
+
(0, import_common5.Injectable)(),
|
|
354
|
+
__decorateParam(0, (0, import_common5.Inject)(LENAN_USER_SERVICE))
|
|
355
|
+
], AuthService);
|
|
356
|
+
|
|
357
|
+
// src/nestjs/services/jwt-token.service.ts
|
|
358
|
+
var import_common6 = require("@nestjs/common");
|
|
359
|
+
var JwtTokenService = class {
|
|
360
|
+
constructor(jwtService, options) {
|
|
361
|
+
this.jwtService = jwtService;
|
|
362
|
+
this.accessSecret = options.jwtSecret;
|
|
363
|
+
this.refreshSecret = options.jwtRefreshSecret ?? `${options.jwtSecret}_refresh`;
|
|
364
|
+
this.accessExpiry = options.jwtAccessExpiry ?? "15m";
|
|
365
|
+
this.refreshExpiry = options.jwtRefreshExpiry ?? "7d";
|
|
366
|
+
}
|
|
367
|
+
accessExpiry;
|
|
368
|
+
refreshExpiry;
|
|
369
|
+
accessSecret;
|
|
370
|
+
refreshSecret;
|
|
371
|
+
/**
|
|
372
|
+
* Generate access and refresh tokens for a user
|
|
373
|
+
*/
|
|
374
|
+
async generateTokens(userId, email) {
|
|
375
|
+
const [accessToken, refreshToken] = await Promise.all([
|
|
376
|
+
this.generateAccessToken(userId, email),
|
|
377
|
+
this.generateRefreshToken(userId, email)
|
|
378
|
+
]);
|
|
379
|
+
return { accessToken, refreshToken };
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Generate an access token
|
|
383
|
+
*/
|
|
384
|
+
async generateAccessToken(userId, email) {
|
|
385
|
+
const payload = {
|
|
386
|
+
sub: userId,
|
|
387
|
+
email,
|
|
388
|
+
type: "access"
|
|
389
|
+
};
|
|
390
|
+
return this.jwtService.signAsync(payload, {
|
|
391
|
+
secret: this.accessSecret,
|
|
392
|
+
expiresIn: this.accessExpiry
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Generate a refresh token
|
|
397
|
+
*/
|
|
398
|
+
async generateRefreshToken(userId, email) {
|
|
399
|
+
const payload = {
|
|
400
|
+
sub: userId,
|
|
401
|
+
email,
|
|
402
|
+
type: "refresh"
|
|
403
|
+
};
|
|
404
|
+
return this.jwtService.signAsync(payload, {
|
|
405
|
+
secret: this.refreshSecret,
|
|
406
|
+
expiresIn: this.refreshExpiry
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Verify an access token and return its payload
|
|
411
|
+
*/
|
|
412
|
+
async verifyAccessToken(token) {
|
|
413
|
+
try {
|
|
414
|
+
const payload = await this.jwtService.verifyAsync(
|
|
415
|
+
token,
|
|
416
|
+
{
|
|
417
|
+
secret: this.accessSecret
|
|
418
|
+
}
|
|
419
|
+
);
|
|
420
|
+
if (payload.type !== "access") {
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
return payload;
|
|
424
|
+
} catch {
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Verify a refresh token and return its payload
|
|
430
|
+
*/
|
|
431
|
+
async verifyRefreshToken(token) {
|
|
432
|
+
try {
|
|
433
|
+
const payload = await this.jwtService.verifyAsync(
|
|
434
|
+
token,
|
|
435
|
+
{
|
|
436
|
+
secret: this.refreshSecret
|
|
437
|
+
}
|
|
438
|
+
);
|
|
439
|
+
if (payload.type !== "refresh") {
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
return payload;
|
|
443
|
+
} catch {
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Decode a token without verification (useful for debugging)
|
|
449
|
+
*/
|
|
450
|
+
decodeToken(token) {
|
|
451
|
+
try {
|
|
452
|
+
return this.jwtService.decode(token);
|
|
453
|
+
} catch {
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
JwtTokenService = __decorateClass([
|
|
459
|
+
(0, import_common6.Injectable)(),
|
|
460
|
+
__decorateParam(1, (0, import_common6.Inject)(LENAN_AUTH_OPTIONS))
|
|
461
|
+
], JwtTokenService);
|
|
462
|
+
|
|
463
|
+
// src/nestjs/services/password.service.ts
|
|
464
|
+
var import_common7 = require("@nestjs/common");
|
|
465
|
+
var bcrypt = __toESM(require("bcryptjs"), 1);
|
|
466
|
+
var PasswordService = class {
|
|
467
|
+
saltRounds = 12;
|
|
468
|
+
/**
|
|
469
|
+
* Hash a plain text password
|
|
470
|
+
*/
|
|
471
|
+
async hash(password) {
|
|
472
|
+
return bcrypt.hash(password, this.saltRounds);
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Compare a plain text password with a hash
|
|
476
|
+
*/
|
|
477
|
+
async compare(password, hash2) {
|
|
478
|
+
return bcrypt.compare(password, hash2);
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Hash a refresh token for storage
|
|
482
|
+
* Uses fewer rounds since refresh tokens are already random
|
|
483
|
+
*/
|
|
484
|
+
async hashToken(token) {
|
|
485
|
+
return bcrypt.hash(token, 10);
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Compare a refresh token with its hash
|
|
489
|
+
*/
|
|
490
|
+
async compareToken(token, hash2) {
|
|
491
|
+
return bcrypt.compare(token, hash2);
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
PasswordService = __decorateClass([
|
|
495
|
+
(0, import_common7.Injectable)()
|
|
496
|
+
], PasswordService);
|
|
497
|
+
|
|
498
|
+
// src/nestjs/strategies/jwt.strategy.ts
|
|
499
|
+
var import_common8 = require("@nestjs/common");
|
|
500
|
+
var import_passport3 = require("@nestjs/passport");
|
|
501
|
+
var import_passport_jwt = require("passport-jwt");
|
|
502
|
+
var JwtStrategy = class extends (0, import_passport3.PassportStrategy)(import_passport_jwt.Strategy, "lenan-jwt") {
|
|
503
|
+
constructor(options, userService) {
|
|
504
|
+
super({
|
|
505
|
+
jwtFromRequest: import_passport_jwt.ExtractJwt.fromAuthHeaderAsBearerToken(),
|
|
506
|
+
ignoreExpiration: false,
|
|
507
|
+
secretOrKey: options.jwtSecret
|
|
508
|
+
});
|
|
509
|
+
this.userService = userService;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Validate the JWT payload and return the user
|
|
513
|
+
* This is called by Passport after successfully verifying the token signature
|
|
514
|
+
*/
|
|
515
|
+
async validate(payload) {
|
|
516
|
+
if (payload.type !== "access") {
|
|
517
|
+
throw new import_common8.UnauthorizedException("Invalid token type");
|
|
518
|
+
}
|
|
519
|
+
const user = await this.userService.findById(payload.sub);
|
|
520
|
+
if (!user) {
|
|
521
|
+
throw new import_common8.UnauthorizedException("User not found");
|
|
522
|
+
}
|
|
523
|
+
const { passwordHash, hashedRefreshToken, ...safeUser } = user;
|
|
524
|
+
return safeUser;
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
JwtStrategy = __decorateClass([
|
|
528
|
+
(0, import_common8.Injectable)(),
|
|
529
|
+
__decorateParam(0, (0, import_common8.Inject)(LENAN_AUTH_OPTIONS)),
|
|
530
|
+
__decorateParam(1, (0, import_common8.Inject)(LENAN_USER_SERVICE))
|
|
531
|
+
], JwtStrategy);
|
|
532
|
+
|
|
533
|
+
// src/nestjs/strategies/refresh-token.strategy.ts
|
|
534
|
+
var import_common9 = require("@nestjs/common");
|
|
535
|
+
var import_passport4 = require("@nestjs/passport");
|
|
536
|
+
var import_passport_jwt2 = require("passport-jwt");
|
|
537
|
+
var RefreshTokenStrategy = class extends (0, import_passport4.PassportStrategy)(
|
|
538
|
+
import_passport_jwt2.Strategy,
|
|
539
|
+
"lenan-jwt-refresh"
|
|
540
|
+
) {
|
|
541
|
+
constructor(options, userService) {
|
|
542
|
+
const refreshSecret = options.jwtRefreshSecret ?? `${options.jwtSecret}_refresh`;
|
|
543
|
+
super({
|
|
544
|
+
jwtFromRequest: import_passport_jwt2.ExtractJwt.fromBodyField("refreshToken"),
|
|
545
|
+
ignoreExpiration: false,
|
|
546
|
+
secretOrKey: refreshSecret,
|
|
547
|
+
passReqToCallback: true
|
|
548
|
+
});
|
|
549
|
+
this.userService = userService;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Validate the refresh token payload
|
|
553
|
+
* Returns user with the refresh token attached for further validation
|
|
554
|
+
*/
|
|
555
|
+
async validate(req, payload) {
|
|
556
|
+
if (payload.type !== "refresh") {
|
|
557
|
+
throw new import_common9.UnauthorizedException("Invalid token type");
|
|
558
|
+
}
|
|
559
|
+
const user = await this.userService.findById(payload.sub);
|
|
560
|
+
if (!user) {
|
|
561
|
+
throw new import_common9.UnauthorizedException("User not found");
|
|
562
|
+
}
|
|
563
|
+
if (!user.hashedRefreshToken) {
|
|
564
|
+
throw new import_common9.UnauthorizedException("Refresh token has been revoked");
|
|
565
|
+
}
|
|
566
|
+
const refreshToken = req.body?.refreshToken;
|
|
567
|
+
return { ...user, refreshToken };
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
RefreshTokenStrategy = __decorateClass([
|
|
571
|
+
(0, import_common9.Injectable)(),
|
|
572
|
+
__decorateParam(0, (0, import_common9.Inject)(LENAN_AUTH_OPTIONS)),
|
|
573
|
+
__decorateParam(1, (0, import_common9.Inject)(LENAN_USER_SERVICE))
|
|
574
|
+
], RefreshTokenStrategy);
|
|
575
|
+
|
|
576
|
+
// src/nestjs/lenan-auth.module.ts
|
|
577
|
+
var LenanAuthModule = class {
|
|
578
|
+
/**
|
|
579
|
+
* Register the auth module with synchronous configuration
|
|
580
|
+
*
|
|
581
|
+
* @param options - Auth module configuration options
|
|
582
|
+
* @param userService - Your UserService class implementing UserServiceInterface
|
|
583
|
+
*/
|
|
584
|
+
static register(options, userService) {
|
|
585
|
+
const providers = this.createProviders(options, userService);
|
|
586
|
+
const controllers = options.useController !== false ? [AuthController] : [];
|
|
587
|
+
return {
|
|
588
|
+
module: LenanAuthModule,
|
|
589
|
+
imports: [
|
|
590
|
+
import_passport5.PassportModule.register({ defaultStrategy: "lenan-jwt" }),
|
|
591
|
+
import_jwt2.JwtModule.register({
|
|
592
|
+
secret: options.jwtSecret,
|
|
593
|
+
signOptions: { expiresIn: options.jwtAccessExpiry ?? "15m" }
|
|
594
|
+
})
|
|
595
|
+
],
|
|
596
|
+
providers,
|
|
597
|
+
controllers,
|
|
598
|
+
exports: [
|
|
599
|
+
AuthService,
|
|
600
|
+
PasswordService,
|
|
601
|
+
JwtTokenService,
|
|
602
|
+
JwtStrategy,
|
|
603
|
+
LENAN_USER_SERVICE
|
|
604
|
+
]
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Register the auth module with async configuration
|
|
609
|
+
*
|
|
610
|
+
* @param asyncOptions - Async configuration options
|
|
611
|
+
* @param userService - Your UserService class implementing UserServiceInterface
|
|
612
|
+
*/
|
|
613
|
+
static registerAsync(asyncOptions, userService) {
|
|
614
|
+
const providers = this.createAsyncProviders(asyncOptions, userService);
|
|
615
|
+
return {
|
|
616
|
+
module: LenanAuthModule,
|
|
617
|
+
imports: [
|
|
618
|
+
...asyncOptions.imports ?? [],
|
|
619
|
+
import_passport5.PassportModule.register({ defaultStrategy: "lenan-jwt" }),
|
|
620
|
+
import_jwt2.JwtModule.registerAsync({
|
|
621
|
+
imports: asyncOptions.imports,
|
|
622
|
+
useFactory: async (...args) => {
|
|
623
|
+
let options;
|
|
624
|
+
if (asyncOptions.useFactory) {
|
|
625
|
+
options = await asyncOptions.useFactory(...args);
|
|
626
|
+
} else {
|
|
627
|
+
throw new Error("useFactory is required for async registration");
|
|
628
|
+
}
|
|
629
|
+
return {
|
|
630
|
+
secret: options.jwtSecret,
|
|
631
|
+
signOptions: {
|
|
632
|
+
expiresIn: options.jwtAccessExpiry ?? "15m"
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
},
|
|
636
|
+
inject: asyncOptions.inject ?? []
|
|
637
|
+
})
|
|
638
|
+
],
|
|
639
|
+
providers,
|
|
640
|
+
controllers: [AuthController],
|
|
641
|
+
exports: [
|
|
642
|
+
AuthService,
|
|
643
|
+
PasswordService,
|
|
644
|
+
JwtTokenService,
|
|
645
|
+
JwtStrategy,
|
|
646
|
+
LENAN_USER_SERVICE
|
|
647
|
+
]
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
static createProviders(options, userService) {
|
|
651
|
+
return [
|
|
652
|
+
{
|
|
653
|
+
provide: LENAN_AUTH_OPTIONS,
|
|
654
|
+
useValue: options
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
provide: LENAN_USER_SERVICE,
|
|
658
|
+
useClass: userService
|
|
659
|
+
},
|
|
660
|
+
AuthService,
|
|
661
|
+
PasswordService,
|
|
662
|
+
JwtTokenService,
|
|
663
|
+
JwtStrategy,
|
|
664
|
+
RefreshTokenStrategy
|
|
665
|
+
];
|
|
666
|
+
}
|
|
667
|
+
static createAsyncProviders(asyncOptions, userService) {
|
|
668
|
+
const asyncOptionsProvider = this.createAsyncOptionsProvider(asyncOptions);
|
|
669
|
+
return [
|
|
670
|
+
asyncOptionsProvider,
|
|
671
|
+
{
|
|
672
|
+
provide: LENAN_USER_SERVICE,
|
|
673
|
+
useClass: userService
|
|
674
|
+
},
|
|
675
|
+
AuthService,
|
|
676
|
+
PasswordService,
|
|
677
|
+
JwtTokenService,
|
|
678
|
+
JwtStrategy,
|
|
679
|
+
RefreshTokenStrategy
|
|
680
|
+
];
|
|
681
|
+
}
|
|
682
|
+
static createAsyncOptionsProvider(asyncOptions) {
|
|
683
|
+
if (asyncOptions.useFactory) {
|
|
684
|
+
return {
|
|
685
|
+
provide: LENAN_AUTH_OPTIONS,
|
|
686
|
+
useFactory: asyncOptions.useFactory,
|
|
687
|
+
inject: asyncOptions.inject ?? []
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
if (asyncOptions.useClass) {
|
|
691
|
+
return {
|
|
692
|
+
provide: LENAN_AUTH_OPTIONS,
|
|
693
|
+
useFactory: async (optionsFactory) => {
|
|
694
|
+
return optionsFactory.createAuthOptions();
|
|
695
|
+
},
|
|
696
|
+
inject: [asyncOptions.useClass]
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
if (asyncOptions.useExisting) {
|
|
700
|
+
return {
|
|
701
|
+
provide: LENAN_AUTH_OPTIONS,
|
|
702
|
+
useFactory: async (optionsFactory) => {
|
|
703
|
+
return optionsFactory.createAuthOptions();
|
|
704
|
+
},
|
|
705
|
+
inject: [asyncOptions.useExisting]
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
throw new Error(
|
|
709
|
+
"Invalid async options: must provide useFactory, useClass, or useExisting"
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
LenanAuthModule = __decorateClass([
|
|
714
|
+
(0, import_common10.Module)({})
|
|
715
|
+
], LenanAuthModule);
|
|
716
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
717
|
+
0 && (module.exports = {
|
|
718
|
+
AuthController,
|
|
719
|
+
AuthService,
|
|
720
|
+
CurrentUser,
|
|
721
|
+
IS_PUBLIC_KEY,
|
|
722
|
+
JwtAuthGuard,
|
|
723
|
+
JwtStrategy,
|
|
724
|
+
JwtTokenService,
|
|
725
|
+
LENAN_AUTH_OPTIONS,
|
|
726
|
+
LENAN_USER_SERVICE,
|
|
727
|
+
LenanAuthModule,
|
|
728
|
+
LoginDto,
|
|
729
|
+
MessageResponseDto,
|
|
730
|
+
PasswordService,
|
|
731
|
+
Public,
|
|
732
|
+
RefreshTokenDto,
|
|
733
|
+
RefreshTokenGuard,
|
|
734
|
+
RefreshTokenStrategy,
|
|
735
|
+
RegisterDto,
|
|
736
|
+
TokensResponseDto
|
|
737
|
+
});
|
|
738
|
+
//# sourceMappingURL=index.cjs.map
|