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