@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 +39 -14
- package/dist/toker-express.d.ts +2 -12
- package/dist/toker-express.js +9 -9
- package/package.json +1 -1
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 `
|
|
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
|
|
166
|
-
* middleware. It only processes requests that have `
|
|
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}
|
|
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
|
|
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
|
|
193
|
-
* @param {Response}
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
284
|
+
res.locals.decodedRefreshToken = decodedToken;
|
|
260
285
|
```
|
|
261
286
|
|
|
262
287
|
|
package/dist/toker-express.d.ts
CHANGED
|
@@ -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,
|
|
47
|
-
declare function decodeRefresh(req: Request,
|
|
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,
|
package/dist/toker-express.js
CHANGED
|
@@ -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 =
|
|
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 = (
|
|
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 = (
|
|
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,
|
|
67
|
+
function decodeAccess(req, res, next) {
|
|
68
68
|
log.debug(`${LOGS_PREFIX}decode access token`);
|
|
69
|
-
if (!
|
|
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
|
-
|
|
91
|
+
res.locals.decodedAccessToken = dt;
|
|
92
92
|
next();
|
|
93
93
|
}
|
|
94
|
-
function decodeRefresh(req,
|
|
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
|
-
|
|
110
|
+
res.locals.decodedRefreshToken = dt;
|
|
111
111
|
next();
|
|
112
112
|
}
|
|
113
113
|
|