add-nest-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.
Files changed (33) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +368 -0
  3. package/bin/cli.js +11 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +1133 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/generator/templates/decorators/current-user.decorator.ts.hbs +8 -0
  8. package/dist/generator/templates/decorators/public.decorator.ts.hbs +4 -0
  9. package/dist/generator/templates/decorators/roles.decorator.ts.hbs +4 -0
  10. package/dist/generator/templates/dto/auth-response.dto.ts.hbs +13 -0
  11. package/dist/generator/templates/dto/create-user.dto.ts.hbs +17 -0
  12. package/dist/generator/templates/dto/login.dto.ts.hbs +12 -0
  13. package/dist/generator/templates/dto/register.dto.ts.hbs +13 -0
  14. package/dist/generator/templates/entities/refresh-token.entity.typeorm.hbs +24 -0
  15. package/dist/generator/templates/entities/user.entity.typeorm.hbs +30 -0
  16. package/dist/generator/templates/jwt/auth.controller.ts.hbs +34 -0
  17. package/dist/generator/templates/jwt/auth.module.ts.hbs +48 -0
  18. package/dist/generator/templates/jwt/auth.service.ts.hbs +193 -0
  19. package/dist/generator/templates/jwt/jwt-auth.guard.ts.hbs +24 -0
  20. package/dist/generator/templates/jwt/jwt.strategy.ts.hbs +52 -0
  21. package/dist/generator/templates/jwt/local-auth.guard.ts.hbs +5 -0
  22. package/dist/generator/templates/jwt/local.strategy.ts.hbs +22 -0
  23. package/dist/generator/templates/rbac/role.enum.ts.hbs +5 -0
  24. package/dist/generator/templates/rbac/roles.guard.ts.hbs +22 -0
  25. package/dist/generator/templates/shared/README.auth.md.hbs +283 -0
  26. package/dist/generator/templates/shared/env.template.hbs +29 -0
  27. package/dist/generator/templates/users/users.controller.ts.hbs +31 -0
  28. package/dist/generator/templates/users/users.module.ts.hbs +27 -0
  29. package/dist/generator/templates/users/users.service.ts.hbs +93 -0
  30. package/dist/index.d.ts +6 -0
  31. package/dist/index.js +1130 -0
  32. package/dist/index.js.map +1 -0
  33. package/package.json +62 -0
