@dwtechs/toker-express 0.1.2 → 0.2.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 CHANGED
@@ -2,7 +2,7 @@
2
2
  [![License: MIT](https://img.shields.io/npm/l/@dwtechs/toker-express.svg?color=brightgreen)](https://opensource.org/licenses/MIT)
3
3
  [![npm version](https://badge.fury.io/js/%40dwtechs%2Ftoker-express.svg)](https://www.npmjs.com/package/@dwtechs/toker-express)
4
4
  [![last version release date](https://img.shields.io/github/release-date/DWTechs/Toker-express.js)](https://www.npmjs.com/package/@dwtechs/toker-express)
5
- ![Jest:coverage](https://img.shields.io/badge/Jest:coverage-83%25-brightgreen.svg)
5
+ ![Jest:coverage](https://img.shields.io/badge/Jest:coverage-95%25-brightgreen.svg)
6
6
 
7
7
 
8
8
  - [Synopsis](#synopsis)
@@ -126,15 +126,15 @@ const refreshDuration = isNumber(REFRESH_TOKEN_DURATION, false) ? REFRESH_TOKEN_
126
126
  * Refreshes the JWT tokens for a user.
127
127
  *
128
128
  * This function generates new access and refresh tokens for a consumer based on the provided
129
- * decoded access token or user ID in the request body. It validates the issuer (iss) and
129
+ * decoded access token from req.decodedAccessToken or user ID from req.body.rows[0].id. It validates the issuer (iss) and
130
130
  * creates new tokens if the validation is successful. The new tokens are then added to the
131
- * response local and the request body objects.
131
+ * response locals object and optionally to req.body.rows[0] if rows is an array with at least one element.
132
132
  *
133
- * @param {Request} req - The request object containing the decoded access token or user ID. Where the new tokens will be added
134
- * @param {Response} res - The response object where the new tokens will be added.
133
+ * @param {Request} req - The request object containing req.decodedAccessToken or req.body.rows[0].id.
134
+ * @param {Response} res - The response object where the new tokens will be added in res.locals.
135
135
  * @param {NextFunction} next - The next middleware function in the Express.js request-response cycle.
136
136
  *
137
- * @returns {Promise<void>} Calls the next middleware function with an error if the issuer is invalid,
137
+ * @returns {void} Calls the next middleware function with an error if the issuer is invalid,
138
138
  * otherwise proceeds to the next middleware function.
139
139
  *
140
140
  * @throws {InvalidIssuerError} If the issuer (iss) is not a string or number (HTTP 400)
@@ -150,7 +150,7 @@ function refresh(req: Request, res: Response, next: NextFunction): void {}
150
150
  * Express middleware function to decode and verify an access token from the Authorization header.
151
151
  *
152
152
  * This middleware extracts the JWT access token from the Authorization header, validates its format,
153
- * verifies its signature, and attaches the decoded token to the request object for use by subsequent
153
+ * verifies its signature, and attaches the decoded token to req.decodedAccessToken for use by subsequent
154
154
  * middleware. It only processes requests that have `req.isProtected` set to true.
155
155
  *
156
156
  * @param {Request} req - The Express request object containing the Authorization header
@@ -171,14 +171,6 @@ function refresh(req: Request, res: Response, next: NextFunction): void {}
171
171
  * - statusCode: 401 - When token is not a valid JWT format
172
172
  * - statusCode: 400 - When decoded token is missing required 'iss' claim
173
173
  *
174
- * @example
175
- * ```typescript
176
- * // Usage in Express route with protection middleware
177
- * const protect = (req: Request, res: Response, next: NextFunction) => {
178
- * req.isProtected = true;
179
- * next();
180
- * };
181
- *
182
174
  */
183
175
  function decodeAccess(req: Request, _res: Response, next: NextFunction): void {}
184
176
 
@@ -189,7 +181,7 @@ function decodeAccess(req: Request, _res: Response, next: NextFunction): void {}
189
181
  * @param {Response} _res - The response object (not used in this function).
190
182
  * @param {NextFunction} next - The next middleware function to be called.
191
183
  *
192
- * @returns {Promise<void>} Calls the next middleware function with an error object if the token is invalid or missing required fields.
184
+ * @returns {void} Calls the next middleware function with an error object if the token is invalid or required fields are missing.
193
185
  *
194
186
  * @throws {InvalidTokenError} If the token is malformed or has invalid structure (HTTP 401)
195
187
  * @throws {InvalidSecretsError} If the secrets configuration is invalid (HTTP 500)
@@ -210,7 +202,7 @@ function decodeRefresh(req: Request, _res: Response, next: NextFunction): void {
210
202
  This function will look for an ISS in the client request body :
211
203
 
212
204
  ```Javascript
213
- const iss = req.body.decodedAccessToken?.iss || req.body?.id?.toString();
205
+ const iss = req.decodedAccessToken?.iss || (isArray(req.body.rows, null, 1) ? req.body.rows[0]?.id?.toString() : null);
214
206
  ```
215
207
 
216
208
  It will then send both new refresh and access tokens in the res.locals and req.body objects.
@@ -218,8 +210,11 @@ It will then send both new refresh and access tokens in the res.locals and req.b
218
210
  ```Javascript
219
211
  res.locals.accessToken = accessToken;
220
212
  res.locals.refreshToken = refreshToken;
221
- req.body.accessToken = accessToken;
222
- req.body.refreshToken = refreshToken;
213
+
214
+ if (isArray(req.body.rows)) {
215
+ req.body.rows[0].accessToken = accessToken;
216
+ req.body.rows[0].refreshToken = refreshToken;
217
+ }
223
218
  ```
224
219
 
225
220
  ### JWT Decoding
@@ -37,9 +37,9 @@ declare global {
37
37
  }
38
38
  }
39
39
 
40
- declare function refresh(req: Request, res: Response, next: NextFunction): Promise<void>;
40
+ declare function refresh(req: Request, res: Response, next: NextFunction): void;
41
41
  declare function decodeAccess(req: Request, _res: Response, next: NextFunction): void;
42
- declare function decodeRefresh(req: Request, _res: Response, next: NextFunction): Promise<void>;
42
+ declare function decodeRefresh(req: Request, _res: Response, next: NextFunction): void;
43
43
 
44
44
  export {
45
45
  refresh,
@@ -25,18 +25,9 @@ https://github.com/DWTechs/Toker-express.js
25
25
  */
26
26
 
27
27
  import { sign, parseBearer, verify } from '@dwtechs/toker';
28
- import { isString, isNumber, isValidNumber, isJWT } from '@dwtechs/checkard';
28
+ import { isString, isNumber, isArray, isValidNumber, isJWT } from '@dwtechs/checkard';
29
29
  import { log } from '@dwtechs/winstan';
30
30
 
31
- var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
32
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
33
- return new (P || (P = Promise))(function (resolve, reject) {
34
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
35
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
36
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
37
- step((generator = generator.apply(thisArg, _arguments || [])).next());
38
- });
39
- };
40
31
  const { TOKEN_SECRET, ACCESS_TOKEN_DURATION, REFRESH_TOKEN_DURATION } = process.env;
41
32
  const LOGS_PREFIX = "Toker-express: ";
42
33
  if (!TOKEN_SECRET)
@@ -47,28 +38,34 @@ const secrets = [TOKEN_SECRET];
47
38
  const accessDuration = isNumber(ACCESS_TOKEN_DURATION, false) ? Number(ACCESS_TOKEN_DURATION) : 600;
48
39
  const refreshDuration = isNumber(REFRESH_TOKEN_DURATION, false) ? Number(REFRESH_TOKEN_DURATION) : 86400;
49
40
  function refresh(req, res, next) {
50
- return __awaiter(this, void 0, void 0, function* () {
51
- var _a, _b, _c;
52
- const iss = ((_a = req.decodedAccessToken) === null || _a === void 0 ? void 0 : _a.iss) || ((_c = (_b = req.body) === null || _b === void 0 ? void 0 : _b.id) === null || _c === void 0 ? void 0 : _c.toString());
53
- if (!isValidNumber(iss, 1, 999999999, false))
54
- return next({ statusCode: 400, message: `${LOGS_PREFIX}Missing iss` });
55
- log.debug(`${LOGS_PREFIX}Create tokens for user ${iss}`);
56
- let accessToken;
57
- let refreshToken;
58
- try {
59
- accessToken = sign(iss, accessDuration, "access", secrets);
60
- refreshToken = sign(iss, refreshDuration, "refresh", secrets);
61
- }
62
- catch (err) {
63
- return next(err);
64
- }
65
- log.debug(`refreshToken='${refreshToken}', accessToken='${accessToken}'`);
66
- res.locals.accessToken = accessToken;
67
- res.locals.refreshToken = refreshToken;
68
- req.body.accessToken = accessToken;
69
- req.body.refreshToken = refreshToken;
70
- next();
71
- });
41
+ var _a, _b, _c;
42
+ let iss = (_a = req.decodedAccessToken) === null || _a === void 0 ? void 0 : _a.iss;
43
+ const rbr = req.body.rows;
44
+ let rbrIsArray = false;
45
+ if (!iss) {
46
+ rbrIsArray = isArray(rbr, null, 1);
47
+ iss = rbrIsArray ? (_c = (_b = rbr[0]) === null || _b === void 0 ? void 0 : _b.id) === null || _c === void 0 ? void 0 : _c.toString() : null;
48
+ }
49
+ if (!isValidNumber(iss, 1, 999999999, false))
50
+ return next({ statusCode: 400, message: `${LOGS_PREFIX}Missing iss` });
51
+ log.debug(`${LOGS_PREFIX}Create tokens for user ${iss}`);
52
+ let at;
53
+ let rt;
54
+ try {
55
+ at = sign(iss, accessDuration, "access", secrets);
56
+ rt = sign(iss, refreshDuration, "refresh", secrets);
57
+ }
58
+ catch (err) {
59
+ return next(err);
60
+ }
61
+ log.debug(`refreshToken='${rt}', accessToken='${at}'`);
62
+ res.locals.accessToken = at;
63
+ res.locals.refreshToken = rt;
64
+ if (rbrIsArray) {
65
+ rbr[0].accessToken = at;
66
+ rbr[0].refreshToken = rt;
67
+ }
68
+ next();
72
69
  }
73
70
  function decodeAccess(req, _res, next) {
74
71
  log.debug(`${LOGS_PREFIX}decode access token`);
@@ -84,38 +81,37 @@ function decodeAccess(req, _res, next) {
84
81
  log.debug(`${LOGS_PREFIX}accessToken : ${t}`);
85
82
  if (!isJWT(t))
86
83
  return next({ statusCode: 401, message: `${LOGS_PREFIX}Invalid access token` });
87
- let decodedToken = null;
84
+ let dt = null;
88
85
  try {
89
- decodedToken = verify(t, secrets, true);
86
+ dt = verify(t, secrets, true);
90
87
  }
91
88
  catch (e) {
92
89
  return next(e);
93
90
  }
94
- if (!isValidNumber(decodedToken.iss, 1, 999999999, false))
91
+ if (!isValidNumber(dt.iss, 1, 999999999, false))
95
92
  return next({ statusCode: 400, message: `${LOGS_PREFIX}Missing iss` });
96
- log.debug(`${LOGS_PREFIX}Decoded access token : ${JSON.stringify(decodedToken)}`);
97
- req.decodedAccessToken = decodedToken;
93
+ log.debug(`${LOGS_PREFIX}Decoded access token : ${JSON.stringify(dt)}`);
94
+ req.decodedAccessToken = dt;
98
95
  next();
99
96
  }
100
97
  function decodeRefresh(req, _res, next) {
101
- return __awaiter(this, void 0, void 0, function* () {
102
- const token = req.body.refreshToken;
103
- log.debug(`${LOGS_PREFIX}decodeRefresh(token=${token})`);
104
- if (!isJWT(token))
105
- return next({ statusCode: 401, message: `${LOGS_PREFIX}Invalid refresh token` });
106
- let decodedToken = null;
107
- try {
108
- decodedToken = verify(token, secrets, false);
109
- }
110
- catch (e) {
111
- return next(e);
112
- }
113
- if (!isValidNumber(decodedToken.iss, 1, 999999999, false))
114
- return next({ statusCode: 400, message: `${LOGS_PREFIX}Missing iss` });
115
- log.debug(`${LOGS_PREFIX}Decoded refresh token : ${JSON.stringify(req.decodedRefreshToken)}`);
116
- req.decodedRefreshToken = decodedToken;
117
- next();
118
- });
98
+ var _a;
99
+ const token = (_a = req.body) === null || _a === void 0 ? void 0 : _a.refreshToken;
100
+ log.debug(`${LOGS_PREFIX}decodeRefresh(token=${token})`);
101
+ if (!isJWT(token))
102
+ return next({ statusCode: 401, message: `${LOGS_PREFIX}Invalid refresh token` });
103
+ let dt = null;
104
+ try {
105
+ dt = verify(token, secrets, false);
106
+ }
107
+ catch (e) {
108
+ return next(e);
109
+ }
110
+ if (!isValidNumber(dt.iss, 1, 999999999, false))
111
+ return next({ statusCode: 400, message: `${LOGS_PREFIX}Missing iss` });
112
+ log.debug(`${LOGS_PREFIX}Decoded refresh token : ${JSON.stringify(dt)}`);
113
+ req.decodedRefreshToken = dt;
114
+ next();
119
115
  }
120
116
 
121
117
  export { decodeAccess, decodeRefresh, refresh };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dwtechs/toker-express",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Open source JWT management library for Express.js to refresh and decode tokens safely.",
5
5
  "keywords": [
6
6
  "JWT",
@@ -37,9 +37,9 @@
37
37
  "dist/"
38
38
  ],
39
39
  "dependencies": {
40
- "@dwtechs/checkard": "3.5.1",
40
+ "@dwtechs/checkard": "3.6.0",
41
41
  "@dwtechs/toker": "0.1.1",
42
- "@dwtechs/winstan": "0.4.0"
42
+ "@dwtechs/winstan": "0.5.0"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@babel/preset-env": "7.26.0",