@flink-app/jwt-auth-plugin 0.12.1-alpha.40 → 0.12.1-alpha.43

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.
@@ -1,14 +1,78 @@
1
- import { FlinkAuthPlugin, FlinkAuthUser } from "@flink-app/flink";
1
+ import { FlinkAuthPlugin, FlinkAuthUser, FlinkRequest } from "@flink-app/flink";
2
2
  import jwtSimple from "jwt-simple";
3
+ /**
4
+ * Custom token extraction callback.
5
+ *
6
+ * Return values:
7
+ * - `string`: Token found, use this token
8
+ * - `null`: No token found, authentication should fail
9
+ * - `undefined`: Skip custom extraction, use default Bearer token extraction
10
+ */
11
+ export type TokenExtractor = (req: FlinkRequest) => string | null | undefined;
12
+ /**
13
+ * Custom permission validation callback.
14
+ *
15
+ * Called after getUser to validate if the user has required permissions.
16
+ * Useful for dynamic permissions stored in database.
17
+ *
18
+ * @param user - The authenticated user object returned from getUser
19
+ * @param routePermissions - Array of permissions required by the route
20
+ * @returns true if user has required permissions, false otherwise
21
+ */
22
+ export type PermissionChecker = (user: FlinkAuthUser, routePermissions: string[]) => Promise<boolean> | boolean;
3
23
  export interface JwtAuthPluginOptions {
4
24
  secret: string;
5
25
  algo?: jwtSimple.TAlgorithm;
6
- getUser: (tokenData: any) => Promise<FlinkAuthUser | null | undefined>;
26
+ getUser: (tokenData: any, req: FlinkRequest) => Promise<FlinkAuthUser | null | undefined>;
7
27
  passwordPolicy?: RegExp;
8
28
  tokenTTL?: number;
9
29
  rolePermissions: {
10
30
  [role: string]: string[];
11
31
  };
32
+ /**
33
+ * Optional custom token extraction callback.
34
+ *
35
+ * Allows conditional token extraction based on request properties (path, method, headers, etc.).
36
+ * Return `undefined` to fall back to default Bearer token extraction.
37
+ */
38
+ tokenExtractor?: TokenExtractor;
39
+ /**
40
+ * Optional custom permission checker for dynamic permissions.
41
+ *
42
+ * When provided, replaces static rolePermissions checking.
43
+ * Called after getUser with the full user object.
44
+ *
45
+ * Example:
46
+ * ```typescript
47
+ * checkPermissions: async (user, routePermissions) => {
48
+ * return routePermissions.every(perm =>
49
+ * user.permissions?.includes(perm)
50
+ * );
51
+ * }
52
+ * ```
53
+ */
54
+ checkPermissions?: PermissionChecker;
55
+ /**
56
+ * When true, uses roles from the user object returned by getUser
57
+ * instead of roles from the decoded token for static permission checking.
58
+ *
59
+ * Useful for multi-tenant scenarios where user roles vary by organization context.
60
+ * The organization context can be determined from request headers, subdomain, path, etc.
61
+ *
62
+ * Example:
63
+ * ```typescript
64
+ * useDynamicRoles: true,
65
+ * getUser: async (tokenData, req) => {
66
+ * const orgId = req.headers['x-organization-id'];
67
+ * const membership = await getOrgMembership(tokenData.userId, orgId);
68
+ * return {
69
+ * id: tokenData.userId,
70
+ * roles: [membership.role], // Org-specific role
71
+ * };
72
+ * }
73
+ * ```
74
+ */
75
+ useDynamicRoles?: boolean;
12
76
  }
13
77
  export interface JwtAuthPlugin extends FlinkAuthPlugin {
14
78
  /**
@@ -38,4 +102,5 @@ export interface JwtAuthPlugin extends FlinkAuthPlugin {
38
102
  /**
39
103
  * Configures and creates authentication plugin.
40
104
  */
41
- export declare function jwtAuthPlugin({ secret, getUser, rolePermissions, algo, passwordPolicy, tokenTTL, }: JwtAuthPluginOptions): JwtAuthPlugin;
105
+ export declare function jwtAuthPlugin({ secret, getUser, rolePermissions, algo, passwordPolicy, tokenTTL, //Defaults to hundred year
106
+ tokenExtractor, checkPermissions, useDynamicRoles, }: JwtAuthPluginOptions): JwtAuthPlugin;
@@ -65,7 +65,8 @@ 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;
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;
69
70
  return {
70
71
  authenticateRequest: function (req, permissions) { return __awaiter(_this, void 0, void 0, function () {
71
72
  return __generator(this, function (_a) {
@@ -73,6 +74,9 @@ function jwtAuthPlugin(_a) {
73
74
  algo: algo,
74
75
  secret: secret,
75
76
  getUser: getUser,
77
+ tokenExtractor: tokenExtractor,
78
+ checkPermissions: checkPermissions,
79
+ useDynamicRoles: useDynamicRoles,
76
80
  })];
77
81
  });
78
82
  }); },
