nest-authme 1.2.2 → 1.2.4

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.
@@ -1,5 +1,5 @@
1
1
  import { Injectable, UnauthorizedException{{#if features.resetPassword}}, BadRequestException{{/if}}{{#if features.emailVerification}}, NotFoundException{{/if}}{{#if features.accountLockout}}, ForbiddenException{{/if}} } from '@nestjs/common';
2
- {{#if (or features.emailVerification features.resetPassword)}}
2
+ {{#if (or features.emailVerification features.resetPassword features.refreshTokens)}}
3
3
  import * as crypto from 'crypto';
4
4
  {{/if}}
5
5
  import { JwtService } from '@nestjs/jwt';
@@ -57,7 +57,7 @@ export class AuthService {
57
57
  * Validate user credentials
58
58
  */
59
59
  async validateUser(email: string, password: string): Promise<any> {
60
- const user = await this.usersService.findByEmail(email);
60
+ const user = await this.usersService.findByEmail(email.toLowerCase());
61
61
  if (!user) {
62
62
  return null;
63
63
  }
@@ -152,7 +152,7 @@ export class AuthService {
152
152
  const hashedPassword = await bcrypt.hash(registerDto.password, bcryptRounds);
153
153
 
154
154
  const user = await this.usersService.create({
155
- email: registerDto.email,
155
+ email: registerDto.email.toLowerCase(),
156
156
  {{#if features.useUsername}}
157
157
  username: registerDto.username,
158
158
  {{/if}}
@@ -244,6 +244,10 @@ export class AuthService {
244
244
  * Verify email with token
245
245
  */
246
246
  async verifyEmail(token: string): Promise<{ message: string }> {
247
+ if (!token) {
248
+ throw new NotFoundException('Invalid verification token');
249
+ }
250
+
247
251
  const user = await this.usersService.findByVerificationToken(token);
248
252
  if (!user) {
249
253
  throw new NotFoundException('Invalid verification token');
@@ -261,7 +265,7 @@ export class AuthService {
261
265
  {{else}}
262
266
  async resendVerification(email: string): Promise<{ verificationToken: string; message: string }> {
263
267
  {{/if}}
264
- const user = await this.usersService.findByEmail(email);
268
+ const user = await this.usersService.findByEmail(email.toLowerCase());
265
269
  if (!user) {
266
270
  throw new NotFoundException('User not found');
267
271
  }
@@ -294,7 +298,7 @@ export class AuthService {
294
298
  */
295
299
  {{#if features.emailService}}
296
300
  async forgotPassword(forgotPasswordDto: ForgotPasswordDto): Promise<{ message: string }> {
297
- const user = await this.usersService.findByEmail(forgotPasswordDto.email);
301
+ const user = await this.usersService.findByEmail(forgotPasswordDto.email.toLowerCase());
298
302
  if (!user) {
299
303
  // Return success even if user not found to prevent email enumeration
300
304
  return { message: 'If the email exists, a password reset link has been sent' };
@@ -311,7 +315,7 @@ export class AuthService {
311
315
  }
312
316
  {{else}}
313
317
  async forgotPassword(forgotPasswordDto: ForgotPasswordDto): Promise<{ resetToken: string; message: string }> {
314
- const user = await this.usersService.findByEmail(forgotPasswordDto.email);
318
+ const user = await this.usersService.findByEmail(forgotPasswordDto.email.toLowerCase());
315
319
  if (!user) {
316
320
  // Return success even if user not found to prevent email enumeration
317
321
  return { resetToken: '', message: 'If the email exists, a reset token has been generated' };
@@ -356,7 +360,7 @@ export class AuthService {
356
360
  */
357
361
  private async createRefreshToken(userId: string): Promise<any> {
358
362
  const token = this.jwtService.sign(
359
- { sub: userId },
363
+ { sub: userId, jti: crypto.randomBytes(16).toString('hex') },
360
364
  { expiresIn: '{{jwt.refreshExpiration}}' }
361
365
  );
362
366
 
@@ -1,19 +1,25 @@
1
- import { Injectable } from '@nestjs/common';
1
+ import { Injectable, Logger } from '@nestjs/common';
2
2
  import { ConfigService } from '@nestjs/config';
3
3
  import * as nodemailer from 'nodemailer';
4
4
 
5
5
  @Injectable()
6
6
  export class MailService {
7
+ private readonly logger = new Logger(MailService.name);
7
8
  private transporter: nodemailer.Transporter;
9
+ private isConfigured: boolean;
8
10
 
9
11
  constructor(private configService: ConfigService) {
12
+ const smtpUser = this.configService.get('SMTP_USER', '');
13
+ const smtpPass = this.configService.get('SMTP_PASS', '');
14
+ this.isConfigured = !!(smtpUser && smtpPass);
15
+
10
16
  this.transporter = nodemailer.createTransport({
11
17
  host: this.configService.get('SMTP_HOST', 'smtp.ethereal.email'),
12
18
  port: parseInt(this.configService.get('SMTP_PORT', '587'), 10),
13
19
  secure: false,
14
20
  auth: {
15
- user: this.configService.get('SMTP_USER'),
16
- pass: this.configService.get('SMTP_PASS'),
21
+ user: smtpUser,
22
+ pass: smtpPass,
17
23
  },
18
24
  });
19
25
  }
@@ -33,17 +39,29 @@ export class MailService {
33
39
  async sendVerificationEmail(to: string, token: string): Promise<void> {
34
40
  const verifyUrl = `${this.appUrl}/auth/verify-email?token=${token}`;
35
41
 
36
- await this.transporter.sendMail({
37
- from: this.mailFrom,
38
- to,
39
- subject: 'Verify your email address',
40
- html: `
41
- <h2>Email Verification</h2>
42
- <p>Click the link below to verify your email address:</p>
43
- <p><a href="${verifyUrl}">${verifyUrl}</a></p>
44
- <p>If you did not create an account, please ignore this email.</p>
45
- `,
46
- });
42
+ if (!this.isConfigured) {
43
+ this.logger.warn(
44
+ `SMTP not configured. Verification email for ${to} not sent. Token: ${token}`,
45
+ );
46
+ this.logger.warn(`Verify URL: ${verifyUrl}`);
47
+ return;
48
+ }
49
+
50
+ try {
51
+ await this.transporter.sendMail({
52
+ from: this.mailFrom,
53
+ to,
54
+ subject: 'Verify your email address',
55
+ html: `
56
+ <h2>Email Verification</h2>
57
+ <p>Click the link below to verify your email address:</p>
58
+ <p><a href="${verifyUrl}">${verifyUrl}</a></p>
59
+ <p>If you did not create an account, please ignore this email.</p>
60
+ `,
61
+ });
62
+ } catch (error) {
63
+ this.logger.error(`Failed to send verification email to ${to}: ${error.message}`);
64
+ }
47
65
  }
48
66
  {{/if}}
49
67
 
@@ -54,18 +72,30 @@ export class MailService {
54
72
  async sendResetPasswordEmail(to: string, token: string): Promise<void> {
55
73
  const resetUrl = `${this.appUrl}/auth/reset-password?token=${token}`;
56
74
 
57
- await this.transporter.sendMail({
58
- from: this.mailFrom,
59
- to,
60
- subject: 'Reset your password',
61
- html: `
62
- <h2>Password Reset</h2>
63
- <p>Click the link below to reset your password:</p>
64
- <p><a href="${resetUrl}">${resetUrl}</a></p>
65
- <p>This link expires in 1 hour.</p>
66
- <p>If you did not request a password reset, please ignore this email.</p>
67
- `,
68
- });
75
+ if (!this.isConfigured) {
76
+ this.logger.warn(
77
+ `SMTP not configured. Reset email for ${to} not sent. Token: ${token}`,
78
+ );
79
+ this.logger.warn(`Reset URL: ${resetUrl}`);
80
+ return;
81
+ }
82
+
83
+ try {
84
+ await this.transporter.sendMail({
85
+ from: this.mailFrom,
86
+ to,
87
+ subject: 'Reset your password',
88
+ html: `
89
+ <h2>Password Reset</h2>
90
+ <p>Click the link below to reset your password:</p>
91
+ <p><a href="${resetUrl}">${resetUrl}</a></p>
92
+ <p>This link expires in 1 hour.</p>
93
+ <p>If you did not request a password reset, please ignore this email.</p>
94
+ `,
95
+ });
96
+ } catch (error) {
97
+ this.logger.error(`Failed to send reset email to ${to}: ${error.message}`);
98
+ }
69
99
  }
70
100
  {{/if}}
71
101
  }
@@ -333,7 +333,11 @@ describe('AuthService', () => {
333
333
 
334
334
  const result = await authService.resendVerification('test@example.com');
335
335
 
336
+ {{#if features.emailService}}
337
+ expect(result.message).toBeDefined();
338
+ {{else}}
336
339
  expect(result.verificationToken).toBeDefined();
340
+ {{/if}}
337
341
  expect(mockUsersService.setVerificationToken).toHaveBeenCalled();
338
342
  });
339
343
  });
@@ -347,7 +351,11 @@ describe('AuthService', () => {
347
351
 
348
352
  const result = await authService.forgotPassword({ email: 'test@example.com' });
349
353
 
354
+ {{#if features.emailService}}
355
+ expect(result.message).toBeDefined();
356
+ {{else}}
350
357
  expect(result.resetToken).toBeDefined();
358
+ {{/if}}
351
359
  expect(mockUsersService.setResetToken).toHaveBeenCalled();
352
360
  });
353
361
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nest-authme",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "Add production-ready authentication to any NestJS project in 60 seconds",
5
5
  "keywords": [
6
6
  "nestjs",