@flink-app/jwt-auth-plugin 1.0.0 → 2.0.0-alpha.101

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/CHANGELOG.md CHANGED
@@ -1,5 +1,436 @@
1
1
  # @flink-app/jwt-auth-plugin
2
2
 
3
+ ## 2.0.0-alpha.101
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [b11f2f8]
8
+ - @flink-app/flink@2.0.0-alpha.101
9
+
10
+ ## 2.0.0-alpha.100
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [6e8166a]
15
+ - @flink-app/flink@2.0.0-alpha.100
16
+
17
+ ## 2.0.0-alpha.99
18
+
19
+ ### Patch Changes
20
+
21
+ - Updated dependencies [ed9cd2c]
22
+ - @flink-app/flink@2.0.0-alpha.99
23
+
24
+ ## 2.0.0-alpha.98
25
+
26
+ ### Minor Changes
27
+
28
+ - 2f4a132: feat: expose verified token on the request
29
+
30
+ The JWT auth plugin now populates `req.token` (the decoded, signature-verified
31
+ payload) and `req.rawToken` (the original token string) after successful
32
+ authentication. Handlers can read token claims such as `jti` directly instead of
33
+ re-extracting and re-decoding the bearer token with an unverified decode.
34
+
35
+ ```ts
36
+ // Before
37
+ const authHeader = req.headers.authorization;
38
+ const bearer = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : undefined;
39
+ const decoded = bearer ? (jsonwebtoken.decode(bearer) as { jti?: string }) : null;
40
+ const sessionId = decoded?.jti ?? legacySessionId(req.user._id);
41
+
42
+ // After
43
+ const { jti } = (req.token as { jti?: string }) ?? {};
44
+ const sessionId = jti ?? legacySessionId(req.user._id);
45
+ ```
46
+
47
+ `token` / `rawToken` are added as optional fields on the core `FlinkRequest`
48
+ type, populated by auth plugins that have a token concept.
49
+
50
+ ### Patch Changes
51
+
52
+ - Updated dependencies [2f4a132]
53
+ - @flink-app/flink@2.0.0-alpha.98
54
+
55
+ ## 2.0.0-alpha.97
56
+
57
+ ### Minor Changes
58
+
59
+ - eed6bc1: Pass host application context to auth plugins.
60
+
61
+ `FlinkAuthPlugin.authenticateRequest` now receives the host app's context as an optional third argument. Flink core supplies it on every authenticated request, so auth plugins can access repos/services without the caller wiring up a lazy accessor or maintaining a module-level mutable reference.
62
+
63
+ For `@flink-app/jwt-auth-plugin`, this means `getUser` receives the context directly:
64
+
65
+ ```typescript
66
+ jwtAuthPlugin({
67
+ getUser: async (tokenData, req, ctx) => {
68
+ const user = await ctx?.repos.userRepo.getById(tokenData._id);
69
+ return user ? { username: user.username, _id: user._id } : null;
70
+ },
71
+ });
72
+ ```
73
+
74
+ The `getContext` option previously added in alpha.96 is no longer needed and has been removed — remove it from your `jwtAuthPlugin({ … })` call. The third `ctx` parameter on `getUser` is optional, so callbacks that ignore it are unaffected.
75
+
76
+ ### Patch Changes
77
+
78
+ - Updated dependencies [eed6bc1]
79
+ - @flink-app/flink@2.0.0-alpha.97
80
+
81
+ ## 2.0.0-alpha.96
82
+
83
+ ### Patch Changes
84
+
85
+ - e431237: Add `iat` (issued-at) claim to generated JWTs.
86
+
87
+ `iat` is a standard JWT claim that records when the token was issued (Unix seconds). Adding it enables consumers to implement stateless force-logout / token-revocation patterns by storing a per-user "tokens-valid-from" timestamp and rejecting any token whose `iat` is older.
88
+
89
+ Backwards compatible — the claim is informational and does not affect verification of existing tokens. If a caller's payload already contains an `iat` field it takes precedence (useful for tests).
90
+
91
+ - Updated dependencies
92
+ - @flink-app/flink@2.0.0-alpha.96
93
+
94
+ ## 2.0.0-alpha.95
95
+
96
+ ### Patch Changes
97
+
98
+ - @flink-app/flink@2.0.0-alpha.95
99
+
100
+ ## 2.0.0-alpha.94
101
+
102
+ ### Patch Changes
103
+
104
+ - @flink-app/flink@2.0.0-alpha.94
105
+
106
+ ## 2.0.0-alpha.93
107
+
108
+ ### Patch Changes
109
+
110
+ - @flink-app/flink@2.0.0-alpha.93
111
+
112
+ ## 2.0.0-alpha.92
113
+
114
+ ### Patch Changes
115
+
116
+ - Updated dependencies [8d23d56]
117
+ - @flink-app/flink@2.0.0-alpha.92
118
+
119
+ ## 2.0.0-alpha.91
120
+
121
+ ### Patch Changes
122
+
123
+ - @flink-app/flink@2.0.0-alpha.91
124
+
125
+ ## 2.0.0-alpha.90
126
+
127
+ ### Patch Changes
128
+
129
+ - Updated dependencies [0d84b5f]
130
+ - @flink-app/flink@2.0.0-alpha.90
131
+
132
+ ## 2.0.0-alpha.89
133
+
134
+ ### Patch Changes
135
+
136
+ - @flink-app/flink@2.0.0-alpha.89
137
+
138
+ ## 2.0.0-alpha.88
139
+
140
+ ### Patch Changes
141
+
142
+ - @flink-app/flink@2.0.0-alpha.88
143
+
144
+ ## 2.0.0-alpha.87
145
+
146
+ ### Patch Changes
147
+
148
+ - Updated dependencies
149
+ - @flink-app/flink@2.0.0-alpha.87
150
+
151
+ ## 2.0.0-alpha.86
152
+
153
+ ### Patch Changes
154
+
155
+ - @flink-app/flink@2.0.0-alpha.86
156
+
157
+ ## 2.0.0-alpha.85
158
+
159
+ ### Minor Changes
160
+
161
+ - 179c085: Add support for trailing-segment wildcard scopes in `rolePermissions`. A role scope ending in `.*` (e.g. `"license.subscription.*"`) now matches any required route permission that starts with that prefix including the trailing dot — `"license.subscription.get"` and `"license.subscription.cancel"` match, while `"license.subscription"` and `"license.subscriptions.get"` do not. Wildcards are segment-scoped to prevent accidental privilege escalation, and only trailing `.*` is special. Existing behavior (route `"*"`, role `"*"`, exact-match scopes, empty permission arrays) is unchanged.
162
+
163
+ ### Patch Changes
164
+
165
+ - @flink-app/flink@2.0.0-alpha.85
166
+
167
+ ## 2.0.0-alpha.84
168
+
169
+ ### Patch Changes
170
+
171
+ - @flink-app/flink@2.0.0-alpha.84
172
+
173
+ ## 2.0.0-alpha.83
174
+
175
+ ### Patch Changes
176
+
177
+ - Updated dependencies
178
+ - @flink-app/flink@2.0.0-alpha.83
179
+
180
+ ## 2.0.0-alpha.82
181
+
182
+ ### Patch Changes
183
+
184
+ - @flink-app/flink@2.0.0-alpha.82
185
+
186
+ ## 2.0.0-alpha.81
187
+
188
+ ### Patch Changes
189
+
190
+ - @flink-app/flink@2.0.0-alpha.81
191
+
192
+ ## 2.0.0-alpha.80
193
+
194
+ ### Patch Changes
195
+
196
+ - @flink-app/flink@2.0.0-alpha.80
197
+
198
+ ## 2.0.0-alpha.79
199
+
200
+ ### Patch Changes
201
+
202
+ - @flink-app/flink@2.0.0-alpha.79
203
+
204
+ ## 2.0.0-alpha.78
205
+
206
+ ### Patch Changes
207
+
208
+ - @flink-app/flink@2.0.0-alpha.78
209
+
210
+ ## 2.0.0-alpha.77
211
+
212
+ ### Patch Changes
213
+
214
+ - @flink-app/flink@2.0.0-alpha.77
215
+
216
+ ## 2.0.0-alpha.76
217
+
218
+ ### Minor Changes
219
+
220
+ - Add resolveTokenTTL callback to jwt-auth-plugin for dynamic token TTL based on role. Fix renames in email-plugin and inbound-email-plugin. Fix tsconfig in generic-auth-plugin.
221
+
222
+ ### Patch Changes
223
+
224
+ - @flink-app/flink@2.0.0-alpha.76
225
+
226
+ ## 2.0.0-alpha.75
227
+
228
+ ### Patch Changes
229
+
230
+ - @flink-app/flink@2.0.0-alpha.75
231
+
232
+ ## 2.0.0-alpha.74
233
+
234
+ ### Patch Changes
235
+
236
+ - Updated dependencies
237
+ - Updated dependencies
238
+ - @flink-app/flink@2.0.0-alpha.74
239
+
240
+ ## 2.0.0-alpha.73
241
+
242
+ ### Patch Changes
243
+
244
+ - Updated dependencies
245
+ - @flink-app/flink@2.0.0-alpha.73
246
+
247
+ ## 2.0.0-alpha.72
248
+
249
+ ### Patch Changes
250
+
251
+ - Updated dependencies
252
+ - @flink-app/flink@2.0.0-alpha.72
253
+
254
+ ## 2.0.0-alpha.71
255
+
256
+ ### Patch Changes
257
+
258
+ - @flink-app/flink@2.0.0-alpha.71
259
+
260
+ ## 2.0.0-alpha.70
261
+
262
+ ### Patch Changes
263
+
264
+ - @flink-app/flink@2.0.0-alpha.70
265
+
266
+ ## 2.0.0-alpha.69
267
+
268
+ ### Patch Changes
269
+
270
+ - @flink-app/flink@2.0.0-alpha.69
271
+
272
+ ## 2.0.0-alpha.68
273
+
274
+ ### Patch Changes
275
+
276
+ - @flink-app/flink@2.0.0-alpha.68
277
+
278
+ ## 2.0.0-alpha.67
279
+
280
+ ### Patch Changes
281
+
282
+ - Updated dependencies [418cb54]
283
+ - Updated dependencies [70d574c]
284
+ - @flink-app/flink@2.0.0-alpha.67
285
+
286
+ ## 2.0.0-alpha.66
287
+
288
+ ### Patch Changes
289
+
290
+ - Updated dependencies [5593d26]
291
+ - @flink-app/flink@2.0.0-alpha.66
292
+
293
+ ## 2.0.0-alpha.65
294
+
295
+ ### Patch Changes
296
+
297
+ - @flink-app/flink@2.0.0-alpha.65
298
+
299
+ ## 2.0.0-alpha.64
300
+
301
+ ### Patch Changes
302
+
303
+ - Updated dependencies [95b99ee]
304
+ - @flink-app/flink@2.0.0-alpha.64
305
+
306
+ ## 2.0.0-alpha.63
307
+
308
+ ### Patch Changes
309
+
310
+ - Updated dependencies [810df2c]
311
+ - Updated dependencies [8dd0752]
312
+ - @flink-app/flink@2.0.0-alpha.63
313
+
314
+ ## 2.0.0-alpha.62
315
+
316
+ ### Patch Changes
317
+
318
+ - Updated dependencies
319
+ - @flink-app/flink@2.0.0-alpha.62
320
+
321
+ ## 2.0.0-alpha.61
322
+
323
+ ### Patch Changes
324
+
325
+ - @flink-app/flink@2.0.0-alpha.61
326
+
327
+ ## 2.0.0-alpha.60
328
+
329
+ ### Patch Changes
330
+
331
+ - @flink-app/flink@2.0.0-alpha.60
332
+
333
+ ## 2.0.0-alpha.59
334
+
335
+ ### Patch Changes
336
+
337
+ - Updated dependencies [dbc2119]
338
+ - @flink-app/flink@2.0.0-alpha.59
339
+
340
+ ## 2.0.0-alpha.58
341
+
342
+ ### Patch Changes
343
+
344
+ - Updated dependencies
345
+ - @flink-app/flink@2.0.0-alpha.58
346
+
347
+ ## 2.0.0-alpha.57
348
+
349
+ ### Patch Changes
350
+
351
+ - Updated dependencies [ef7f495]
352
+ - Updated dependencies [2d53aa4]
353
+ - Updated dependencies [b3cc5d1]
354
+ - Updated dependencies [d377fac]
355
+ - Updated dependencies [4ad44c0]
356
+ - Updated dependencies [90f80c7]
357
+ - Updated dependencies [f0887e7]
358
+ - Updated dependencies [68c46d3]
359
+ - Updated dependencies [4ad44c0]
360
+ - Updated dependencies
361
+ - @flink-app/flink@2.0.0-alpha.57
362
+
363
+ ## 2.0.0-alpha.56
364
+
365
+ ### Patch Changes
366
+
367
+ - Zod 3.x compat
368
+ - Updated dependencies
369
+ - Updated dependencies [0203256]
370
+ - @flink-app/flink@2.0.0-alpha.56
371
+
372
+ ## 2.0.0-alpha.55
373
+
374
+ ### Patch Changes
375
+
376
+ - Updated dependencies [3267206]
377
+ - Updated dependencies [497b7c2]
378
+ - Updated dependencies [3267206]
379
+ - @flink-app/flink@2.0.0-alpha.55
380
+
381
+ ## 2.0.0-alpha.53
382
+
383
+ ### Patch Changes
384
+
385
+ - Updated dependencies [669c806]
386
+ - @flink-app/flink@2.0.0-alpha.53
387
+
388
+ ## 2.0.0-alpha.52
389
+
390
+ ### Patch Changes
391
+
392
+ - Updated dependencies [fe9f8f2]
393
+ - @flink-app/flink@2.0.0-alpha.52
394
+
395
+ ## 2.0.0-alpha.51
396
+
397
+ ### Patch Changes
398
+
399
+ - fix: add peerDependencies to all plugins and adapters
400
+
401
+ **Breaking Change Prevention:**
402
+
403
+ - Added "@flink-app/flink": ">=2.0.0-alpha.0" as peerDependency to all plugins
404
+ - Fixed oidc-plugin peerDependencies from "workspace:\*" to proper version ranges
405
+ - Fixed oauth-plugin to include @flink-app/flink peerDependency
406
+
407
+ **JWT Auth Plugin Interface Fix:**
408
+
409
+ - Removed redundant createToken declaration from JwtAuthPlugin interface
410
+ - The method is already inherited from FlinkAuthPlugin, this fixes TypeScript type checking issues
411
+
412
+ **Why this matters:**
413
+
414
+ - Ensures only one version of @flink-app/flink exists in node_modules
415
+ - Prevents type conflicts when TypeScript resolves interfaces across packages
416
+ - Follows standard plugin architecture best practices (same pattern as React, Webpack, etc.)
417
+ - Package managers now warn if incompatible versions are installed
418
+ - @flink-app/flink@2.0.0-alpha.51
419
+
420
+ ## 2.0.0-alpha.50
421
+
422
+ ## 2.0.0-alpha.49
423
+
424
+ ### Patch Changes
425
+
426
+ - Typescript include fix
427
+
428
+ ## 2.0.0-alpha.48
429
+
430
+ ### Minor Changes
431
+
432
+ - AI features
433
+
3
434
  ## 1.0.0
