@dwtechs/toker-express 0.3.0 → 0.4.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
@@ -126,7 +126,7 @@ const refreshDuration = isNumber(REFRESH_TOKEN_DURATION, false) ? REFRESH_TOKEN_
126
126
  * Express middleware to generate new access and refresh JWT tokens for a user.
127
127
  *
128
128
  * This middleware creates new access and refresh tokens based on:
129
- * 1. The issuer (iss) from `req.decodedAccessToken.iss` if available, OR
129
+ * 1. The issuer (iss) from `res.locals.decodedAccessToken.iss` if available, OR
130
130
  * 2. The user ID from `res.locals.id` if no decoded token is present
131
131
  *
132
132
  * The generated tokens are stored in:
@@ -134,9 +134,9 @@ const refreshDuration = isNumber(REFRESH_TOKEN_DURATION, false) ? REFRESH_TOKEN_
134
134
  * - `req.body.rows[0].accessToken` and `req.body.rows[0].refreshToken` (if rows array exists)
135
135
  *
136
136
  * @param {Request} req - The Express request object. May contain:
137
- * - `req.decodedAccessToken.iss`: User ID from decoded access token
138
137
  * - `req.body.rows`: Optional array where tokens will be added to first element
139
138
  * @param {Response} res - The Express response object. Should contain:
139
+ * - `res.locals.decodedAccessToken.iss`: User ID from decoded access token (checked first), OR
140
140
  * - `res.locals.id`: User ID (used if decodedAccessToken is not available)
141
141
  * Tokens will be added to `res.locals.accessToken` and `res.locals.refreshToken`
142
142
  * @param {NextFunction} next - Express next middleware function
@@ -162,15 +162,17 @@ function refresh(req: Request, res: Response, next: NextFunction): void {}
162
162
  * Express middleware function to decode and verify an access token from the Authorization header.
163
163
  *
164
164
  * This middleware extracts the JWT access token from the Authorization header, validates its format,
165
- * verifies its signature, and attaches the decoded token to req.decodedAccessToken for use by subsequent
166
- * middleware. It only processes requests that have `req.isProtected` set to true.
165
+ * verifies its signature, and attaches the decoded token to res.locals.decodedAccessToken for use by subsequent
166
+ * middleware. It only processes requests that have `res.locals.isProtected` set to true.
167
167
  *
168
168
  * @param {Request} req - The Express request object containing the Authorization header
169
- * @param {Response} _res - The Express response object (not used in this function)
169
+ * @param {Response} res - The Express response object. Should contain:
170
+ * - `res.locals.isProtected`: Boolean flag to determine if route requires JWT protection
171
+ * Decoded token will be added to `res.locals.decodedAccessToken`
170
172
  * @param {NextFunction} next - The next middleware function to be called
171
173
  *
172
- * @returns {void} Calls the next middleware function, either with an error or successfully
173
- *
174
+ * @returns {void} Calls the next middleware function with an error object if the token is invalid or iss is missing.
175
+ *
174
176
  * @throws {MissingAuthorizationError} If the Authorization header is missing (HTTP 401)
175
177
  * @throws {InvalidBearerFormatError} If the Authorization header format is invalid (HTTP 401)
176
178
  * @throws {InvalidTokenError} If the token is malformed or has invalid structure (HTTP 401)