@@ -0,0 +1,5 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { AuthGuard } from '@nestjs/passport';
3
+
4
+ @Injectable()
5
+ export class LocalAuthGuard extends AuthGuard('local') {}
@@ -0,0 +1,22 @@
1
+ import { Injectable, UnauthorizedException } from '@nestjs/common';
2
+ import { PassportStrategy } from '@nestjs/passport';
3
+ import { Strategy } from 'passport-local';
4
+ import { AuthService } from '../auth.service';
5
+
6
+ @Injectable()
7
+ export class LocalStrategy extends PassportStrategy(Strategy) {
8
+ constructor(private authService: AuthService) {
9
+ super({
10
+ usernameField: 'email',
11
+ passwordField: 'password',
12
+ });
13
+ }
14
+
15
+ async validate(email: string, password: string): Promise<any> {
16
+ const user = await this.authService.validateUser(email, password);
17
+ if (!user) {
18
+ throw new UnauthorizedException('Invalid credentials');
19
+ }
20
+ return user;
21
+ }
22
+ }
@@ -0,0 +1,5 @@
1
+ export enum Role {
2
+ {{#each rbac.roles}}
3
+ {{uppercase this}} = '{{this}}',
4
+ {{/each}}
5
+ }
@@ -0,0 +1,22 @@
1
+ import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
2
+ import { Reflector } from '@nestjs/core';
3
+ import { ROLES_KEY } from '../decorators/roles.decorator';
4
+
5
+ @Injectable()
6
+ export class RolesGuard implements CanActivate {
7
+ constructor(private reflector: Reflector) {}
8
+
9
+ canActivate(context: ExecutionContext): boolean {
10
+ const requiredRoles = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [
11
+ context.getHandler(),
12
+ context.getClass(),
13
+ ]);
14
+
15
+ if (!requiredRoles) {
16
+ return true;
17
+ }
18
+
19
+ const { user } = context.switchToHttp().getRequest();
20
+ return requiredRoles.some((role) => user.roles?.includes(role));
21
+ }
22
+ }
@@ -0,0 +1,283 @@
1
+ # Authentication Module
2
+
3
+ Generated by **add-nest-auth** on {{timestamp}}
4
+
5
+ ## Overview
6
+
7
+ This authentication module provides:
8
+ - ✅ JWT-based authentication
9
+ {{#if rbac.enabled}}
10
+ - ✅ Role-Based Access Control (RBAC)
11
+ {{/if}}
12
+ {{#if features.refreshTokens}}
13
+ - ✅ Refresh token rotation
14
+ {{/if}}
15
+ - ✅ Password hashing with bcrypt
16
+ - ✅ Request validation with class-validator
17
+ {{#if (eq orm "typeorm")}}
18
+ - ✅ TypeORM integration
19
+ {{/if}}
20
+
21
+ ## Endpoints
22
+
23
+ ### Public Endpoints (No authentication required)
24
+
25
+ #### POST /auth/register
26
+ Register a new user.
27
+
28
+ **Request body:**
29
+ ```json
30
+ {
31
+ "email": "user@example.com",
32
+ "password": "your-password"
33
+ }
34
+ ```
35
+
36
+ **Response:**
37
+ ```json
38
+ {
39
+ "accessToken": "eyJhbGciOiJIUzI1NiIs...",
40
+ {{#if features.refreshTokens}}
41
+ "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
42
+ {{/if}}
43
+ "user": {
44
+ "id": "uuid",
45
+ "email": "user@example.com"{{#if rbac.enabled}},
46
+ "roles": ["User"]{{/if}}
47
+ }
48
+ }
49
+ ```
50
+
51
+ #### POST /auth/login
52
+ Login with existing credentials.
53
+
54
+ **Request body:**
55
+ ```json
56
+ {
57
+ "email": "user@example.com",
58
+ "password": "your-password"
59
+ }
60
+ ```
61
+
62
+ **Response:** Same as register
63
+
64
+ {{#if features.refreshTokens}}
65
+ #### POST /auth/refresh
66
+ Refresh access token using refresh token.
67
+
68
+ **Request body:**
69
+ ```json
70
+ {
71
+ "refreshToken": "eyJhbGciOiJIUzI1NiIs..."
72
+ }
73
+ ```
74
+
75
+ **Response:**
76
+ ```json
77
+ {
78
+ "accessToken": "eyJhbGciOiJIUzI1NiIs..."
79
+ }
80
+ ```
81
+ {{/if}}
82
+
83
+ ### Protected Endpoints (Authentication required)
84
+
85
+ #### GET /users/profile
86
+ Get current user profile.
87
+
88
+ **Headers:**
89
+ ```
90
+ Authorization: Bearer <accessToken>
91
+ ```
92
+
93
+ **Response:**
94
+ ```json
95
+ {
96
+ "id": "uuid",
97
+ "email": "user@example.com"{{#if rbac.enabled}},
98
+ "roles": ["User"]{{/if}}
99
+ }
100
+ ```
101
+
102
+ {{#if rbac.enabled}}
103
+ #### GET /users (Admin only)
104
+ Get all users.
105
+
106
+ **Headers:**
107
+ ```
108
+ Authorization: Bearer <accessToken>
109
+ ```
110
+
111
+ **Response:**
112
+ ```json
113
+ [
114
+ {
115
+ "id": "uuid",
116
+ "email": "user@example.com",
117
+ "roles": ["User"],
118
+ "createdAt": "2026-01-01T00:00:00.000Z"
119
+ }
120
+ ]
121
+ ```
122
+ {{/if}}
123
+
124
+ ## Usage
125
+
126
+ ### Protect Routes
127
+
128
+ By default, all routes require authentication. Use the `@Public()` decorator to make routes public:
129
+
130
+ ```typescript
131
+ import { Public } from './auth/decorators/public.decorator';
132
+
133
+ @Public()
134
+ @Get('public')
135
+ getPublicData() {
136
+ return 'This endpoint is public';
137
+ }
138
+ ```
139
+
140
+ ### Get Current User
141
+
142
+ Use the `@CurrentUser()` decorator to access the authenticated user:
143
+
144
+ ```typescript
145
+ import { CurrentUser } from './auth/decorators/current-user.decorator';
146
+
147
+ @Get('me')
148
+ getMe(@CurrentUser() user: any) {
149
+ return user;
150
+ }
151
+ ```
152
+
153
+ {{#if rbac.enabled}}
154
+ ### Role-Based Access Control
155
+
156
+ Use the `@Roles()` decorator to restrict access by role:
157
+
158
+ ```typescript
159
+ import { Roles } from './auth/decorators/roles.decorator';
160
+ import { RolesGuard } from './auth/guards/roles.guard';
161
+
162
+ @UseGuards(JwtAuthGuard, RolesGuard)
163
+ @Roles('Admin')
164
+ @Get('admin-only')
165
+ adminOnlyRoute() {
166
+ return 'Only admins can see this';
167
+ }
168
+ ```
169
+
170
+ Available roles: {{#each rbac.roles}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}
171
+ {{/if}}
172
+
173
+ ## Environment Variables
174
+
175
+ Copy `.env.example` to `.env` and update the values:
176
+
177
+ ```bash
178
+ cp .env.example .env
179
+ ```
180
+
181
+ Required variables:
182
+ - `JWT_SECRET` - Secret key for JWT signing (auto-generated, but you can change it)
183
+ - `JWT_EXPIRES_IN` - Access token expiration (e.g., "1h", "15m")
184
+ {{#if features.refreshTokens}}
185
+ - `JWT_REFRESH_EXPIRES_IN` - Refresh token expiration (e.g., "7d")
186
+ {{/if}}
187
+ {{#if (eq orm "typeorm")}}
188
+ - `DATABASE_*` - Database connection details
189
+ {{/if}}
190
+
191
+ {{#if (eq orm "typeorm")}}
192
+ ## Database Setup
193
+
194
+ ### Create Migration
195
+
196
+ Generate a migration for the auth tables:
197
+
198
+ ```bash
199
+ npm run migration:generate -- src/migrations/CreateAuthTables
200
+ ```
201
+
202
+ ### Run Migration
203
+
204
+ Apply the migration:
205
+
206
+ ```bash
207
+ npm run migration:run
208
+ ```
209
+
210
+ ### Revert Migration
211
+
212
+ Rollback the migration:
213
+
214
+ ```bash
215
+ npm run migration:revert
216
+ ```
217
+ {{/if}}
218
+
219
+ ## Security Best Practices
220
+
221
+ 1. **Never commit .env file** - It contains sensitive secrets
222
+ 2. **Use strong JWT secrets** - At least 32 characters, random
223
+ 3. **HTTPS in production** - Always use HTTPS to protect tokens
224
+ 4. **Short token expiration** - Keep access tokens short-lived
225
+ {{#if features.refreshTokens}}
226
+ 5. **Rotate refresh tokens** - Refresh tokens are automatically rotated
227
+ {{/if}}
228
+ 6. **Rate limiting** - Add rate limiting to auth endpoints
229
+ 7. **Password requirements** - Enforce strong password policies
230
+
231
+ ## Testing
232
+
233
+ ### Register a User
234
+
235
+ ```bash
236
+ curl -X POST http://localhost:3000/auth/register \
237
+ -H "Content-Type: application/json" \
238
+ -d '{"email":"test@example.com","password":"Password123!"}'
239
+ ```
240
+
241
+ ### Login
242
+
243
+ ```bash
244
+ curl -X POST http://localhost:3000/auth/login \
245
+ -H "Content-Type: application/json" \
246
+ -d '{"email":"test@example.com","password":"Password123!"}'
247
+ ```
248
+
249
+ ### Access Protected Route
250
+
251
+ ```bash
252
+ curl http://localhost:3000/users/profile \
253
+ -H "Authorization: Bearer <your-access-token>"
254
+ ```
255
+
256
+ ## Troubleshooting
257
+
258
+ ### "JWT secret not found"
259
+ Make sure `.env` file exists and contains `JWT_SECRET`.
260
+
261
+ ### "Database connection failed"
262
+ Check your database is running and credentials in `.env` are correct.
263
+
264
+ ### "Invalid credentials"
265
+ Ensure email and password are correct. Passwords are case-sensitive.
266
+
267
+ {{#if (eq orm "typeorm")}}
268
+ ### "Entity not found"
269
+ Run migrations: `npm run migration:run`
270
+ {{/if}}
271
+
272
+ ## Documentation
273
+
274
+ - [NestJS Authentication](https://docs.nestjs.com/security/authentication)
275
+ - [Passport.js](http://www.passportjs.org/)
276
+ - [JWT](https://jwt.io/)
277
+ {{#if (eq orm "typeorm")}}
278
+ - [TypeORM](https://typeorm.io/)
279
+ {{/if}}
280
+
281
+ ---
282
+
283
+ Generated with ❤️ by [add-nest-auth](https://github.com/yourusername/add-nest-auth)
@@ -0,0 +1,29 @@
1
+ # JWT Configuration
2
+ JWT_SECRET={{jwt.secret}}
3
+ JWT_EXPIRES_IN={{jwt.accessExpiration}}
4
+ {{#if features.refreshTokens}}
5
+ JWT_REFRESH_EXPIRES_IN={{jwt.refreshExpiration}}
6
+ {{/if}}
7
+
8
+ # Database Configuration
9
+ {{#if (eq database "postgres")}}
10
+ DATABASE_HOST=localhost
11
+ DATABASE_PORT=5432
12
+ DATABASE_USER=postgres
13
+ DATABASE_PASSWORD=postgres
14
+ DATABASE_NAME={{projectName}}
15
+ {{/if}}
16
+ {{#if (eq database "mysql")}}
17
+ DATABASE_HOST=localhost
18
+ DATABASE_PORT=3306
19
+ DATABASE_USER=root
20
+ DATABASE_PASSWORD=root
21
+ DATABASE_NAME={{projectName}}
22
+ {{/if}}
23
+ {{#if (eq database "mongodb")}}
24
+ MONGODB_URI=mongodb://localhost:27017/{{projectName}}
25
+ {{/if}}
26
+
27
+ # Application
28
+ NODE_ENV=development
29
+ PORT=3000
@@ -0,0 +1,31 @@
1
+ import { Controller, Get{{#if rbac.enabled}}, UseGuards{{/if}} } from '@nestjs/common';
2
+ {{#if rbac.enabled}}
3
+ import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
4
+ import { RolesGuard } from '../auth/guards/roles.guard';
5
+ import { Roles } from '../auth/decorators/roles.decorator';
6
+ {{/if}}
7
+ import { CurrentUser } from '../auth/decorators/current-user.decorator';
8
+ import { UsersService } from './users.service';
9
+
10
+ @Controller('users')
11
+ {{#if rbac.enabled}}
12
+ @UseGuards(JwtAuthGuard, RolesGuard)
13
+ {{else}}
14
+ @UseGuards(JwtAuthGuard)
15
+ {{/if}}
16
+ export class UsersController {
17
+ constructor(private readonly usersService: UsersService) {}
18
+
19
+ @Get('profile')
20
+ getProfile(@CurrentUser() user: any) {
21
+ return user;
22
+ }
23
+
24
+ {{#if rbac.enabled}}
25
+ @Get()
26
+ @Roles('Admin')
27
+ findAll() {
28
+ return this.usersService.findAll();
29
+ }
30
+ {{/if}}
31
+ }
@@ -0,0 +1,27 @@
1
+ import { Module } from '@nestjs/common';
2
+ {{#if (eq orm "typeorm")}}
3
+ import { TypeOrmModule } from '@nestjs/typeorm';
4
+ import { User } from './entities/user.entity';
5
+ {{#if features.refreshTokens}}
6
+ import { RefreshToken } from './entities/refresh-token.entity';
7
+ {{/if}}
8
+ {{/if}}
9
+ import { UsersService } from './users.service';
10
+ import { UsersController } from './users.controller';
11
+
12
+ @Module({
13
+ {{#if (eq orm "typeorm")}}
14
+ imports: [
15
+ TypeOrmModule.forFeature([
16
+ User,
17
+ {{#if features.refreshTokens}}
18
+ RefreshToken,
19
+ {{/if}}
20
+ ]),
21
+ ],
22
+ {{/if}}
23
+ controllers: [UsersController],
24
+ providers: [UsersService],
25
+ exports: [UsersService],
26
+ })
27
+ export class UsersModule {}
@@ -0,0 +1,93 @@
1
+ import { Injectable, ConflictException, NotFoundException } from '@nestjs/common';
2
+ {{#if (eq orm "typeorm")}}
3
+ import { InjectRepository } from '@nestjs/typeorm';
4
+ import { Repository } from 'typeorm';
5
+ import { User } from './entities/user.entity';
6
+ {{/if}}
7
+ import { CreateUserDto } from '../auth/dto/create-user.dto';
8
+
9
+ @Injectable()
10
+ export class UsersService {
11
+ {{#if (eq orm "typeorm")}}
12
+ constructor(
13
+ @InjectRepository(User)
14
+ private usersRepository: Repository<User>,
15
+ ) {}
16
+
17
+ /**
18
+ * Create a new user
19
+ */
20
+ async create(createUserDto: CreateUserDto): Promise<User> {
21
+ // Check if user already exists
22
+ const existingUser = await this.usersRepository.findOne({
23
+ where: { email: createUserDto.email },
24
+ });
25
+
26
+ if (existingUser) {
27
+ throw new ConflictException('User with this email already exists');
28
+ }
29
+
30
+ const user = this.usersRepository.create(createUserDto);
31
+ return await this.usersRepository.save(user);
32
+ }
33
+
34
+ /**
35
+ * Find user by email
36
+ */
37
+ async findByEmail(email: string): Promise<User | null> {
38
+ return await this.usersRepository.findOne({
39
+ where: { email },
40
+ });
41
+ }
42
+
43
+ /**
44
+ * Find user by ID
45
+ */
46
+ async findById(id: string): Promise<User | null> {
47
+ return await this.usersRepository.findOne({
48
+ where: { id },
49
+ });
50
+ }
51
+
52
+ /**
53
+ * Find all users
54
+ */
55
+ async findAll(): Promise<User[]> {
56
+ return await this.usersRepository.find({
57
+ select: ['id', 'email'{{#if rbac.enabled}}, 'roles'{{/if}}, 'createdAt'],
58
+ });
59
+ }
60
+ {{else}}
61
+ /**
62
+ * Create a new user
63
+ * TODO: Implement with your ORM
64
+ */
65
+ async create(createUserDto: CreateUserDto): Promise<any> {
66
+ throw new Error('Not implemented');
67
+ }
68
+
69
+ /**
70
+ * Find user by email
71
+ * TODO: Implement with your ORM
72
+ */
73
+ async findByEmail(email: string): Promise<any> {
74
+ throw new Error('Not implemented');
75
+ }
76
+
77
+ /**
78
+ * Find user by ID
79
+ * TODO: Implement with your ORM
80
+ */
81
+ async findById(id: string): Promise<any> {
82
+ throw new Error('Not implemented');
83
+ }
84
+
85
+ /**
86
+ * Find all users
87
+ * TODO: Implement with your ORM
88
+ */
89
+ async findAll(): Promise<any[]> {
90
+ throw new Error('Not implemented');
91
+ }
92
+ {{/if}}
93
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Main CLI orchestrator
3
+ */
4
+ declare function run(cwd?: string): Promise<void>;
5
+
6
+ export { run };