4
435
 
5
436
  ### Minor Changes
@@ -20,10 +20,28 @@ export type TokenExtractor = (req: FlinkRequest) => string | null | undefined;
20
20
  * @returns true if user has required permissions, false otherwise
21
21
  */
22
22
  export type PermissionChecker = (user: FlinkAuthUser, routePermissions: string[]) => Promise<boolean> | boolean;
23
+ /**
24
+ * Custom token TTL resolver callback.
25
+ *
26
+ * Called during token creation to determine expiration dynamically based on roles.
27
+ *
28
+ * Return values:
29
+ * - `number`: TTL in milliseconds to use for this token
30
+ * - `undefined`: Fall back to static `tokenTTL` option (or default ~100 years)
31
+ */
32
+ export type TokenTTLResolver = (roles: string[], payload: any) => number | undefined;
23
33
  export interface JwtAuthPluginOptions {
24
34
  secret: string;
25
35
  algo?: jwtSimple.TAlgorithm;
26
- getUser: (tokenData: any, req: FlinkRequest) => Promise<FlinkAuthUser | null | undefined>;
36
+ /**
37
+ * @param ctx The host application's context, supplied by Flink core on
38
+ * each authenticated request. Lets the callback access repos/services
39
+ * that don't exist when the plugin is constructed (e.g. before
40
+ * `FlinkApp.start()` resolves) without forcing the consumer to keep a
41
+ * module-level mutable reference. Undefined only when invoked outside
42
+ * the normal request pipeline (e.g. unit tests).
43
+ */
44
+ getUser: (tokenData: any, req: FlinkRequest, ctx?: any) => Promise<FlinkAuthUser | null | undefined>;
27
45
  passwordPolicy?: RegExp;
28
46
  tokenTTL?: number;
29
47
  rolePermissions: {
@@ -73,15 +91,25 @@ export interface JwtAuthPluginOptions {
73
91
  * ```
74
92
  */
75
93
  useDynamicRoles?: boolean;
76
- }
77
- export interface JwtAuthPlugin extends FlinkAuthPlugin {
78
94
  /**
79
- * Encodes and returns JWT token that includes provided payload.
95
+ * Optional dynamic token TTL resolver.
96
+ *
97
+ * When provided, called during token creation with the user's roles and payload.
98
+ * Return a TTL in milliseconds to override the static `tokenTTL`, or `undefined`
99
+ * to fall back to `tokenTTL` (or the default).
80
100
  *
81
- * The payload can by anything but should in most cases be and object that
82
- * holds user information including an identifier such as the username or id.
101
+ * Example:
102
+ * ```typescript
103
+ * resolveTokenTTL: (roles) => {
104
+ * if (roles.includes("service-account")) return 1000 * 60 * 60 * 24 * 365; // 1 year
105
+ * if (roles.includes("admin")) return 1000 * 60 * 60 * 8; // 8 hours
106
+ * return 1000 * 60 * 60; // 1 hour default
107
+ * }
108
+ * ```
83
109
  */
84
- createToken: (payload: any, roles: string[]) => Promise<string>;
110
+ resolveTokenTTL?: TokenTTLResolver;
111
+ }
112
+ export interface JwtAuthPlugin extends FlinkAuthPlugin {
85
113
  /**
86
114
  * Generates new password hash and salt for provided password.
87
115
  *
@@ -102,5 +130,4 @@ export interface JwtAuthPlugin extends FlinkAuthPlugin {
102
130
  /**
103
131
  * Configures and creates authentication plugin.
104
132
  */
105
- export declare function jwtAuthPlugin({ secret, getUser, rolePermissions, algo, passwordPolicy, tokenTTL, //Defaults to hundred year
106
- tokenExtractor, checkPermissions, useDynamicRoles, }: JwtAuthPluginOptions): JwtAuthPlugin;
133
+ export declare function jwtAuthPlugin({ secret, getUser, rolePermissions, algo, passwordPolicy, tokenTTL, tokenExtractor, checkPermissions, useDynamicRoles, resolveTokenTTL, }: JwtAuthPluginOptions): JwtAuthPlugin;
@@ -65,12 +65,11 @@ var defaultPasswordPolicy = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/;
65
65
  */
66
66
  function jwtAuthPlugin(_a) {
67
67
  var _this = this;
68
- var secret = _a.secret, getUser = _a.getUser, rolePermissions = _a.rolePermissions, _b = _a.algo, algo = _b === void 0 ? "HS256" : _b, _c = _a.passwordPolicy, passwordPolicy = _c === void 0 ? defaultPasswordPolicy : _c, _d = _a.tokenTTL, tokenTTL = _d === void 0 ? 1000 * 60 * 60 * 24 * 365 * 100 : _d, //Defaults to hundred year
69
- tokenExtractor = _a.tokenExtractor, checkPermissions = _a.checkPermissions, _e = _a.useDynamicRoles, useDynamicRoles = _e === void 0 ? false : _e;
68
+ var secret = _a.secret, getUser = _a.getUser, rolePermissions = _a.rolePermissions, _b = _a.algo, algo = _b === void 0 ? "HS256" : _b, _c = _a.passwordPolicy, passwordPolicy = _c === void 0 ? defaultPasswordPolicy : _c, tokenTTL = _a.tokenTTL, tokenExtractor = _a.tokenExtractor, checkPermissions = _a.checkPermissions, _d = _a.useDynamicRoles, useDynamicRoles = _d === void 0 ? false : _d, resolveTokenTTL = _a.resolveTokenTTL;
70
69
  return {
71
- authenticateRequest: function (req, permissions) { return __awaiter(_this, void 0, void 0, function () {
70
+ authenticateRequest: function (req, permissions, ctx) { return __awaiter(_this, void 0, void 0, function () {
72
71
  return __generator(this, function (_a) {
73
- return [2 /*return*/, authenticateRequest(req, permissions, rolePermissions, {
72
+ return [2 /*return*/, authenticateRequest(req, permissions, rolePermissions, ctx, {
74
73
  algo: algo,
75
74
  secret: secret,
76
75
  getUser: getUser,
@@ -80,13 +79,13 @@ function jwtAuthPlugin(_a) {
80
79
  })];
81
80
  });
82
81
  }); },
83
- createToken: function (payload, roles) { return createToken(__assign(__assign({}, payload), { roles: roles }), { algo: algo, secret: secret, tokenTTL: tokenTTL }); },
82
+ createToken: function (payload, roles) { return createToken(__assign(__assign({}, payload), { roles: roles }), { algo: algo, secret: secret, tokenTTL: tokenTTL, resolveTokenTTL: resolveTokenTTL }, roles); },
84
83
  createPasswordHashAndSalt: function (password) { return createPasswordHashAndSalt(password, passwordPolicy); },
85
84
  validatePassword: validatePassword,
86
85
  };
87
86
  }
88
- function authenticateRequest(req_1, routePermissions_1, rolePermissions_1, _a) {
89
- return __awaiter(this, arguments, void 0, function (req, routePermissions, rolePermissions, _b) {
87
+ function authenticateRequest(req_1, routePermissions_1, rolePermissions_1, ctx_1, _a) {
88
+ return __awaiter(this, arguments, void 0, function (req, routePermissions, rolePermissions, ctx, _b) {
90
89
  var token, decodedToken, permissionsArr, validPerms, user, validPerms, hasPermission;
91
90
  var secret = _b.secret, algo = _b.algo, getUser = _b.getUser, tokenExtractor = _b.tokenExtractor, checkPermissions = _b.checkPermissions, useDynamicRoles = _b.useDynamicRoles;
92
91
  return __generator(this, function (_c) {
@@ -123,7 +122,7 @@ function authenticateRequest(req_1, routePermissions_1, rolePermissions_1, _a) {
123
122
  return [2 /*return*/, false];
124
123
  }
125
124
  }
126
- return [4 /*yield*/, getUser(decodedToken, req)];
125
+ return [4 /*yield*/, getUser(decodedToken, req, ctx)];
127
126
  case 1:
128
127
  user = _c.sent();
129
128
  if (!user) {
@@ -149,6 +148,16 @@ function authenticateRequest(req_1, routePermissions_1, rolePermissions_1, _a) {
149
148
  _c.label = 3;
150
149
  case 3:
151
150
  req.user = user;
151
+ // Expose the verified token so handlers can read claims (e.g. jti)
152
+ // without re-extracting and re-decoding (and without an unverified decode).
153
+ req.token = decodedToken;
154
+ req.rawToken = token;
155
+ // Populate userPermissions from resolved permissions
156
+ req.userPermissions = computeUserPermissions(user, decodedToken, {
157
+ useDynamicRoles: useDynamicRoles,
158
+ rolePermissions: rolePermissions,
159
+ checkPermissions: checkPermissions,
160
+ });
152
161
  return [2 /*return*/, true];
153
162
  case 4: return [2 /*return*/, false];
154
163
  }
@@ -163,14 +172,19 @@ function getTokenFromReq(req) {
163
172
  }
164
173
  return;
165
174
  }
166
- function createToken(payload_1, _a) {
167
- return __awaiter(this, arguments, void 0, function (payload, _b) {
168
- var secret = _b.secret, algo = _b.algo, tokenTTL = _b.tokenTTL;
169
- return __generator(this, function (_c) {
175
+ function createToken(payload_1, _a, roles_1) {
176
+ return __awaiter(this, arguments, void 0, function (payload, _b, roles) {
177
+ var resolvedTTL, effectiveTTL, iat;
178
+ var _c;
179
+ var secret = _b.secret, algo = _b.algo, tokenTTL = _b.tokenTTL, resolveTokenTTL = _b.resolveTokenTTL;
180
+ return __generator(this, function (_d) {
170
181
  if (!payload) {
171
182
  throw new Error("Cannot create token - payload is missing");
172
183
  }
173
- return [2 /*return*/, jwt_simple_1.default.encode(__assign({ exp: _calculateExpiration(tokenTTL || 1000 * 60 * 60 * 24 * 365 * 100) }, payload), secret, algo)];
184
+ resolvedTTL = resolveTokenTTL === null || resolveTokenTTL === void 0 ? void 0 : resolveTokenTTL(roles, payload);
185
+ effectiveTTL = (_c = resolvedTTL !== null && resolvedTTL !== void 0 ? resolvedTTL : tokenTTL) !== null && _c !== void 0 ? _c : 1000 * 60 * 60 * 24 * 365 * 100;
186
+ iat = Math.floor(Date.now() / 1000);
187
+ return [2 /*return*/, jwt_simple_1.default.encode(__assign({ iat: iat, exp: _calculateExpiration(effectiveTTL) }, payload), secret, algo)];
174
188
  });
175
189
  });
176
190
  }
@@ -211,3 +225,40 @@ function validatePassword(password, passwordHash, salt) {
211
225
  });
212
226
  });
213
227
  }
228
+ /**
229
+ * Compute user permissions based on auth plugin configuration
230
+ *
231
+ * Strategies (in order):
232
+ * 1. User object has permissions directly (from getUser callback)
233
+ * 2. Dynamic roles - map user.roles to permissions
234
+ * 3. Static roles from token - map token.roles to permissions
235
+ * 4. No permissions available - return empty array
236
+ */
237
+ function computeUserPermissions(user, decodedToken, config) {
238
+ // Strategy 1: User object has permissions directly (from getUser callback)
239
+ if (user.permissions && Array.isArray(user.permissions)) {
240
+ return user.permissions;
241
+ }
242
+ // Strategy 2: Dynamic roles - map user.roles to permissions
243
+ if (config.useDynamicRoles && user.roles && Array.isArray(user.roles)) {
244
+ return flattenRolesToPermissions(user.roles, config.rolePermissions);
245
+ }
246
+ // Strategy 3: Static roles from token - map token.roles to permissions
247
+ if (decodedToken.roles && Array.isArray(decodedToken.roles)) {
248
+ return flattenRolesToPermissions(decodedToken.roles, config.rolePermissions);
249
+ }
250
+ // Strategy 4: No permissions available
251
+ return [];
252
+ }
253
+ /**
254
+ * Convert roles to permissions using rolePermissions mapping
255
+ */
256
+ function flattenRolesToPermissions(roles, roleMap) {
257
+ var perms = new Set();
258
+ for (var _i = 0, roles_1 = roles; _i < roles_1.length; _i++) {
259
+ var role = roles_1[_i];
260
+ var rolePerms = roleMap[role] || [];
261
+ rolePerms.forEach(function (p) { return perms.add(p); });
262
+ }
263
+ return Array.from(perms);
264
+ }
@@ -2,6 +2,23 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.hasValidPermissions = hasValidPermissions;
4
4
  var flink_1 = require("@flink-app/flink");
5
+ /**
6
+ * Checks whether a single role scope entry matches a required route
7
+ * permission. Supports exact match and trailing-segment wildcards of the
8
+ * form `"foo.bar.*"`, which match any route permission that starts with
9
+ * `"foo.bar."` (segment-scoped — does NOT match `"foo.bar"` itself, and
10
+ * does NOT match `"foo.barbaz.x"`).
11
+ */
12
+ function scopeMatches(scope, routePerm) {
13
+ if (scope === routePerm)
14
+ return true;
15
+ if (scope.endsWith(".*")) {
16
+ // Keep the trailing "." so "a.*" does not match "a" or "ab.c".
17
+ var prefix = scope.slice(0, -1);
18
+ return routePerm.startsWith(prefix);
19
+ }
20
+ return false;
21
+ }
5
22
  /**
6
23
  * Checks if provided role has permission to access route
7
24
  * based on its permissions.
@@ -28,7 +45,7 @@ function hasValidPermissions(roles, rolePermissions, routePermissions) {
28
45
  return { value: true };
29
46
  }
30
47
  if (routePermissions.every(function (routePerm) {
31
- return thisRolesPermissions.includes(routePerm);
48
+ return thisRolesPermissions.some(function (scope) { return scopeMatches(scope, routePerm); });
32
49
  })) {
33
50
  return { value: true };
34
51
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flink-app/jwt-auth-plugin",
3
- "version": "1.0.0",
3
+ "version": "2.0.0-alpha.101",
4
4
  "description": "Flink plugin for JWT auth",
5
5
  "author": "joel@frost.se",
6
6
  "license": "MIT",
@@ -19,7 +19,10 @@
19
19
  "@types/node": "22.13.10",
20
20
  "ts-node": "^10.9.2",
21
21
  "tsc-watch": "^4.2.9",
22
- "@flink-app/flink": "1.0.0"
22
+ "@flink-app/flink": "2.0.0-alpha.101"
23
+ },
24
+ "peerDependencies": {
25
+ "@flink-app/flink": ">=2.0.0-alpha.101"
23
26
  },
24
27
  "gitHead": "4243e3b3cd6d4e1ca001a61baa8436bf2bbe4113",
25
28
  "scripts": {