@flink-app/jwt-auth-plugin 0.14.1 → 2.0.0-alpha.100

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,11 +1,440 @@
1
1
  # @flink-app/jwt-auth-plugin
2
2
 
3
+ ## 2.0.0-alpha.100
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [6e8166a]
8
+ - @flink-app/flink@2.0.0-alpha.100
9
+
10
+ ## 2.0.0-alpha.99
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [ed9cd2c]
15
+ - @flink-app/flink@2.0.0-alpha.99
16
+
17
+ ## 2.0.0-alpha.98
18
+
19
+ ### Minor Changes
20
+
21
+ - 2f4a132: feat: expose verified token on the request
22
+
23
+ The JWT auth plugin now populates `req.token` (the decoded, signature-verified
24
+ payload) and `req.rawToken` (the original token string) after successful
25
+ authentication. Handlers can read token claims such as `jti` directly instead of
26
+ re-extracting and re-decoding the bearer token with an unverified decode.
27
+
28
+ ```ts
29
+ // Before
30
+ const authHeader = req.headers.authorization;
31
+ const bearer = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : undefined;
32
+ const decoded = bearer ? (jsonwebtoken.decode(bearer) as { jti?: string }) : null;
33
+ const sessionId = decoded?.jti ?? legacySessionId(req.user._id);
34
+
35
+ // After
36
+ const { jti } = (req.token as { jti?: string }) ?? {};
37
+ const sessionId = jti ?? legacySessionId(req.user._id);
38
+ ```
39
+
40
+ `token` / `rawToken` are added as optional fields on the core `FlinkRequest`
41
+ type, populated by auth plugins that have a token concept.
42
+
43
+ ### Patch Changes
44
+
45
+ - Updated dependencies [2f4a132]
46
+ - @flink-app/flink@2.0.0-alpha.98
47
+
48
+ ## 2.0.0-alpha.97
49
+
50
+ ### Minor Changes
51
+
52
+ - eed6bc1: Pass host application context to auth plugins.
53
+
54
+ `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.
55
+
56
+ For `@flink-app/jwt-auth-plugin`, this means `getUser` receives the context directly:
57
+
58
+ ```typescript
59
+ jwtAuthPlugin({
60
+ getUser: async (tokenData, req, ctx) => {
61
+ const user = await ctx?.repos.userRepo.getById(tokenData._id);
62
+ return user ? { username: user.username, _id: user._id } : null;
63
+ },
64
+ });
65
+ ```
66
+
67
+ 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.
68
+
69
+ ### Patch Changes
70
+
71
+ - Updated dependencies [eed6bc1]
72
+ - @flink-app/flink@2.0.0-alpha.97
73
+
74
+ ## 2.0.0-alpha.96
75
+
76
+ ### Patch Changes
77
+
78
+ - e431237: Add `iat` (issued-at) claim to generated JWTs.
79
+
80
+ `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.
81
+
82
+ 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).
83
+
84
+ - Updated dependencies
85
+ - @flink-app/flink@2.0.0-alpha.96
86
+
87
+ ## 2.0.0-alpha.95
88
+
89
+ ### Patch Changes
90
+
91
+ - @flink-app/flink@2.0.0-alpha.95
92
+
93
+ ## 2.0.0-alpha.94
94
+
95
+ ### Patch Changes
96
+
97
+ - @flink-app/flink@2.0.0-alpha.94
98
+
99
+ ## 2.0.0-alpha.93
100
+
101
+ ### Patch Changes
102
+
103
+ - @flink-app/flink@2.0.0-alpha.93
104
+
105
+ ## 2.0.0-alpha.92
106
+
107
+ ### Patch Changes
108
+
109
+ - Updated dependencies [8d23d56]
110
+ - @flink-app/flink@2.0.0-alpha.92
111
+
112
+ ## 2.0.0-alpha.91
113
+
114
+ ### Patch Changes
115
+
116
+ - @flink-app/flink@2.0.0-alpha.91
117
+
118
+ ## 2.0.0-alpha.90
119
+
120
+ ### Patch Changes
121
+
122
+ - Updated dependencies [0d84b5f]
123
+ - @flink-app/flink@2.0.0-alpha.90
124
+
125
+ ## 2.0.0-alpha.89
126
+
127
+ ### Patch Changes
128
+
129
+ - @flink-app/flink@2.0.0-alpha.89
130
+
131
+ ## 2.0.0-alpha.88
132
+
133
+ ### Patch Changes
134
+
135
+ - @flink-app/flink@2.0.0-alpha.88
136
+
137
+ ## 2.0.0-alpha.87
138
+
139
+ ### Patch Changes
140
+
141
+ - Updated dependencies
142
+ - @flink-app/flink@2.0.0-alpha.87
143
+
144
+ ## 2.0.0-alpha.86
145
+
146
+ ### Patch Changes
147
+
148
+ - @flink-app/flink@2.0.0-alpha.86
149
+
150
+ ## 2.0.0-alpha.85
151
+
152
+ ### Minor Changes
153
+
154
+ - 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.
155
+
156
+ ### Patch Changes
157
+
158
+ - @flink-app/flink@2.0.0-alpha.85
159
+
160
+ ## 2.0.0-alpha.84
161
+
162
+ ### Patch Changes
163
+
164
+ - @flink-app/flink@2.0.0-alpha.84
165
+
166
+ ## 2.0.0-alpha.83
167
+
168
+ ### Patch Changes
169
+
170
+ - Updated dependencies
171
+ - @flink-app/flink@2.0.0-alpha.83
172
+
173
+ ## 2.0.0-alpha.82
174
+
175
+ ### Patch Changes
176
+
177
+ - @flink-app/flink@2.0.0-alpha.82
178
+
179
+ ## 2.0.0-alpha.81
180
+
181
+ ### Patch Changes
182
+
183
+ - @flink-app/flink@2.0.0-alpha.81
184
+
185
+ ## 2.0.0-alpha.80
186
+
187
+ ### Patch Changes
188
+
189
+ - @flink-app/flink@2.0.0-alpha.80
190
+
191
+ ## 2.0.0-alpha.79
192
+
193
+ ### Patch Changes
194
+
195
+ - @flink-app/flink@2.0.0-alpha.79
196
+
197
+ ## 2.0.0-alpha.78
198
+
199
+ ### Patch Changes
200
+
201
+ - @flink-app/flink@2.0.0-alpha.78
202
+
203
+ ## 2.0.0-alpha.77
204
+
205
+ ### Patch Changes
206
+
207
+ - @flink-app/flink@2.0.0-alpha.77
208
+
209
+ ## 2.0.0-alpha.76
210
+
211
+ ### Minor Changes
212
+
213
+ - 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.
214
+
215
+ ### Patch Changes
216
+
217
+ - @flink-app/flink@2.0.0-alpha.76
218
+
219
+ ## 2.0.0-alpha.75
220
+
221
+ ### Patch Changes
222
+
223
+ - @flink-app/flink@2.0.0-alpha.75
224
+
225
+ ## 2.0.0-alpha.74
226
+
227
+ ### Patch Changes
228
+
229
+ - Updated dependencies
230
+ - Updated dependencies
231
+ - @flink-app/flink@2.0.0-alpha.74
232
+
233
+ ## 2.0.0-alpha.73
234
+
235
+ ### Patch Changes
236
+
237
+ - Updated dependencies
238
+ - @flink-app/flink@2.0.0-alpha.73
239
+
240
+ ## 2.0.0-alpha.72
241
+
242
+ ### Patch Changes
243
+
244
+ - Updated dependencies
245
+ - @flink-app/flink@2.0.0-alpha.72
246
+
247
+ ## 2.0.0-alpha.71
248
+
249
+ ### Patch Changes
250
+
251
+ - @flink-app/flink@2.0.0-alpha.71
252
+
253
+ ## 2.0.0-alpha.70
254
+
255
+ ### Patch Changes
256
+
257
+ - @flink-app/flink@2.0.0-alpha.70
258
+
259
+ ## 2.0.0-alpha.69
260
+
261
+ ### Patch Changes
262
+
263
+ - @flink-app/flink@2.0.0-alpha.69
264
+
265
+ ## 2.0.0-alpha.68
266
+
267
+ ### Patch Changes
268
+
269
+ - @flink-app/flink@2.0.0-alpha.68
270
+
271
+ ## 2.0.0-alpha.67
272
+
273
+ ### Patch Changes
274
+
275
+ - Updated dependencies [418cb54]
276
+ - Updated dependencies [70d574c]
277
+ - @flink-app/flink@2.0.0-alpha.67
278
+
279
+ ## 2.0.0-alpha.66
280
+
281
+ ### Patch Changes
282
+
283
+ - Updated dependencies [5593d26]
284
+ - @flink-app/flink@2.0.0-alpha.66
285
+
286
+ ## 2.0.0-alpha.65
287
+
288
+ ### Patch Changes
289
+
290
+ - @flink-app/flink@2.0.0-alpha.65
291
+
292
+ ## 2.0.0-alpha.64
293
+
294
+ ### Patch Changes
295
+
296
+ - Updated dependencies [95b99ee]
297
+ - @flink-app/flink@2.0.0-alpha.64
298
+
299
+ ## 2.0.0-alpha.63
300
+
301
+ ### Patch Changes
302
+
303
+ - Updated dependencies [810df2c]
304
+ - Updated dependencies [8dd0752]
305
+ - @flink-app/flink@2.0.0-alpha.63
306
+
307
+ ## 2.0.0-alpha.62
308
+
309
+ ### Patch Changes
310
+
311
+ - Updated dependencies
312
+ - @flink-app/flink@2.0.0-alpha.62
313
+
314
+ ## 2.0.0-alpha.61
315
+
316
+ ### Patch Changes
317
+
318
+ - @flink-app/flink@2.0.0-alpha.61
319
+
320
+ ## 2.0.0-alpha.60
321
+
322
+ ### Patch Changes
323
+
324
+ - @flink-app/flink@2.0.0-alpha.60
325
+
326
+ ## 2.0.0-alpha.59
327
+
328
+ ### Patch Changes
329
+
330
+ - Updated dependencies [dbc2119]
331
+ - @flink-app/flink@2.0.0-alpha.59
332
+
333
+ ## 2.0.0-alpha.58
334
+
335
+ ### Patch Changes
336
+
337
+ - Updated dependencies
338
+ - @flink-app/flink@2.0.0-alpha.58
339
+
340
+ ## 2.0.0-alpha.57
341
+
342
+ ### Patch Changes
343
+
344
+ - Updated dependencies [ef7f495]
345
+ - Updated dependencies [2d53aa4]
346
+ - Updated dependencies [b3cc5d1]
347
+ - Updated dependencies [d377fac]
348
+ - Updated dependencies [4ad44c0]
349
+ - Updated dependencies [90f80c7]
350
+ - Updated dependencies [f0887e7]
351
+ - Updated dependencies [68c46d3]
352
+ - Updated dependencies [4ad44c0]
353
+ - Updated dependencies
354
+ - @flink-app/flink@2.0.0-alpha.57
355
+
356
+ ## 2.0.0-alpha.56
357
+
358
+ ### Patch Changes
359
+
360
+ - Zod 3.x compat
361
+ - Updated dependencies
362
+ - Updated dependencies [0203256]
363
+ - @flink-app/flink@2.0.0-alpha.56
364
+
365
+ ## 2.0.0-alpha.55
366
+
367
+ ### Patch Changes
368
+
369
+ - Updated dependencies [3267206]
370
+ - Updated dependencies [497b7c2]
371
+ - Updated dependencies [3267206]
372
+ - @flink-app/flink@2.0.0-alpha.55
373
+
374
+ ## 2.0.0-alpha.53
375
+
376
+ ### Patch Changes
377
+
378
+ - Updated dependencies [669c806]
379
+ - @flink-app/flink@2.0.0-alpha.53
380
+
381
+ ## 2.0.0-alpha.52
382
+
383
+ ### Patch Changes
384
+
385
+ - Updated dependencies [fe9f8f2]
386
+ - @flink-app/flink@2.0.0-alpha.52
387
+
388
+ ## 2.0.0-alpha.51
389
+
390
+ ### Patch Changes
391
+
392
+ - fix: add peerDependencies to all plugins and adapters
393
+
394
+ **Breaking Change Prevention:**
395
+
396
+ - Added "@flink-app/flink": ">=2.0.0-alpha.0" as peerDependency to all plugins
397
+ - Fixed oidc-plugin peerDependencies from "workspace:\*" to proper version ranges
398
+ - Fixed oauth-plugin to include @flink-app/flink peerDependency
399
+
400
+ **JWT Auth Plugin Interface Fix:**
401
+
402
+ - Removed redundant createToken declaration from JwtAuthPlugin interface
403
+ - The method is already inherited from FlinkAuthPlugin, this fixes TypeScript type checking issues
404
+
405
+ **Why this matters:**
406
+
407
+ - Ensures only one version of @flink-app/flink exists in node_modules
408
+ - Prevents type conflicts when TypeScript resolves interfaces across packages
409
+ - Follows standard plugin architecture best practices (same pattern as React, Webpack, etc.)
410
+ - Package managers now warn if incompatible versions are installed
411
+ - @flink-app/flink@2.0.0-alpha.51
412
+
413
+ ## 2.0.0-alpha.50
414
+
415
+ ## 2.0.0-alpha.49
416
+
417
+ ### Patch Changes
418
+
419
+ - Typescript include fix
420
+
421
+ ## 2.0.0-alpha.48
422
+
423
+ ### Minor Changes
424
+
425
+ - AI features
426
+
427
+ ## 1.0.0
428
+
429
+ ### Minor Changes
430
+
431
+ - Align minor version, from now all packages in monorepo will have same version
3
432
 
4
433
  ## 0.14.1
5
434
 
6
435
  ### Patch Changes
7
436
 
8
- - fix: restore compatibility between core framework and plugins after 0.14.0 release
437
+ - fix: restore compatibility between core framework and plugins after 0.14.0 release
9
438
 
10
439
  ## 0.13.1
11
440
 
@@ -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": "0.14.1",
3
+ "version": "2.0.0-alpha.100",
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": "0.14.1"
22
+ "@flink-app/flink": "2.0.0-alpha.100"
23
+ },
24
+ "peerDependencies": {
25
+ "@flink-app/flink": ">=2.0.0-alpha.100"
23
26
  },
24
27
  "gitHead": "4243e3b3cd6d4e1ca001a61baa8436bf2bbe4113",
25
28
  "scripts": {