nest-authme 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 (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +305 -0
  3. package/bin/cli.js +11 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +1619 -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 +42 -0
  11. package/dist/generator/templates/dto/change-password.dto.ts.hbs +22 -0
  12. package/dist/generator/templates/dto/create-user.dto.ts.hbs +38 -0
  13. package/dist/generator/templates/dto/forgot-password.dto.ts.hbs +13 -0
  14. package/dist/generator/templates/dto/login.dto.ts.hbs +21 -0
  15. package/dist/generator/templates/dto/register.dto.ts.hbs +33 -0
  16. package/dist/generator/templates/dto/reset-password.dto.ts.hbs +22 -0
  17. package/dist/generator/templates/entities/refresh-token.entity.typeorm.hbs +24 -0
  18. package/dist/generator/templates/entities/user.entity.typeorm.hbs +51 -0
  19. package/dist/generator/templates/jwt/auth.controller.ts.hbs +177 -0
  20. package/dist/generator/templates/jwt/auth.module.ts.hbs +81 -0
  21. package/dist/generator/templates/jwt/auth.service.ts.hbs +416 -0
  22. package/dist/generator/templates/jwt/jwt-auth.guard.ts.hbs +24 -0
  23. package/dist/generator/templates/jwt/jwt.strategy.ts.hbs +61 -0
  24. package/dist/generator/templates/jwt/local-auth.guard.ts.hbs +5 -0
  25. package/dist/generator/templates/jwt/local.strategy.ts.hbs +22 -0
  26. package/dist/generator/templates/prisma/prisma.module.ts.hbs +9 -0
  27. package/dist/generator/templates/prisma/prisma.service.ts.hbs +9 -0
  28. package/dist/generator/templates/prisma/schema.prisma.additions.hbs +40 -0
  29. package/dist/generator/templates/rbac/role.enum.ts.hbs +5 -0
  30. package/dist/generator/templates/rbac/roles.guard.ts.hbs +22 -0
  31. package/dist/generator/templates/shared/README.auth.md.hbs +306 -0
  32. package/dist/generator/templates/shared/env.hbs +36 -0
  33. package/dist/generator/templates/shared/env.template.hbs +36 -0
  34. package/dist/generator/templates/shared/main.ts.snippet.hbs +49 -0
  35. package/dist/generator/templates/tests/auth.controller.spec.ts.hbs +189 -0
  36. package/dist/generator/templates/tests/auth.service.spec.ts.hbs +334 -0
  37. package/dist/generator/templates/users/users.controller.ts.hbs +55 -0
  38. package/dist/generator/templates/users/users.module.ts.hbs +31 -0
  39. package/dist/generator/templates/users/users.service.ts.hbs +192 -0
  40. package/dist/index.d.ts +9 -0
  41. package/dist/index.js +1566 -0
  42. package/dist/index.js.map +1 -0
  43. package/package.json +65 -0
@@ -0,0 +1,61 @@
1
+ import { Injectable, UnauthorizedException } from '@nestjs/common';
2
+ import { PassportStrategy } from '@nestjs/passport';
3
+ import { ExtractJwt, Strategy } from 'passport-jwt';
4
+ import { ConfigService } from '@nestjs/config';
5
+ {{#if (eq orm "typeorm")}}
6
+ import { InjectRepository } from '@nestjs/typeorm';
7
+ import { Repository } from 'typeorm';
8
+ import { User } from '../../users/entities/user.entity';
9
+ {{else}}
10
+ import { UsersService } from '../../users/users.service';
11
+ {{/if}}
12
+
13
+ @Injectable()
14
+ export class JwtStrategy extends PassportStrategy(Strategy) {
15
+ constructor(
16
+ {{#if (eq orm "typeorm")}}
17
+ @InjectRepository(User)
18
+ private usersRepository: Repository<User>,
19
+ {{else}}
20
+ private usersService: UsersService,
21
+ {{/if}}
22
+ private configService: ConfigService,
23
+ ) {
24
+ const secret = configService.get<string>('JWT_SECRET');
25
+
26
+ if (!secret) {
27
+ throw new Error(
28
+ 'JWT_SECRET is not defined. Please add JWT_SECRET to your .env file.'
29
+ );
30
+ }
31
+
32
+ super({
33
+ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
34
+ ignoreExpiration: false,
35
+ secretOrKey: secret,
36
+ });
37
+ }
38
+
39
+ async validate(payload: any) {
40
+ {{#if (eq orm "typeorm")}}
41
+ const user = await this.usersRepository.findOne({
42
+ where: { id: payload.sub },
43
+ });
44
+ {{else}}
45
+ const user = await this.usersService.findById(payload.sub);
46
+ {{/if}}
47
+
48
+ if (!user) {
49
+ throw new UnauthorizedException();
50
+ }
51
+
52
+ // Return user object (will be attached to request.user)
53
+ return {
54
+ id: user.id,
55
+ email: user.email,
56
+ {{#if rbac.enabled}}
57
+ roles: user.roles,
58
+ {{/if}}
59
+ };
60
+ }
61
+ }
@@ -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,9 @@
1
+ import { Global, Module } from '@nestjs/common';
2
+ import { PrismaService } from './prisma.service';
3
+
4
+ @Global()
5
+ @Module({
6
+ providers: [PrismaService],
7
+ exports: [PrismaService],
8
+ })
9
+ export class PrismaModule {}
@@ -0,0 +1,9 @@
1
+ import { Injectable, OnModuleInit } from '@nestjs/common';
2
+ import { PrismaClient } from '@prisma/client';
3
+
4
+ @Injectable()
5
+ export class PrismaService extends PrismaClient implements OnModuleInit {
6
+ async onModuleInit() {
7
+ await this.$connect();
8
+ }
9
+ }
@@ -0,0 +1,40 @@
1
+ // ============================================================
2
+ // Add these models to your prisma/schema.prisma file
3
+ // Then run: npx prisma migrate dev --name add-auth-models
4
+ // ============================================================
5
+
6
+ model User {
7
+ id String @id @default(uuid())
8
+ email String @unique
9
+ {{#if features.useUsername}}
10
+ username String @unique
11
+ {{/if}}
12
+ password String
13
+ {{#if rbac.enabled}}
14
+ roles String[] @default(["User"])
15
+ {{/if}}
16
+ {{#if features.emailVerification}}
17
+ isEmailVerified Boolean @default(false)
18
+ emailVerificationToken String?
19
+ {{/if}}
20
+ {{#if features.resetPassword}}
21
+ passwordResetToken String?
22
+ passwordResetExpires DateTime?
23
+ {{/if}}
24
+ {{#if features.refreshTokens}}
25
+ refreshTokens RefreshToken[]
26
+ {{/if}}
27
+ createdAt DateTime @default(now())
28
+ updatedAt DateTime @updatedAt
29
+ }
30
+ {{#if features.refreshTokens}}
31
+
32
+ model RefreshToken {
33
+ id String @id @default(uuid())
34
+ token String @unique
35
+ userId String
36
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
37
+ expiresAt DateTime
38
+ createdAt DateTime @default(now())
39
+ }
40
+ {{/if}}
@@ -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,306 @@
1
+ # Authentication Module
2
+
3
+ Generated by **nest-authme** 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
+ ## Setup
125
+
126
+ ### Enable Global JWT Guard (Recommended)
127
+
128
+ To automatically protect all routes by default, add the JWT guard globally in your `main.ts`.
129
+
130
+ **📄 See `main.ts.example` in your project root for a complete setup example.**
131
+
132
+ Quick setup - add these lines to your `main.ts`:
133
+
134
+ ```typescript
135
+ import { Reflector } from '@nestjs/core';
136
+ import { JwtAuthGuard } from './auth/guards/jwt-auth.guard';
137
+
138
+ // In bootstrap() function:
139
+ const reflector = app.get(Reflector);
140
+ app.useGlobalGuards(new JwtAuthGuard(reflector));
141
+ ```
142
+
143
+ This makes all routes protected by default. Use `@Public()` to make specific routes public.
144
+
145
+ **Alternative:** Manually add `@UseGuards(JwtAuthGuard)` to each protected controller/route.
146
+
147
+ ## Usage
148
+
149
+ ### Protect Routes
150
+
151
+ By default (with global guard), all routes require authentication. Use the `@Public()` decorator to make routes public:
152
+
153
+ ```typescript
154
+ import { Public } from './auth/decorators/public.decorator';
155
+
156
+ @Public()
157
+ @Get('public')
158
+ getPublicData() {
159
+ return 'This endpoint is public';
160
+ }
161
+ ```
162
+
163
+ ### Get Current User
164
+
165
+ Use the `@CurrentUser()` decorator to access the authenticated user:
166
+
167
+ ```typescript
168
+ import { CurrentUser } from './auth/decorators/current-user.decorator';
169
+
170
+ @Get('me')
171
+ getMe(@CurrentUser() user: any) {
172
+ return user;
173
+ }
174
+ ```
175
+
176
+ {{#if rbac.enabled}}
177
+ ### Role-Based Access Control
178
+
179
+ Use the `@Roles()` decorator to restrict access by role:
180
+
181
+ ```typescript
182
+ import { Roles } from './auth/decorators/roles.decorator';
183
+ import { RolesGuard } from './auth/guards/roles.guard';
184
+
185
+ @UseGuards(JwtAuthGuard, RolesGuard)
186
+ @Roles('Admin')
187
+ @Get('admin-only')
188
+ adminOnlyRoute() {
189
+ return 'Only admins can see this';
190
+ }
191
+ ```
192
+
193
+ Available roles: {{#each rbac.roles}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}
194
+ {{/if}}
195
+
196
+ ## Environment Variables
197
+
198
+ Copy `.env.example` to `.env` and update the values:
199
+
200
+ ```bash
201
+ cp .env.example .env
202
+ ```
203
+
204
+ Required variables:
205
+ - `JWT_SECRET` - Secret key for JWT signing (auto-generated, but you can change it)
206
+ - `JWT_EXPIRES_IN` - Access token expiration (e.g., "1h", "15m")
207
+ {{#if features.refreshTokens}}
208
+ - `JWT_REFRESH_EXPIRES_IN` - Refresh token expiration (e.g., "7d")
209
+ {{/if}}
210
+ {{#if (eq orm "typeorm")}}
211
+ - `DATABASE_*` - Database connection details
212
+ {{/if}}
213
+
214
+ {{#if (eq orm "typeorm")}}
215
+ ## Database Setup
216
+
217
+ ### Create Migration
218
+
219
+ Generate a migration for the auth tables:
220
+
221
+ ```bash
222
+ npm run migration:generate -- src/migrations/CreateAuthTables
223
+ ```
224
+
225
+ ### Run Migration
226
+
227
+ Apply the migration:
228
+
229
+ ```bash
230
+ npm run migration:run
231
+ ```
232
+
233
+ ### Revert Migration
234
+
235
+ Rollback the migration:
236
+
237
+ ```bash
238
+ npm run migration:revert
239
+ ```
240
+ {{/if}}
241
+
242
+ ## Security Best Practices
243
+
244
+ 1. **Never commit .env file** - It contains sensitive secrets
245
+ 2. **Use strong JWT secrets** - At least 32 characters, random
246
+ 3. **HTTPS in production** - Always use HTTPS to protect tokens
247
+ 4. **Short token expiration** - Keep access tokens short-lived
248
+ {{#if features.refreshTokens}}
249
+ 5. **Rotate refresh tokens** - Refresh tokens are automatically rotated
250
+ {{/if}}
251
+ 6. **Rate limiting** - Add rate limiting to auth endpoints
252
+ 7. **Password requirements** - Enforce strong password policies
253
+
254
+ ## Testing
255
+
256
+ ### Register a User
257
+
258
+ ```bash
259
+ curl -X POST http://localhost:3000/auth/register \
260
+ -H "Content-Type: application/json" \
261
+ -d '{"email":"test@example.com","password":"Password123!"}'
262
+ ```
263
+
264
+ ### Login
265
+
266
+ ```bash
267
+ curl -X POST http://localhost:3000/auth/login \
268
+ -H "Content-Type: application/json" \
269
+ -d '{"email":"test@example.com","password":"Password123!"}'
270
+ ```
271
+
272
+ ### Access Protected Route
273
+
274
+ ```bash
275
+ curl http://localhost:3000/users/profile \
276
+ -H "Authorization: Bearer <your-access-token>"
277
+ ```
278
+
279
+ ## Troubleshooting
280
+
281
+ ### "JWT secret not found"
282
+ Make sure `.env` file exists and contains `JWT_SECRET`.
283
+
284
+ ### "Database connection failed"
285
+ Check your database is running and credentials in `.env` are correct.
286
+
287
+ ### "Invalid credentials"
288
+ Ensure email and password are correct. Passwords are case-sensitive.
289
+
290
+ {{#if (eq orm "typeorm")}}
291
+ ### "Entity not found"
292
+ Run migrations: `npm run migration:run`
293
+ {{/if}}
294
+
295
+ ## Documentation
296
+
297
+ - [NestJS Authentication](https://docs.nestjs.com/security/authentication)
298
+ - [Passport.js](http://www.passportjs.org/)
299
+ - [JWT](https://jwt.io/)
300
+ {{#if (eq orm "typeorm")}}
301
+ - [TypeORM](https://typeorm.io/)
302
+ {{/if}}
303
+
304
+ ---
305
+
306
+ Generated with ❤️ by [nest-authme](https://www.npmjs.com/package/nest-authme)
@@ -0,0 +1,36 @@
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
+ # Bcrypt
9
+ BCRYPT_ROUNDS=10
10
+
11
+ # Database Configuration
12
+ {{#if (eq orm "prisma")}}
13
+ DATABASE_URL=postgresql://postgres:postgres@localhost:5432/{{projectName}}?schema=public
14
+ {{else}}
15
+ {{#if (eq database "postgres")}}
16
+ DATABASE_HOST=localhost
17
+ DATABASE_PORT=5432
18
+ DATABASE_USER=postgres
19
+ DATABASE_PASSWORD=postgres
20
+ DATABASE_NAME={{projectName}}
21
+ {{/if}}
22
+ {{#if (eq database "mysql")}}
23
+ DATABASE_HOST=localhost
24
+ DATABASE_PORT=3306
25
+ DATABASE_USER=root
26
+ DATABASE_PASSWORD=root
27
+ DATABASE_NAME={{projectName}}
28
+ {{/if}}
29
+ {{#if (eq database "mongodb")}}
30
+ MONGODB_URI=mongodb://localhost:27017/{{projectName}}
31
+ {{/if}}
32
+ {{/if}}
33
+
34
+ # Application
35
+ NODE_ENV=development
36
+ PORT=3000
@@ -0,0 +1,36 @@
1
+ # JWT Configuration
2
+ JWT_SECRET=CHANGE_ME_GENERATE_WITH_openssl_rand_base64_32
3
+ JWT_EXPIRES_IN={{jwt.accessExpiration}}
4
+ {{#if features.refreshTokens}}
5
+ JWT_REFRESH_EXPIRES_IN={{jwt.refreshExpiration}}
6
+ {{/if}}
7
+
8
+ # Bcrypt
9
+ BCRYPT_ROUNDS=10
10
+
11
+ # Database Configuration
12
+ {{#if (eq orm "prisma")}}
13
+ DATABASE_URL=postgresql://postgres:postgres@localhost:5432/{{projectName}}?schema=public
14
+ {{else}}
15
+ {{#if (eq database "postgres")}}
16
+ DATABASE_HOST=localhost
17
+ DATABASE_PORT=5432
18
+ DATABASE_USER=postgres
19
+ DATABASE_PASSWORD=postgres
20
+ DATABASE_NAME={{projectName}}
21
+ {{/if}}
22
+ {{#if (eq database "mysql")}}
23
+ DATABASE_HOST=localhost
24
+ DATABASE_PORT=3306
25
+ DATABASE_USER=root
26
+ DATABASE_PASSWORD=root
27
+ DATABASE_NAME={{projectName}}
28
+ {{/if}}
29
+ {{#if (eq database "mongodb")}}
30
+ MONGODB_URI=mongodb://localhost:27017/{{projectName}}
31
+ {{/if}}
32
+ {{/if}}
33
+
34
+ # Application
35
+ NODE_ENV=development
36
+ PORT=3000
@@ -0,0 +1,49 @@
1
+ // Add this to your main.ts for global JWT authentication
2
+ // Location: src/main.ts
3
+
4
+ import { NestFactory, Reflector } from '@nestjs/core';
5
+ import { AppModule } from './app.module';
6
+ import { JwtAuthGuard } from './auth/guards/jwt-auth.guard';
7
+ import { ValidationPipe } from '@nestjs/common';
8
+ {{#if features.swagger}}
9
+ import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
10
+ {{/if}}
11
+
12
+ async function bootstrap() {
13
+ const app = await NestFactory.create(AppModule);
14
+
15
+ // Enable global validation pipe
16
+ app.useGlobalPipes(
17
+ new ValidationPipe({
18
+ whitelist: true,
19
+ forbidNonWhitelisted: true,
20
+ transform: true,
21
+ }),
22
+ );
23
+
24
+ // Enable global JWT guard (all routes protected by default)
25
+ const reflector = app.get(Reflector);
26
+ app.useGlobalGuards(new JwtAuthGuard(reflector));
27
+
28
+ {{#if features.swagger}}
29
+ // Swagger API documentation
30
+ const swaggerConfig = new DocumentBuilder()
31
+ .setTitle('API Documentation')
32
+ .setDescription('JWT Authentication API')
33
+ .setVersion('1.0')
34
+ .addBearerAuth()
35
+ .build();
36
+ const document = SwaggerModule.createDocument(app, swaggerConfig);
37
+ SwaggerModule.setup('api', app, document);
38
+
39
+ {{/if}}
40
+ // Enable CORS if needed
41
+ app.enableCors();
42
+
43
+ await app.listen(3000);
44
+ console.log('🚀 Application is running on: http://localhost:3000');
45
+ {{#if features.swagger}}
46
+ console.log('📚 Swagger docs: http://localhost:3000/api');
47
+ {{/if}}
48
+ }
49
+ bootstrap();