@dwtechs/toker-express 0.5.2 → 0.6.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.
- package/README.md +167 -101
- package/dist/toker-express.d.ts +6 -4
- package/dist/toker-express.js +41 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
[](https://opensource.org/licenses/MIT)
|
|
3
3
|
[](https://www.npmjs.com/package/@dwtechs/toker-express)
|
|
4
4
|
[](https://www.npmjs.com/package/@dwtechs/toker-express)
|
|
5
|
-

|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
- [Synopsis](#synopsis)
|
|
@@ -47,7 +47,7 @@ $ npm i @dwtechs/toker-express
|
|
|
47
47
|
```javascript
|
|
48
48
|
|
|
49
49
|
// @ts-check
|
|
50
|
-
import
|
|
50
|
+
import { parseBearer, createTokens, refreshTokens, decodeAccess, decodeRefresh, } from "@dwtechs/toker-express";
|
|
51
51
|
import express from "express";
|
|
52
52
|
const router = express.Router();
|
|
53
53
|
|
|
@@ -60,24 +60,24 @@ const add = [
|
|
|
60
60
|
uEntity.normalize,
|
|
61
61
|
uEntity.validate,
|
|
62
62
|
login,
|
|
63
|
-
|
|
63
|
+
createTokens,
|
|
64
64
|
cEntity.add,
|
|
65
65
|
];
|
|
66
66
|
|
|
67
67
|
const refresh = [
|
|
68
68
|
cEntity.validate,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
parseBearer,
|
|
70
|
+
decodeAccess,
|
|
71
|
+
decodeRefresh,
|
|
72
72
|
checkToken,
|
|
73
|
-
|
|
73
|
+
refreshTokens,
|
|
74
74
|
cEntity.update,
|
|
75
75
|
];
|
|
76
76
|
|
|
77
77
|
const del = [
|
|
78
78
|
checkToken,
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
parseBearer,
|
|
80
|
+
decodeAccess,
|
|
81
81
|
cEntity.delete,
|
|
82
82
|
];
|
|
83
83
|
|
|
@@ -125,22 +125,49 @@ const refreshDuration = isNumber(REFRESH_TOKEN_DURATION, false) ? REFRESH_TOKEN_
|
|
|
125
125
|
```typescript
|
|
126
126
|
|
|
127
127
|
/**
|
|
128
|
-
* Express middleware to
|
|
128
|
+
* Express middleware to create new access and refresh JWT tokens for a user.
|
|
129
129
|
*
|
|
130
|
-
* This middleware
|
|
131
|
-
*
|
|
132
|
-
*
|
|
130
|
+
* This middleware generates new access and refresh tokens using the user ID
|
|
131
|
+
* from `res.locals.user.id` and stores them in `req.body.rows[0]` for
|
|
132
|
+
* subsequent processing (e.g., database insertion).
|
|
133
|
+
* Note that this middleware assumes that `req.body.rows[0]` already exists
|
|
134
|
+
* with some user data like nickname and roles.
|
|
133
135
|
*
|
|
134
|
-
* The
|
|
135
|
-
*
|
|
136
|
-
* -
|
|
136
|
+
* @param {Request} req - The Express request object. Should contain:
|
|
137
|
+
* - `req.body.rows`: Array where tokens will be added to the first element
|
|
138
|
+
* @param {Response} res - The Express response object. Should contain:
|
|
139
|
+
* - `res.locals.user.id`: User ID to use as issuer (iss) in the tokens
|
|
140
|
+
* @param {NextFunction} next - Express next middleware function
|
|
141
|
+
*
|
|
142
|
+
* @returns {void}
|
|
143
|
+
*
|
|
144
|
+
* @throws Will call next() with error object containing:
|
|
145
|
+
* - statusCode: 400 - When user ID (iss) is missing or invalid (not a number between 1-999999999)
|
|
146
|
+
* - statusCode: 500 - When token signing fails (invalid secrets, duration, or base64 secret)
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* // After user creation/registration, call createTokens to generate tokens
|
|
150
|
+
* app.post('/login', validateUser, createTokens, saveConsumer, ...);
|
|
151
|
+
*/
|
|
152
|
+
function createTokens(req: Request, res: Response, next: NextFunction): void {}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Express middleware to refresh and generate new access and refresh JWT tokens.
|
|
137
156
|
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
157
|
+
* This middleware creates new access and refresh tokens using the user ID (iss)
|
|
158
|
+
* from a previously decoded access token stored by decodeAccess middleware
|
|
159
|
+
* in `res.locals.tokens.decodedAccess.iss`.
|
|
160
|
+
* The generated tokens are stored in `req.body.rows[0]` for subsequent processing.
|
|
161
|
+
* This is typically used in token refresh flows where an existing access token
|
|
162
|
+
* has been decoded and validated.
|
|
163
|
+
* Note that req.body.rows[0] must already exist at this stage because with consumer id
|
|
164
|
+
* because consumer id has already been found at this stage and will be used top update
|
|
165
|
+
* the consumer in the database.
|
|
166
|
+
*
|
|
167
|
+
* @param {Request} req - The Express request object. Should contain:
|
|
168
|
+
* - `req.body.rows`: Array where tokens will be added to the first element
|
|
140
169
|
* @param {Response} res - The Express response object. Should contain:
|
|
141
|
-
* - `res.locals.
|
|
142
|
-
* - `res.locals.id`: User ID (used if decodedAccessToken is not available)
|
|
143
|
-
* Tokens will be added to `res.locals.accessToken` and `res.locals.refreshToken`
|
|
170
|
+
* - `res.locals.tokens.decodedAccess.iss`: User ID from the decoded access token
|
|
144
171
|
* @param {NextFunction} next - Express next middleware function
|
|
145
172
|
*
|
|
146
173
|
* @returns {void}
|
|
@@ -150,142 +177,177 @@ const refreshDuration = isNumber(REFRESH_TOKEN_DURATION, false) ? REFRESH_TOKEN_
|
|
|
150
177
|
* - statusCode: 500 - When token signing fails (invalid secrets, duration, or base64 secret)
|
|
151
178
|
*
|
|
152
179
|
* @example
|
|
153
|
-
* //
|
|
154
|
-
* app.post('/
|
|
155
|
-
* res.json({
|
|
156
|
-
* accessToken: res.locals.accessToken,
|
|
157
|
-
* refreshToken: res.locals.refreshToken
|
|
158
|
-
* });
|
|
159
|
-
* });
|
|
180
|
+
* // Token refresh flow with decoded access token
|
|
181
|
+
* app.post('/refresh', decodeAccess, refreshTokens, updateDatabase, ...);
|
|
160
182
|
*/
|
|
161
|
-
function
|
|
183
|
+
function refreshTokens(req: Request, res: Response, next: NextFunction): void {}
|
|
162
184
|
|
|
163
185
|
/**
|
|
164
|
-
* Express middleware
|
|
186
|
+
* Express middleware to extract the JWT bearer token from the Authorization header.
|
|
165
187
|
*
|
|
166
188
|
* This middleware extracts the JWT token from the Authorization header (Bearer scheme)
|
|
167
|
-
* and stores it in res.locals.
|
|
168
|
-
* It only processes requests that have `res.locals.isProtected` set to true.
|
|
189
|
+
* and stores it in `res.locals.tokens.access` for use by subsequent middleware (typically decodeAccess).
|
|
190
|
+
* It only processes requests that have `res.locals.route.isProtected` set to true.
|
|
191
|
+
* For non-protected routes, it simply passes control to the next middleware.
|
|
169
192
|
*
|
|
170
|
-
* @param {Request} req - The Express request object
|
|
193
|
+
* @param {Request} req - The Express request object. Should contain:
|
|
194
|
+
* - `req.headers.authorization`: Authorization header with Bearer token format
|
|
171
195
|
* @param {Response} res - The Express response object. Should contain:
|
|
172
|
-
* - `res.locals.isProtected`: Boolean flag to determine if route requires JWT protection
|
|
173
|
-
* Parsed token will be added to `res.locals.
|
|
196
|
+
* - `res.locals.route.isProtected`: Boolean flag to determine if route requires JWT protection
|
|
197
|
+
* Parsed token will be added to `res.locals.tokens.access`
|
|
174
198
|
* @param {NextFunction} next - The next middleware function to be called
|
|
175
199
|
*
|
|
176
|
-
* @returns {void}
|
|
200
|
+
* @returns {void}
|
|
177
201
|
*
|
|
178
|
-
* @throws
|
|
179
|
-
*
|
|
202
|
+
* @throws Will call next() with error when:
|
|
203
|
+
* - Authorization header is missing (HTTP 401)
|
|
204
|
+
* - Authorization header format is invalid or not Bearer scheme (HTTP 401)
|
|
180
205
|
*
|
|
206
|
+
* @example
|
|
207
|
+
* // Use in protected route chain
|
|
208
|
+
* app.get('/profile', parseBearer, decodeAccess, ...);
|
|
181
209
|
*/
|
|
182
|
-
function
|
|
210
|
+
function parseBearer(req: Request, res: Response, next: NextFunction): void {}
|
|
183
211
|
|
|
184
212
|
/**
|
|
185
|
-
* Express middleware
|
|
213
|
+
* Express middleware to decode and verify an access token.
|
|
214
|
+
*
|
|
215
|
+
* This middleware validates the JWT access token from `res.locals.tokens.access`,
|
|
216
|
+
* verifies its signature and structure, validates the issuer (iss) claim,
|
|
217
|
+
* and stores the decoded token in `res.locals.tokens.decodedAccess` for use by
|
|
218
|
+
* subsequent middleware. It only processes requests that have `res.locals.route.isProtected`
|
|
219
|
+
* set to true. For non-protected routes, it simply passes control to the next middleware.
|
|
186
220
|
*
|
|
187
|
-
* This middleware
|
|
188
|
-
*
|
|
189
|
-
*
|
|
221
|
+
* Note: This middleware IGNORES token expiration (exp claim) by design, allowing
|
|
222
|
+
* expired access tokens to be decoded. This is useful for token refresh flows where
|
|
223
|
+
* you need to identify the user even after their access token has expired.
|
|
190
224
|
*
|
|
191
|
-
* @param {Request}
|
|
225
|
+
* @param {Request} _req - The Express request object (unused)
|
|
192
226
|
* @param {Response} res - The Express response object. Should contain:
|
|
193
|
-
* - `res.locals.isProtected`: Boolean flag to determine if route requires JWT protection
|
|
194
|
-
* - `res.locals.
|
|
195
|
-
* Decoded token will be added to `res.locals.
|
|
227
|
+
* - `res.locals.route.isProtected`: Boolean flag to determine if route requires JWT protection
|
|
228
|
+
* - `res.locals.tokens.access`: The JWT token to decode (from parseBearer middleware)
|
|
229
|
+
* Decoded token will be added to `res.locals.tokens.decodedAccess`
|
|
196
230
|
* @param {NextFunction} next - The next middleware function to be called
|
|
197
231
|
*
|
|
198
|
-
* @returns {void}
|
|
232
|
+
* @returns {void}
|
|
199
233
|
*
|
|
200
|
-
* @throws
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
* -
|
|
208
|
-
* - statusCode: 400 - When decoded token is missing required 'iss' claim
|
|
234
|
+
* @throws Will call next() with error when:
|
|
235
|
+
* - Token is not a valid JWT format (HTTP 401)
|
|
236
|
+
* - Token is malformed or has invalid structure (HTTP 401)
|
|
237
|
+
* - Token cannot be used yet (nbf claim) (HTTP 401)
|
|
238
|
+
* - Token signature is invalid (HTTP 401)
|
|
239
|
+
* - Issuer (iss) is missing or invalid - not a number between 1-999999999 (HTTP 400)
|
|
240
|
+
* - Secrets configuration is invalid (HTTP 500)
|
|
241
|
+
* - Secret cannot be decoded from base64 (HTTP 500)
|
|
209
242
|
*
|
|
243
|
+
* @example
|
|
244
|
+
* // Use in protected route chain for token refresh
|
|
245
|
+
* app.post('/refresh', parseBearer, decodeAccess, refreshTokens, ...);
|
|
210
246
|
*/
|
|
211
|
-
function decodeAccess(
|
|
247
|
+
function decodeAccess(_req: Request, res: Response, next: NextFunction): void {}
|
|
212
248
|
|
|
213
249
|
/**
|
|
214
|
-
*
|
|
250
|
+
* Express middleware to decode and verify a refresh token from the request body.
|
|
215
251
|
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
252
|
+
* This middleware validates the JWT refresh token from `req.body.refreshToken`,
|
|
253
|
+
* verifies its signature, structure, expiration, and the issuer (iss) claim,
|
|
254
|
+
* then stores the decoded token in `res.locals.tokens.decodedRefresh` for use
|
|
255
|
+
* by subsequent middleware.
|
|
219
256
|
*
|
|
220
|
-
*
|
|
257
|
+
* Note: Unlike decodeAccess, this middleware DOES check token expiration (exp claim).
|
|
258
|
+
* Refresh tokens must be valid and not expired.
|
|
259
|
+
*
|
|
260
|
+
* @param {Request} req - The Express request object. Should contain:
|
|
261
|
+
* - `req.body.refreshToken`: The JWT refresh token to decode and verify
|
|
262
|
+
* @param {Response} res - The Express response object.
|
|
263
|
+
* Decoded token will be added to `res.locals.tokens.decodedRefresh`
|
|
264
|
+
* @param {NextFunction} next - The next middleware function to be called
|
|
265
|
+
*
|
|
266
|
+
* @returns {void}
|
|
221
267
|
*
|
|
222
|
-
* @throws
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
* -
|
|
230
|
-
* -
|
|
268
|
+
* @throws Will call next() with error when:
|
|
269
|
+
* - Token is not a valid JWT format (HTTP 401)
|
|
270
|
+
* - Token is malformed or has invalid structure (HTTP 401)
|
|
271
|
+
* - Token has expired (exp claim) (HTTP 401)
|
|
272
|
+
* - Token cannot be used yet (nbf claim) (HTTP 401)
|
|
273
|
+
* - Token signature is invalid (HTTP 401)
|
|
274
|
+
* - Issuer (iss) is missing or invalid - not a number between 1-999999999 (HTTP 400)
|
|
275
|
+
* - Secrets configuration is invalid (HTTP 500)
|
|
276
|
+
* - Secret cannot be decoded from base64 (HTTP 500)
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* // Use in refresh token flow
|
|
280
|
+
* app.post('/refresh', parseBearer, decodeAccess, decodeRefresh, refreshTokens, ...);
|
|
231
281
|
*/
|
|
232
|
-
function decodeRefresh(req: Request,
|
|
282
|
+
function decodeRefresh(req: Request, res: Response, next: NextFunction): void {}
|
|
233
283
|
|
|
234
284
|
```
|
|
235
285
|
|
|
236
|
-
### JWT
|
|
286
|
+
### JWT Token Generation
|
|
287
|
+
|
|
288
|
+
#### createTokens
|
|
237
289
|
|
|
238
|
-
This function
|
|
290
|
+
This function creates new tokens for a new user or during initial login. It looks for the user ID from:
|
|
239
291
|
|
|
240
292
|
```Javascript
|
|
241
|
-
|
|
293
|
+
const iss = res.locals.user.id;
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
It stores both new refresh and access tokens in `req.body.rows[0]`:
|
|
242
297
|
|
|
243
|
-
|
|
244
|
-
|
|
298
|
+
```Javascript
|
|
299
|
+
req.body.rows[0].accessToken = at;
|
|
300
|
+
req.body.rows[0].refreshToken = rt;
|
|
245
301
|
```
|
|
246
302
|
|
|
247
|
-
|
|
303
|
+
**Note:** This function assumes that `req.body.rows[0]` already exists with user data like nickname and roles.
|
|
304
|
+
|
|
305
|
+
#### refreshTokens
|
|
248
306
|
|
|
249
|
-
|
|
307
|
+
This function refreshes tokens for existing users. It looks for the user ID (iss) from a decoded access token:
|
|
250
308
|
|
|
251
309
|
```Javascript
|
|
252
|
-
res.locals
|
|
253
|
-
|
|
310
|
+
let iss = res.locals?.tokens?.decodedAccess?.iss;
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
It stores both new refresh and access tokens in `req.body.rows[0]`:
|
|
254
314
|
|
|
255
|
-
|
|
256
|
-
req.body.rows[0].accessToken =
|
|
257
|
-
req.body.rows[0].refreshToken =
|
|
315
|
+
```Javascript
|
|
316
|
+
req.body.rows[0].accessToken = at;
|
|
317
|
+
req.body.rows[0].refreshToken = rt;
|
|
258
318
|
```
|
|
259
319
|
|
|
320
|
+
**Note:** This function assumes that `req.body.rows[0]` already exists with consumer data that will be updated in the database.
|
|
321
|
+
|
|
260
322
|
### JWT Decoding
|
|
261
323
|
|
|
262
324
|
#### Route Protection with isProtected
|
|
263
325
|
|
|
264
|
-
The `
|
|
326
|
+
The `parseBearer()` and `decodeAccess()` middlewares only process requests when `res.locals.route.isProtected` is set to `true`. This allows you to selectively protect routes that require authentication.
|
|
265
327
|
|
|
266
328
|
You should set this flag in a middleware before calling these functions:
|
|
267
329
|
|
|
268
330
|
```Javascript
|
|
269
331
|
// Example middleware to mark route as protected
|
|
270
332
|
function protectRoute(req, res, next) {
|
|
271
|
-
res.locals.
|
|
333
|
+
res.locals.route = { isProtected: true };
|
|
272
334
|
next();
|
|
273
335
|
}
|
|
274
336
|
|
|
275
337
|
// Usage
|
|
276
|
-
router.get('/protected-route', protectRoute, tk.
|
|
338
|
+
router.get('/protected-route', protectRoute, tk.parseBearer, tk.decodeAccess, yourHandler);
|
|
277
339
|
```
|
|
278
340
|
|
|
279
|
-
If `res.locals.isProtected` is `false`, `undefined`, or `null`, these middlewares will simply call `next()` without processing the token, allowing the request to continue to the next middleware.
|
|
341
|
+
If `res.locals.route.isProtected` is `false`, `undefined`, or `null`, these middlewares will simply call `next()` without processing the token, allowing the request to continue to the next middleware.
|
|
280
342
|
|
|
281
343
|
#### Access Token Processing
|
|
282
344
|
|
|
283
|
-
The access token processing is
|
|
345
|
+
The access token processing is split into two separate middlewares for better flexibility:
|
|
284
346
|
|
|
285
|
-
1. **
|
|
347
|
+
1. **parseBearer()** - Extracts the bearer token from the Authorization header
|
|
286
348
|
2. **decodeAccess()** - Validates and decodes the JWT token
|
|
287
349
|
|
|
288
|
-
#####
|
|
350
|
+
##### parseBearer()
|
|
289
351
|
|
|
290
352
|
This middleware extracts the JWT token from the Authorization header using the Bearer scheme.
|
|
291
353
|
|
|
@@ -293,31 +355,33 @@ This middleware extracts the JWT token from the Authorization header using the B
|
|
|
293
355
|
const bearer = req.headers.authorization; // "Bearer <token>"
|
|
294
356
|
```
|
|
295
357
|
|
|
296
|
-
The parsed token is then stored in `res.locals.
|
|
358
|
+
The parsed token is then stored in `res.locals.tokens.access`:
|
|
297
359
|
|
|
298
360
|
```Javascript
|
|
299
|
-
res.locals.
|
|
361
|
+
res.locals.tokens.access = token;
|
|
300
362
|
```
|
|
301
363
|
|
|
302
364
|
##### decodeAccess()
|
|
303
365
|
|
|
304
|
-
This middleware takes the token from `res.locals.
|
|
366
|
+
This middleware takes the token from `res.locals.tokens.access`, validates it, and decodes it.
|
|
305
367
|
|
|
306
368
|
```Javascript
|
|
307
|
-
const token = res.locals.
|
|
369
|
+
const token = res.locals.tokens.access;
|
|
308
370
|
```
|
|
309
371
|
|
|
310
|
-
The decoded token is then stored in `res.locals.
|
|
372
|
+
The decoded token is then stored in `res.locals.tokens.decodedAccess`:
|
|
311
373
|
|
|
312
374
|
```Javascript
|
|
313
|
-
res.locals.
|
|
375
|
+
res.locals.tokens.decodedAccess = decodedToken;
|
|
314
376
|
```
|
|
315
377
|
|
|
316
|
-
**
|
|
378
|
+
**Important:** This middleware **ignores token expiration** by design. This allows expired access tokens to be decoded, which is useful for token refresh flows where you need to identify the user even after their access token has expired.
|
|
379
|
+
|
|
380
|
+
**Note:** You should use both middlewares in sequence for full access token processing, or you can use just `parseBearer()` if you only need to extract the token without decoding it.
|
|
317
381
|
|
|
318
382
|
#### Refresh Token Decoding
|
|
319
383
|
|
|
320
|
-
decodeRefresh()
|
|
384
|
+
decodeRefresh() function will look for a token in the client request body.
|
|
321
385
|
|
|
322
386
|
```Javascript
|
|
323
387
|
const token = req.body.refreshToken;
|
|
@@ -326,9 +390,11 @@ const token = req.body.refreshToken;
|
|
|
326
390
|
It will then send the decoded token in the res object.
|
|
327
391
|
|
|
328
392
|
```Javascript
|
|
329
|
-
res.locals.
|
|
393
|
+
res.locals.tokens.decodedRefresh = decodedToken;
|
|
330
394
|
```
|
|
331
395
|
|
|
396
|
+
**Important:** Unlike `decodeAccess()`, this middleware **does check token expiration**. Refresh tokens must be valid and not expired.
|
|
397
|
+
|
|
332
398
|
|
|
333
399
|
## Logs
|
|
334
400
|
|
package/dist/toker-express.d.ts
CHANGED
|
@@ -26,14 +26,16 @@ https://github.com/DWTechs/Toker-express.js
|
|
|
26
26
|
|
|
27
27
|
import type { Request, Response, NextFunction } from 'express';
|
|
28
28
|
|
|
29
|
-
declare function
|
|
30
|
-
declare function
|
|
29
|
+
declare function createTokens(req: Request, res: Response, next: NextFunction): void;
|
|
30
|
+
declare function refreshTokens(req: Request, res: Response, next: NextFunction): void;
|
|
31
|
+
declare function parseBearer(req: Request, res: Response, next: NextFunction): void;
|
|
31
32
|
declare function decodeAccess(_req: Request, res: Response, next: NextFunction): void;
|
|
32
33
|
declare function decodeRefresh(req: Request, res: Response, next: NextFunction): void;
|
|
33
34
|
|
|
34
35
|
export {
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
createTokens,
|
|
37
|
+
refreshTokens,
|
|
38
|
+
parseBearer,
|
|
37
39
|
decodeAccess,
|
|
38
40
|
decodeRefresh,
|
|
39
41
|
};
|
package/dist/toker-express.js
CHANGED
|
@@ -24,8 +24,8 @@ SOFTWARE.
|
|
|
24
24
|
https://github.com/DWTechs/Toker-express.js
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
import { sign, parseBearer, verify } from '@dwtechs/toker';
|
|
28
|
-
import { isString, isNumber,
|
|
27
|
+
import { sign, parseBearer as parseBearer$1, verify } from '@dwtechs/toker';
|
|
28
|
+
import { isString, isNumber, isValidInteger, isJWT } from '@dwtechs/checkard';
|
|
29
29
|
import { log } from '@dwtechs/winstan';
|
|
30
30
|
|
|
31
31
|
const { TOKEN_SECRET, ACCESS_TOKEN_DURATION, REFRESH_TOKEN_DURATION } = process.env;
|
|
@@ -37,12 +37,10 @@ if (!isString(TOKEN_SECRET, "!0"))
|
|
|
37
37
|
const secrets = [TOKEN_SECRET];
|
|
38
38
|
const accessDuration = isNumber(ACCESS_TOKEN_DURATION, false) ? Number(ACCESS_TOKEN_DURATION) : 600;
|
|
39
39
|
const refreshDuration = isNumber(REFRESH_TOKEN_DURATION, false) ? Number(REFRESH_TOKEN_DURATION) : 86400;
|
|
40
|
-
function
|
|
41
|
-
var _a, _b
|
|
42
|
-
|
|
43
|
-
if (!iss)
|
|
44
|
-
iss = (_c = res.locals.id) !== null && _c !== void 0 ? _c : null;
|
|
45
|
-
if (!isValidNumber(iss, 1, 999999999, false))
|
|
40
|
+
function createTokens(req, res, next) {
|
|
41
|
+
var _a, _b;
|
|
42
|
+
const iss = (_b = (_a = res.locals) === null || _a === void 0 ? void 0 : _a.user) === null || _b === void 0 ? void 0 : _b.id;
|
|
43
|
+
if (!isValidInteger(iss, 1, 999999999, false))
|
|
46
44
|
return next({ statusCode: 400, message: `${LOGS_PREFIX}Missing iss` });
|
|
47
45
|
log.debug(`${LOGS_PREFIX}Create tokens for user ${iss}`);
|
|
48
46
|
let at;
|
|
@@ -55,22 +53,36 @@ function refresh(req, res, next) {
|
|
|
55
53
|
return next(err);
|
|
56
54
|
}
|
|
57
55
|
log.debug(`refreshToken='${rt}', accessToken='${at}'`);
|
|
58
|
-
res.locals.accessToken = at;
|
|
59
|
-
res.locals.refreshToken = rt;
|
|
60
|
-
if (!isArray((_d = req.body) === null || _d === void 0 ? void 0 : _d.rows, ">=", 1))
|
|
61
|
-
req.body.rows = [{}];
|
|
62
|
-
else if (!isObject(req.body.rows[0]))
|
|
63
|
-
req.body.rows[0] = {};
|
|
64
56
|
req.body.rows[0].accessToken = at;
|
|
65
57
|
req.body.rows[0].refreshToken = rt;
|
|
66
58
|
next();
|
|
67
59
|
}
|
|
68
|
-
function
|
|
69
|
-
|
|
60
|
+
function refreshTokens(req, res, next) {
|
|
61
|
+
var _a, _b, _c;
|
|
62
|
+
let iss = (_c = (_b = (_a = res.locals) === null || _a === void 0 ? void 0 : _a.tokens) === null || _b === void 0 ? void 0 : _b.decodedAccess) === null || _c === void 0 ? void 0 : _c.iss;
|
|
63
|
+
if (!isValidInteger(iss, 1, 999999999, false))
|
|
64
|
+
return next({ statusCode: 400, message: `${LOGS_PREFIX}Missing iss` });
|
|
65
|
+
log.debug(`${LOGS_PREFIX}Create tokens for user ${iss}`);
|
|
66
|
+
let at;
|
|
67
|
+
let rt;
|
|
68
|
+
try {
|
|
69
|
+
at = sign(iss, accessDuration, "access", secrets);
|
|
70
|
+
rt = sign(iss, refreshDuration, "refresh", secrets);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
return next(err);
|
|
74
|
+
}
|
|
75
|
+
log.debug(`refreshToken='${rt}', accessToken='${at}'`);
|
|
76
|
+
req.body.rows[0].accessToken = at;
|
|
77
|
+
req.body.rows[0].refreshToken = rt;
|
|
78
|
+
next();
|
|
79
|
+
}
|
|
80
|
+
function parseBearer(req, res, next) {
|
|
81
|
+
if (!res.locals.route.isProtected)
|
|
70
82
|
return next();
|
|
71
|
-
log.debug(`${LOGS_PREFIX}parse bearer token`);
|
|
83
|
+
log.debug(`${LOGS_PREFIX}parse bearer to get access token`);
|
|
72
84
|
try {
|
|
73
|
-
res.locals.
|
|
85
|
+
res.locals.tokens.access = parseBearer$1(req.headers.authorization);
|
|
74
86
|
}
|
|
75
87
|
catch (e) {
|
|
76
88
|
return next(e);
|
|
@@ -79,9 +91,9 @@ function parseBearerToken(req, res, next) {
|
|
|
79
91
|
}
|
|
80
92
|
function decodeAccess(_req, res, next) {
|
|
81
93
|
log.debug(`${LOGS_PREFIX}decode access token`);
|
|
82
|
-
if (!res.locals.isProtected)
|
|
94
|
+
if (!res.locals.route.isProtected)
|
|
83
95
|
return next();
|
|
84
|
-
const t = res.locals.
|
|
96
|
+
const t = res.locals.tokens.access;
|
|
85
97
|
if (!isJWT(t))
|
|
86
98
|
return next({ statusCode: 401, message: `${LOGS_PREFIX}Invalid access token` });
|
|
87
99
|
let dt = null;
|
|
@@ -91,30 +103,30 @@ function decodeAccess(_req, res, next) {
|
|
|
91
103
|
catch (e) {
|
|
92
104
|
return next(e);
|
|
93
105
|
}
|
|
94
|
-
if (!
|
|
106
|
+
if (!isValidInteger(dt.iss, 1, 999999999, false))
|
|
95
107
|
return next({ statusCode: 400, message: `${LOGS_PREFIX}Missing iss` });
|
|
96
108
|
log.debug(`${LOGS_PREFIX}Decoded access token : ${JSON.stringify(dt)}`);
|
|
97
|
-
res.locals.
|
|
109
|
+
res.locals.tokens.decodedAccess = dt;
|
|
98
110
|
next();
|
|
99
111
|
}
|
|
100
112
|
function decodeRefresh(req, res, next) {
|
|
101
113
|
var _a;
|
|
102
|
-
const
|
|
103
|
-
log.debug(`${LOGS_PREFIX}decodeRefresh(token=${
|
|
104
|
-
if (!isJWT(
|
|
114
|
+
const t = (_a = req.body) === null || _a === void 0 ? void 0 : _a.refreshToken;
|
|
115
|
+
log.debug(`${LOGS_PREFIX}decodeRefresh(token=${t})`);
|
|
116
|
+
if (!isJWT(t))
|
|
105
117
|
return next({ statusCode: 401, message: `${LOGS_PREFIX}Invalid refresh token` });
|
|
106
118
|
let dt = null;
|
|
107
119
|
try {
|
|
108
|
-
dt = verify(
|
|
120
|
+
dt = verify(t, secrets, false);
|
|
109
121
|
}
|
|
110
122
|
catch (e) {
|
|
111
123
|
return next(e);
|
|
112
124
|
}
|
|
113
|
-
if (!
|
|
125
|
+
if (!isValidInteger(dt.iss, 1, 999999999, false))
|
|
114
126
|
return next({ statusCode: 400, message: `${LOGS_PREFIX}Missing iss` });
|
|
115
127
|
log.debug(`${LOGS_PREFIX}Decoded refresh token : ${JSON.stringify(dt)}`);
|
|
116
|
-
res.locals.
|
|
128
|
+
res.locals.tokens.decodedRefresh = dt;
|
|
117
129
|
next();
|
|
118
130
|
}
|
|
119
131
|
|
|
120
|
-
export { decodeAccess, decodeRefresh,
|
|
132
|
+
export { createTokens, decodeAccess, decodeRefresh, parseBearer, refreshTokens };
|