cca-auth-module 0.1.86 → 0.1.88
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 +341 -92
- package/dist/index.js +66 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +66 -6
- package/dist/index.mjs.map +1 -1
- package/dist/presentation/constants/constants.d.ts +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,131 +8,380 @@ This module provides endpoints and methods for user authentication, including lo
|
|
|
8
8
|
|
|
9
9
|
## Endpoints
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- **URL:** `/auth/login`
|
|
14
|
-
- **Method:** `POST`
|
|
15
|
-
- **Description:** Authenticates a user and returns tokens or authentication data.
|
|
16
|
-
- **Body Parameters:**
|
|
17
|
-
- `adminPassword` (optional, string): An additional password if admin access is required.
|
|
18
|
-
- Other properties as defined in `LoginDTO` (e.g., email, password, etc.).
|
|
19
|
-
- **Success Response:**
|
|
20
|
-
- **Code:** `201 Created`
|
|
21
|
-
- **Content:** JSON object with authentication result (tokens, user details, etc.).
|
|
11
|
+
# Auth API Documentation
|
|
22
12
|
|
|
23
13
|
---
|
|
24
14
|
|
|
25
|
-
|
|
15
|
+
## 1. Login
|
|
26
16
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
17
|
+
**POST** `/auth/login`
|
|
18
|
+
|
|
19
|
+
### Request Body
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"email": "user@example.com",
|
|
24
|
+
"password": "yourPassword"
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Success Response
|
|
29
|
+
|
|
30
|
+
**Status:** `200 OK`
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"success": true,
|
|
35
|
+
"message": "Login successful",
|
|
36
|
+
"data": {
|
|
37
|
+
"accessToken": "string",
|
|
38
|
+
"userId": "string",
|
|
39
|
+
"expiresAt": "2025-08-06T12:00:00.000Z",
|
|
40
|
+
"auth": {
|
|
41
|
+
"hasAccessToken": true,
|
|
42
|
+
"enable": false,
|
|
43
|
+
"status": "BASIC_AUTH",
|
|
44
|
+
"verified": false
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"meta": {
|
|
48
|
+
"timestamp": "2025-08-06T11:59:59.999Z"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 2. Admin Login
|
|
56
|
+
|
|
57
|
+
**POST** `/auth/admin-login`
|
|
58
|
+
|
|
59
|
+
### Request Body
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"email": "admin@example.com",
|
|
64
|
+
"password": "adminPassword",
|
|
65
|
+
"adminPassword": "superSecretAdminPassword"
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Success Response
|
|
70
|
+
|
|
71
|
+
**Status:** `201 Created`
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"success": true,
|
|
76
|
+
"message": "Admin login successful",
|
|
77
|
+
"data": {
|
|
78
|
+
"message": "Admin authenticated",
|
|
79
|
+
"auth": {
|
|
80
|
+
"hasAccessToken": true,
|
|
81
|
+
"enable": false,
|
|
82
|
+
"status": "BASIC_AUTH",
|
|
83
|
+
"verified": false
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 3. Logout
|
|
92
|
+
|
|
93
|
+
**POST** `/auth/logout`
|
|
94
|
+
|
|
95
|
+
### Request Body
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"id": "userId"
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Success Response
|
|
104
|
+
|
|
105
|
+
**Status:** `200 OK`
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"success": true,
|
|
110
|
+
"message": "Logout successful",
|
|
111
|
+
"data": {
|
|
112
|
+
"auth": {
|
|
113
|
+
"hasAccessToken": false,
|
|
114
|
+
"enable": false,
|
|
115
|
+
"status": "LOGGED_OUT",
|
|
116
|
+
"verified": false
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 4. Register
|
|
125
|
+
|
|
126
|
+
**POST** `/auth/register`
|
|
127
|
+
|
|
128
|
+
### Request Body
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"email": "newuser@example.com",
|
|
133
|
+
"name": "New User",
|
|
134
|
+
"password": "userPassword",
|
|
135
|
+
"role": "user",
|
|
136
|
+
"adminPassword": "optionalAdminPassword"
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Success Response
|
|
141
|
+
|
|
142
|
+
**Status:** `200 OK`
|
|
143
|
+
|
|
144
|
+
```json
|
|
145
|
+
{
|
|
146
|
+
"success": true,
|
|
147
|
+
"message": "Registration successful",
|
|
148
|
+
"data": {
|
|
149
|
+
"auth": {
|
|
150
|
+
"hasAccessToken": false,
|
|
151
|
+
"enable": false,
|
|
152
|
+
"status": "REGISTERED",
|
|
153
|
+
"verified": false
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
"meta": {
|
|
157
|
+
"status": true
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
38
161
|
|
|
39
162
|
---
|
|
40
163
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
164
|
+
## 5. Refresh Token
|
|
165
|
+
|
|
166
|
+
**POST** `/auth/refresh-token`
|
|
167
|
+
|
|
168
|
+
### Request Body
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"refreshToken": "yourRefreshToken"
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Success Response
|
|
177
|
+
|
|
178
|
+
**Status:** `200 OK`
|
|
179
|
+
|
|
180
|
+
```json
|
|
181
|
+
{
|
|
182
|
+
"success": true,
|
|
183
|
+
"message": "Token refreshed successfully",
|
|
184
|
+
"data": {
|
|
185
|
+
"accessToken": "newAccessToken",
|
|
186
|
+
"refreshToken": "newRefreshToken",
|
|
187
|
+
"auth": {
|
|
188
|
+
"hasAccessToken": true,
|
|
189
|
+
"enable": false,
|
|
190
|
+
"status": "BASIC_AUTH",
|
|
191
|
+
"verified": false
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
56
196
|
|
|
57
197
|
---
|
|
58
198
|
|
|
59
|
-
|
|
199
|
+
## 6. 2FA Setup
|
|
200
|
+
|
|
201
|
+
**POST** `/auth/2fa/setup`
|
|
60
202
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
203
|
+
### Headers
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
Authorization: Bearer <accessToken>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Request Body
|
|
210
|
+
|
|
211
|
+
None
|
|
212
|
+
|
|
213
|
+
### Success Response
|
|
214
|
+
|
|
215
|
+
**Status:** `200 OK`
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"success": true,
|
|
220
|
+
"message": "2FA setup successful",
|
|
221
|
+
"data": {
|
|
222
|
+
"qrCode": "data:image/png;base64,...",
|
|
223
|
+
"auth": {
|
|
224
|
+
"hasAccessToken": true,
|
|
225
|
+
"enable": false,
|
|
226
|
+
"status": "NEEDS_SETUP"
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
"meta": {
|
|
230
|
+
"nextStep": "Scan the QR code and enter your first code to verify",
|
|
231
|
+
"redirectTo": "/2fa-enable"
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
```
|
|
69
235
|
|
|
70
236
|
---
|
|
71
237
|
|
|
72
|
-
|
|
238
|
+
## 7. Enable 2FA
|
|
239
|
+
|
|
240
|
+
**POST** `/auth/2fa/enable`
|
|
73
241
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
242
|
+
### Headers
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
Authorization: Bearer <accessToken>
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Request Body
|
|
249
|
+
|
|
250
|
+
```json
|
|
251
|
+
{
|
|
252
|
+
"userId": "userId",
|
|
253
|
+
"token": "2faToken"
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Success Response
|
|
258
|
+
|
|
259
|
+
**Status:** `200 OK`
|
|
260
|
+
|
|
261
|
+
```json
|
|
262
|
+
{
|
|
263
|
+
"success": true,
|
|
264
|
+
"message": "2FA enabled successfully",
|
|
265
|
+
"data": {
|
|
266
|
+
"isEnabled": true,
|
|
267
|
+
"enabledAt": "2025-08-06T12:00:00.000Z",
|
|
268
|
+
"auth": {
|
|
269
|
+
"hasAccessToken": true,
|
|
270
|
+
"enable": true,
|
|
271
|
+
"status": "PENDING_VERIFICATION"
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
"meta": {
|
|
275
|
+
"nextStep": "Proceed to verify with a valid 2FA token",
|
|
276
|
+
"redirectTo": "/verify-2fa"
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
```
|
|
80
280
|
|
|
81
281
|
---
|
|
82
282
|
|
|
83
|
-
##
|
|
283
|
+
## 8. Verify 2FA
|
|
84
284
|
|
|
85
|
-
|
|
285
|
+
**POST** `/auth/2fa/verify`
|
|
86
286
|
|
|
87
|
-
|
|
88
|
-
import express from 'express';
|
|
89
|
-
import { AuthController, authConfig, createAuthContainer } from 'path-to-your-auth-module';
|
|
90
|
-
import { ConfigSource } from 'path-to-your-domain-interfaces';
|
|
287
|
+
### Request Body
|
|
91
288
|
|
|
92
|
-
|
|
93
|
-
|
|
289
|
+
```json
|
|
290
|
+
{
|
|
291
|
+
"userId": "userId",
|
|
292
|
+
"token": "2faVerificationToken"
|
|
293
|
+
}
|
|
294
|
+
```
|
|
94
295
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
296
|
+
### Success Response
|
|
297
|
+
|
|
298
|
+
**Status:** `200 OK`
|
|
299
|
+
|
|
300
|
+
```json
|
|
301
|
+
{
|
|
302
|
+
"success": true,
|
|
303
|
+
"message": "2FA verification successful",
|
|
304
|
+
"data": {
|
|
305
|
+
"token": "accessToken",
|
|
306
|
+
"refreshToken": "refreshToken",
|
|
307
|
+
"user": {
|
|
308
|
+
"id": "userId",
|
|
309
|
+
"email": "user@example.com",
|
|
310
|
+
"name": "User Name",
|
|
311
|
+
"role": "user"
|
|
312
|
+
},
|
|
313
|
+
"auth": {
|
|
314
|
+
"hasAccessToken": true,
|
|
315
|
+
"enable": true,
|
|
316
|
+
"status": "FULL_AUTH",
|
|
317
|
+
"verified": true
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
"meta": {
|
|
321
|
+
"recommendation": "You're fully authenticated",
|
|
322
|
+
"redirectTo": "/"
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
```
|
|
103
326
|
|
|
104
|
-
|
|
105
|
-
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## 9. Disable 2FA
|
|
106
330
|
|
|
107
|
-
|
|
108
|
-
const container = createAuthContainer();
|
|
109
|
-
const authController: AuthController = container.resolve(AuthController);
|
|
331
|
+
**POST** `/auth/2fa/disable`
|
|
110
332
|
|
|
111
|
-
|
|
112
|
-
app.post('/auth/login', authController.login);
|
|
113
|
-
app.post('/auth/logout/:id', authController.logout);
|
|
114
|
-
app.post('/auth/register', authController.register);
|
|
115
|
-
app.post('/auth/refresh-token', authController.refreshToken);
|
|
333
|
+
### Headers
|
|
116
334
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
335
|
+
```
|
|
336
|
+
Authorization: Bearer <accessToken>
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Request Body
|
|
340
|
+
|
|
341
|
+
```json
|
|
342
|
+
{
|
|
343
|
+
"token": "current2faToken"
|
|
344
|
+
}
|
|
345
|
+
```
|
|
121
346
|
|
|
122
|
-
|
|
347
|
+
### Success Response
|
|
348
|
+
|
|
349
|
+
**Status:** `200 OK`
|
|
350
|
+
|
|
351
|
+
```json
|
|
352
|
+
{
|
|
353
|
+
"success": true,
|
|
354
|
+
"message": "2FA disabled successfully",
|
|
355
|
+
"data": {
|
|
356
|
+
"disabledAt": "2025-08-06T12:00:00.000Z",
|
|
357
|
+
"auth": {
|
|
358
|
+
"hasAccessToken": true,
|
|
359
|
+
"enable": false,
|
|
360
|
+
"status": "BASIC_AUTH",
|
|
361
|
+
"verified": false
|
|
362
|
+
}
|
|
363
|
+
},
|
|
364
|
+
"meta": {
|
|
365
|
+
"securityNote": "Account now relies only on password. Re-enable 2FA for better security.",
|
|
366
|
+
"redirectTo": "/login"
|
|
367
|
+
}
|
|
368
|
+
}
|
|
123
369
|
```
|
|
124
370
|
|
|
125
371
|
---
|
|
126
372
|
|
|
127
|
-
##
|
|
373
|
+
## Notes
|
|
128
374
|
|
|
129
|
-
|
|
375
|
+
- All endpoints returning tokens require secure HTTPS connections.
|
|
376
|
+
- 2FA setup, enable, verify, and disable require authentication with a valid access token (sent in the Authorization header).
|
|
377
|
+
- Request bodies use DTOs as defined in the controller (LoginDTO, RegisterDTO, ITwoFactorEnable, ITwoFactorVerify, etc.).
|
|
378
|
+
- Error responses will follow the format:
|
|
130
379
|
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
380
|
+
```json
|
|
381
|
+
{
|
|
382
|
+
"success": false,
|
|
383
|
+
"error": "Error message"
|
|
384
|
+
}
|
|
136
385
|
```
|
|
137
386
|
|
|
138
387
|
---
|
package/dist/index.js
CHANGED
|
@@ -741,7 +741,10 @@ var AUTH_STATUS = {
|
|
|
741
741
|
BASIC_AUTH: "basic_auth",
|
|
742
742
|
NEEDS_SETUP: "needs_setup",
|
|
743
743
|
PENDING_VERIFICATION: "pending_verification",
|
|
744
|
-
FULL_AUTH: "full_auth"
|
|
744
|
+
FULL_AUTH: "full_auth",
|
|
745
|
+
LOGGED_OUT: "logged_out",
|
|
746
|
+
REGISTERED: "registered",
|
|
747
|
+
TOKEN_REFRESHED: "token_refreshed"
|
|
745
748
|
};
|
|
746
749
|
var MESSAGES = {
|
|
747
750
|
LOGIN_SUCCESS: "Login successful",
|
|
@@ -762,11 +765,20 @@ var _AuthController = class _AuthController {
|
|
|
762
765
|
try {
|
|
763
766
|
const loginDTO = req.body;
|
|
764
767
|
const result = await this.loginUseCase.execute(loginDTO);
|
|
768
|
+
const twoFactorEnabled = result.enabled ?? false;
|
|
765
769
|
const loginData = {
|
|
766
770
|
accessToken: result.accessToken,
|
|
767
771
|
userId: result.id,
|
|
768
772
|
expiresAt: result.expiresAt,
|
|
769
|
-
|
|
773
|
+
auth: this.createAuthData(
|
|
774
|
+
true,
|
|
775
|
+
// hasAccessToken
|
|
776
|
+
twoFactorEnabled,
|
|
777
|
+
// enable (2FA enabled status)
|
|
778
|
+
twoFactorEnabled ? AUTH_STATUS.PENDING_VERIFICATION : AUTH_STATUS.BASIC_AUTH,
|
|
779
|
+
false
|
|
780
|
+
// verified - always false for basic login
|
|
781
|
+
)
|
|
770
782
|
};
|
|
771
783
|
this.sendResponse(res, HTTP_STATUS.OK, MESSAGES.LOGIN_SUCCESS, loginData);
|
|
772
784
|
} catch (error) {
|
|
@@ -780,7 +792,20 @@ var _AuthController = class _AuthController {
|
|
|
780
792
|
throw new ForbiddenError("Admin password is required");
|
|
781
793
|
}
|
|
782
794
|
const result = await this.adminLoginUseCase.execute(loginDTO, adminPassword);
|
|
783
|
-
|
|
795
|
+
const adminLoginData = {
|
|
796
|
+
message: result,
|
|
797
|
+
// Assuming result is a string message
|
|
798
|
+
auth: this.createAuthData(
|
|
799
|
+
true,
|
|
800
|
+
// hasAccessToken
|
|
801
|
+
false,
|
|
802
|
+
// enable - assuming 2FA not enabled for admin login
|
|
803
|
+
AUTH_STATUS.BASIC_AUTH,
|
|
804
|
+
false
|
|
805
|
+
// verified
|
|
806
|
+
)
|
|
807
|
+
};
|
|
808
|
+
this.sendResponse(res, HTTP_STATUS.CREATED, MESSAGES.ADMIN_LOGIN_SUCCESS, adminLoginData);
|
|
784
809
|
} catch (error) {
|
|
785
810
|
next(error);
|
|
786
811
|
}
|
|
@@ -788,7 +813,18 @@ var _AuthController = class _AuthController {
|
|
|
788
813
|
this.logout = /* @__PURE__ */ __name(async (req, res, next) => {
|
|
789
814
|
try {
|
|
790
815
|
await this.logoutUseCase.execute(req.body.id);
|
|
791
|
-
|
|
816
|
+
const logoutData = {
|
|
817
|
+
auth: this.createAuthData(
|
|
818
|
+
false,
|
|
819
|
+
// hasAccessToken - no token after logout
|
|
820
|
+
false,
|
|
821
|
+
// enable - 2FA status irrelevant after logout
|
|
822
|
+
AUTH_STATUS.LOGGED_OUT,
|
|
823
|
+
false
|
|
824
|
+
// verified - false after logout
|
|
825
|
+
)
|
|
826
|
+
};
|
|
827
|
+
this.sendResponse(res, HTTP_STATUS.OK, MESSAGES.LOGOUT_SUCCESS, logoutData);
|
|
792
828
|
} catch (error) {
|
|
793
829
|
next(error);
|
|
794
830
|
}
|
|
@@ -797,11 +833,22 @@ var _AuthController = class _AuthController {
|
|
|
797
833
|
try {
|
|
798
834
|
const { email, name, password, role, adminPassword } = req.body;
|
|
799
835
|
await this.registerUseCase.execute(email, name, password, role, adminPassword);
|
|
836
|
+
const registerData = {
|
|
837
|
+
auth: this.createAuthData(
|
|
838
|
+
false,
|
|
839
|
+
// hasAccessToken - no token after registration
|
|
840
|
+
false,
|
|
841
|
+
// enable - 2FA not enabled for new users
|
|
842
|
+
AUTH_STATUS.REGISTERED,
|
|
843
|
+
false
|
|
844
|
+
// verified - false after registration
|
|
845
|
+
)
|
|
846
|
+
};
|
|
800
847
|
this.sendResponse(
|
|
801
848
|
res,
|
|
802
849
|
HTTP_STATUS.OK,
|
|
803
850
|
MESSAGES.REGISTER_SUCCESS,
|
|
804
|
-
|
|
851
|
+
registerData,
|
|
805
852
|
{ status: true }
|
|
806
853
|
);
|
|
807
854
|
} catch (error) {
|
|
@@ -812,7 +859,20 @@ var _AuthController = class _AuthController {
|
|
|
812
859
|
try {
|
|
813
860
|
const { refreshToken } = req.body;
|
|
814
861
|
const result = await this.refreshTokenUseCase.execute(refreshToken);
|
|
815
|
-
|
|
862
|
+
if (!result) {
|
|
863
|
+
throw new Error("Failed to refresh token");
|
|
864
|
+
}
|
|
865
|
+
const refreshData = {
|
|
866
|
+
accessToken: result.accessToken,
|
|
867
|
+
refreshToken: result.refreshToken,
|
|
868
|
+
auth: this.createAuthData(
|
|
869
|
+
true,
|
|
870
|
+
false,
|
|
871
|
+
AUTH_STATUS.BASIC_AUTH,
|
|
872
|
+
false
|
|
873
|
+
)
|
|
874
|
+
};
|
|
875
|
+
this.sendResponse(res, HTTP_STATUS.OK, MESSAGES.TOKEN_REFRESH_SUCCESS, refreshData);
|
|
816
876
|
} catch (error) {
|
|
817
877
|
next(error);
|
|
818
878
|
}
|