@@ -189,12 +191,12 @@ function decodeAccess(req: Request, _res: Response, next: NextFunction): void {}
189
191
  /**
190
192
  * Middleware function to decode and verify a refresh token from the request body.
191
193
  *
192
- * @param {Request} req - The request object containing the refresh token in the body.
193
- * @param {Response} _res - The response object (not used in this function).
194
+ * @param {Request} req - The request object containing the refresh token in `req.body.refreshToken`
195
+ * @param {Response} res - The response object. Decoded token will be added to `res.locals.decodedRefreshToken`
194
196
  * @param {NextFunction} next - The next middleware function to be called.
195
197
  *
196
- * @returns {void} Calls the next middleware function with an error object if the token is invalid or required fields are missing.
197
- *
198
+ * @returns {void} Calls the next middleware function with an error object if the token is invalid or iss is missing.
199
+ *
198
200
  * @throws {InvalidTokenError} If the token is malformed or has invalid structure (HTTP 401)
199
201
  * @throws {InvalidSecretsError} If the secrets configuration is invalid (HTTP 500)
200
202
  * @throws {ExpiredTokenError} If the refresh token has expired (exp claim) (HTTP 401)
@@ -214,7 +216,7 @@ function decodeRefresh(req: Request, _res: Response, next: NextFunction): void {
214
216
  This function will look for an ISS (user ID) from two possible sources:
215
217
 
216
218
  ```Javascript
217
- let iss = req.decodedAccessToken?.iss;
219
+ let iss = res.locals?.decodedAccessToken?.iss;
218
220
 
219
221
  if (!iss)
220
222
  iss = res.locals.id ?? null;
@@ -235,6 +237,27 @@ if (isArray(rbr, ">=", 1) && isObject(rbr[0])) {
235
237
 
236
238
  ### JWT Decoding
237
239
 
240
+ #### Route Protection with isProtected
241
+
242
+ The `decodeAccess()` middleware only processes requests when `res.locals.isProtected` is set to `true`. This allows you to selectively protect routes that require authentication.
243
+
244
+ You should set this flag in a middleware before calling `decodeAccess()`:
245
+
246
+ ```Javascript
247
+ // Example middleware to mark route as protected
248
+ function protectRoute(req, res, next) {
249
+ res.locals.isProtected = true;
250
+ next();
251
+ }
252
+
253
+ // Usage
254
+ router.get('/protected-route', protectRoute, tk.decodeAccess, yourHandler);
255
+ ```
256
+
257
+ If `res.locals.isProtected` is `false`, `undefined`, or `null`, the `decodeAccess()` middleware will simply call `next()` without processing the token, allowing the request to continue to the next middleware.
258
+
259
+ #### Access Token Decoding
260
+
238
261
  decodeAccess() functions will look for a bearer in authorization headers.
239
262
 
240
263
  ```Javascript
@@ -244,9 +267,11 @@ const bearer = req.headers.authorization;
244
267
  It will then send the decoded token in the res object.
245
268
 
246
269
  ```Javascript
247
- req.decodedAccessToken = decodedToken;
270
+ res.locals.decodedAccessToken = decodedToken;
248
271
  ```
249
272
 
273
+ #### Refresh Token Decoding
274
+
250
275
  decodeRefresh() functions will look for a token in the client request body.
251
276
 
252
277
  ```Javascript
@@ -256,7 +281,7 @@ const token = req.body.refreshToken;
256
281
  It will then send the decoded token in the res object.
257
282
 
258
283
  ```Javascript
259
- req.decodedRefreshToken = decodedToken;
284
+ res.locals.decodedRefreshToken = decodedToken;
260
285
  ```
261
286
 
262
287
 
@@ -32,19 +32,9 @@ export interface RowWithTokens {
32
32
  [key: string]: any;
33
33
  }
34
34
 
35
- declare global {
36
- namespace Express {
37
- interface Request {
38
- isProtected?: boolean;
39
- decodedAccessToken?: any;
40
- decodedRefreshToken?: any;
41
- }
42
- }
43
- }
44
-
45
35
  declare function refresh(req: Request, res: Response, next: NextFunction): void;
46
- declare function decodeAccess(req: Request, _res: Response, next: NextFunction): void;
47
- declare function decodeRefresh(req: Request, _res: Response, next: NextFunction): void;
36
+ declare function decodeAccess(req: Request, res: Response, next: NextFunction): void;
37
+ declare function decodeRefresh(req: Request, res: Response, next: NextFunction): void;
48
38
 
49
39
  export {
50
40
  refresh,
@@ -38,10 +38,10 @@ 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
40
  function refresh(req, res, next) {
41
- var _a, _b, _c;
42
- let iss = (_a = req.decodedAccessToken) === null || _a === void 0 ? void 0 : _a.iss;
41
+ var _a, _b, _c, _d;
42
+ let iss = (_b = (_a = res.locals) === null || _a === void 0 ? void 0 : _a.decodedAccessToken) === null || _b === void 0 ? void 0 : _b.iss;
43
43
  if (!iss)
44
- iss = (_b = res.locals.id) !== null && _b !== void 0 ? _b : null;
44
+ iss = (_c = res.locals.id) !== null && _c !== void 0 ? _c : null;
45
45
  if (!isValidNumber(iss, 1, 999999999, false))
46
46
  return next({ statusCode: 400, message: `${LOGS_PREFIX}Missing iss` });
47
47
  log.debug(`${LOGS_PREFIX}Create tokens for user ${iss}`);
@@ -57,16 +57,16 @@ function refresh(req, res, next) {
57
57
  log.debug(`refreshToken='${rt}', accessToken='${at}'`);
58
58
  res.locals.accessToken = at;
59
59
  res.locals.refreshToken = rt;
60
- const rbr = (_c = req.body) === null || _c === void 0 ? void 0 : _c.rows;
60
+ const rbr = (_d = req.body) === null || _d === void 0 ? void 0 : _d.rows;
61
61
  if (isArray(rbr, ">=", 1) && isObject(rbr[0])) {
62
62
  rbr[0].accessToken = at;
63
63
  rbr[0].refreshToken = rt;
64
64
  }
65
65
  next();
66
66
  }
67
- function decodeAccess(req, _res, next) {
67
+ function decodeAccess(req, res, next) {
68
68
  log.debug(`${LOGS_PREFIX}decode access token`);
69
- if (!req.isProtected)
69
+ if (!res.locals.isProtected)
70
70
  return next();
71
71
  let t;
72
72
  try {
@@ -88,10 +88,10 @@ function decodeAccess(req, _res, next) {
88
88
  if (!isValidNumber(dt.iss, 1, 999999999, false))
89
89
  return next({ statusCode: 400, message: `${LOGS_PREFIX}Missing iss` });
90
90
  log.debug(`${LOGS_PREFIX}Decoded access token : ${JSON.stringify(dt)}`);
91
- req.decodedAccessToken = dt;
91
+ res.locals.decodedAccessToken = dt;
92
92
  next();
93
93
  }
94
- function decodeRefresh(req, _res, next) {
94
+ function decodeRefresh(req, res, next) {
95
95
  var _a;
96
96
  const token = (_a = req.body) === null || _a === void 0 ? void 0 : _a.refreshToken;
97
97
  log.debug(`${LOGS_PREFIX}decodeRefresh(token=${token})`);
@@ -107,7 +107,7 @@ function decodeRefresh(req, _res, next) {
107
107
  if (!isValidNumber(dt.iss, 1, 999999999, false))
108
108
  return next({ statusCode: 400, message: `${LOGS_PREFIX}Missing iss` });
109
109
  log.debug(`${LOGS_PREFIX}Decoded refresh token : ${JSON.stringify(dt)}`);
110
- req.decodedRefreshToken = dt;
110
+ res.locals.decodedRefreshToken = dt;
111
111
  next();
112
112
  }
113
113
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dwtechs/toker-express",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Open source JWT management library for Express.js to refresh and decode tokens safely.",
5
5
  "keywords": [
6
6
  "JWT",