@@ -84,13 +88,25 @@ function jwtAuthPlugin(_a) {
84
88
  exports.jwtAuthPlugin = jwtAuthPlugin;
85
89
  function authenticateRequest(req_1, routePermissions_1, rolePermissions_1, _a) {
86
90
  return __awaiter(this, arguments, void 0, function (req, routePermissions, rolePermissions, _b) {
87
- var token, decodedToken, permissionsArr, validPerms, user;
88
- var secret = _b.secret, algo = _b.algo, getUser = _b.getUser;
91
+ var token, decodedToken, permissionsArr, validPerms, user, validPerms, hasPermission;
92
+ var secret = _b.secret, algo = _b.algo, getUser = _b.getUser, tokenExtractor = _b.tokenExtractor, checkPermissions = _b.checkPermissions, useDynamicRoles = _b.useDynamicRoles;
89
93
  return __generator(this, function (_c) {
90
94
  switch (_c.label) {
91
95
  case 0:
92
- token = getTokenFromReq(req);
93
- if (!token) return [3 /*break*/, 2];
96
+ if (tokenExtractor) {
97
+ token = tokenExtractor(req);
98
+ // If tokenExtractor returns undefined, fall back to default
99
+ if (token === undefined) {
100
+ token = getTokenFromReq(req);
101
+ }
102
+ // If it returns null, token stays null (no default fallback)
103
+ // If it returns string, token is the string
104
+ }
105
+ else {
106
+ // No custom extractor, use default
107
+ token = getTokenFromReq(req);
108
+ }
109
+ if (!token) return [3 /*break*/, 4];
94
110
  decodedToken = void 0;
95
111
  try {
96
112
  decodedToken = jwt_simple_1.default.decode(token, secret, false, algo);
@@ -99,24 +115,43 @@ function authenticateRequest(req_1, routePermissions_1, rolePermissions_1, _a) {
99
115
  flink_1.log.debug("[JWT AUTH PLUGIN] Failed to decode token: ".concat(err));
100
116
  decodedToken = null;
101
117
  }
102
- if (!decodedToken) return [3 /*break*/, 2];
118
+ if (!decodedToken) return [3 /*break*/, 4];
103
119
  permissionsArr = Array.isArray(routePermissions) ? routePermissions : [routePermissions];
104
- if (permissionsArr && permissionsArr.length > 0) {
120
+ // Static permission check - only if custom checker NOT provided AND not using dynamic roles
121
+ if (!checkPermissions && !useDynamicRoles && permissionsArr && permissionsArr.length > 0) {
105
122
  validPerms = (0, PermissionValidator_1.hasValidPermissions)(decodedToken.roles || [], rolePermissions, permissionsArr);
106
123
  if (!validPerms) {
107
124
  return [2 /*return*/, false];
108
125
  }
109
126
  }
110
- return [4 /*yield*/, getUser(decodedToken)];
127
+ return [4 /*yield*/, getUser(decodedToken, req)];
111
128
  case 1:
112
129
  user = _c.sent();
113
130
  if (!user) {
114
131
  flink_1.log.debug("[JWT AUTH PLUGIN] User not returned from getUser callback");
115
132
  return [2 /*return*/, false];
116
133
  }
134
+ // Dynamic roles: check permissions using roles from user object
135
+ if (!checkPermissions && useDynamicRoles && permissionsArr && permissionsArr.length > 0) {
136
+ validPerms = (0, PermissionValidator_1.hasValidPermissions)(user.roles || [], rolePermissions, permissionsArr);
137
+ if (!validPerms) {
138
+ flink_1.log.debug("[JWT AUTH PLUGIN] Dynamic role permission check failed");
139
+ return [2 /*return*/, false];
140
+ }
141
+ }
142
+ if (!(checkPermissions && permissionsArr && permissionsArr.length > 0)) return [3 /*break*/, 3];
143
+ return [4 /*yield*/, checkPermissions(user, permissionsArr)];
144
+ case 2:
145
+ hasPermission = _c.sent();
146
+ if (!hasPermission) {
147
+ flink_1.log.debug("[JWT AUTH PLUGIN] Custom permission check failed");
148
+ return [2 /*return*/, false];
149
+ }
150
+ _c.label = 3;
151
+ case 3:
117
152
  req.user = user;
118
153
  return [2 /*return*/, true];
119
- case 2: return [2 /*return*/, false];
154
+ case 4: return [2 /*return*/, false];
120
155
  }
121
156
  });
122
157
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flink-app/jwt-auth-plugin",
3
- "version": "0.12.1-alpha.40",
3
+ "version": "0.12.1-alpha.43",
4
4
  "description": "Flink plugin for JWT auth",
5
5
  "scripts": {
6
6
  "test": "node --preserve-symlinks -r ts-node/register -- node_modules/jasmine/bin/jasmine --config=./spec/support/jasmine.json",
@@ -31,5 +31,5 @@
31
31
  "tsc-watch": "^4.2.9",
32
32
  "typescript": "5.4.5"
33
33
  },
34
- "gitHead": "456502f273fe9473df05b71a803f3eda1a2f8931"
34
+ "gitHead": "e5fc78243a97075ce0272f287f3f89fd44681715"
35
35
  }