@flink-app/jwt-auth-plugin 0.11.0 → 0.11.4-next.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.
@@ -3,7 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.genSalt = exports.encrypt = void 0;
6
+ exports.encrypt = encrypt;
7
+ exports.genSalt = genSalt;
7
8
  var bcrypt_1 = __importDefault(require("bcrypt"));
8
9
  function encrypt(password, salt) {
9
10
  return new Promise(function (resolve, reject) {
@@ -14,7 +15,6 @@ function encrypt(password, salt) {
14
15
  });
15
16
  });
16
17
  }
17
- exports.encrypt = encrypt;
18
18
  function genSalt(rounds) {
19
19
  if (rounds === void 0) { rounds = 10; }
20
20
  return new Promise(function (resolve, reject) {
@@ -25,4 +25,3 @@ function genSalt(rounds) {
25
25
  });
26
26
  });
27
27
  }
28
- exports.genSalt = genSalt;
@@ -20,12 +20,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
20
20
  });
21
21
  };
22
22
  var __generator = (this && this.__generator) || function (thisArg, body) {
23
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
24
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
23
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
24
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
25
  function verb(n) { return function (v) { return step([n, v]); }; }
26
26
  function step(op) {
27
27
  if (f) throw new TypeError("Generator is already executing.");
28
- while (_) try {
28
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
29
29
  if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
30
  if (y = 0, t) op = [op[0] & 2, t.value];
31
31
  switch (op[0]) {
@@ -50,7 +50,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
50
50
  return (mod && mod.__esModule) ? mod : { "default": mod };
51
51
  };
52
52
  Object.defineProperty(exports, "__esModule", { value: true });
53
- exports.jwtAuthPlugin = void 0;
53
+ exports.jwtAuthPlugin = jwtAuthPlugin;
54
54
  var flink_1 = require("@flink-app/flink");
55
55
  var jwt_simple_1 = __importDefault(require("jwt-simple"));
56
56
  var BcryptUtils_1 = require("./BcryptUtils");
@@ -85,13 +85,12 @@ function jwtAuthPlugin(_a) {
85
85
  validatePassword: validatePassword,
86
86
  };
87
87
  }
88
- exports.jwtAuthPlugin = jwtAuthPlugin;
89
- function authenticateRequest(req, routePermissions, rolePermissions, _a) {
90
- var secret = _a.secret, algo = _a.algo, getUser = _a.getUser;
91
- return __awaiter(this, void 0, void 0, function () {
88
+ function authenticateRequest(req_1, routePermissions_1, rolePermissions_1, _a) {
89
+ return __awaiter(this, arguments, void 0, function (req, routePermissions, rolePermissions, _b) {
92
90
  var token, decodedToken, permissionsArr, validPerms, user;
93
- return __generator(this, function (_b) {
94
- switch (_b.label) {
91
+ var secret = _b.secret, algo = _b.algo, getUser = _b.getUser;
92
+ return __generator(this, function (_c) {
93
+ switch (_c.label) {
95
94
  case 0:
96
95
  token = getTokenFromReq(req);
97
96
  if (!token) return [3 /*break*/, 2];
@@ -100,7 +99,7 @@ function authenticateRequest(req, routePermissions, rolePermissions, _a) {
100
99
  decodedToken = jwt_simple_1.default.decode(token, secret, false, algo);
101
100
  }
102
101
  catch (err) {
103
- flink_1.log.debug("Failed to decode token: " + err);
102
+ flink_1.log.debug("Failed to decode token: ".concat(err));
104
103
  decodedToken = null;
105
104
  }
106
105
  if (!decodedToken) return [3 /*break*/, 2];
@@ -108,14 +107,14 @@ function authenticateRequest(req, routePermissions, rolePermissions, _a) {
108
107
  ? routePermissions
109
108
  : [routePermissions];
110
109
  if (permissionsArr && permissionsArr.length > 0) {
111
- validPerms = PermissionValidator_1.hasValidPermissions(decodedToken.roles || [], rolePermissions, permissionsArr);
110
+ validPerms = (0, PermissionValidator_1.hasValidPermissions)(decodedToken.roles || [], rolePermissions, permissionsArr);
112
111
  if (!validPerms) {
113
112
  return [2 /*return*/, false];
114
113
  }
115
114
  }
116
115
  return [4 /*yield*/, getUser(decodedToken)];
117
116
  case 1:
118
- user = _b.sent();
117
+ user = _c.sent();
119
118
  req.user = user;
120
119
  return [2 /*return*/, true];
121
120
  case 2: return [2 /*return*/, false];
@@ -131,10 +130,10 @@ function getTokenFromReq(req) {
131
130
  }
132
131
  return;
133
132
  }
134
- function createToken(payload, _a) {
135
- var secret = _a.secret, algo = _a.algo, tokenTTL = _a.tokenTTL;
136
- return __awaiter(this, void 0, void 0, function () {
137
- return __generator(this, function (_b) {
133
+ function createToken(payload_1, _a) {
134
+ return __awaiter(this, arguments, void 0, function (payload, _b) {
135
+ var secret = _b.secret, algo = _b.algo, tokenTTL = _b.tokenTTL;
136
+ return __generator(this, function (_c) {
138
137
  if (!payload) {
139
138
  throw new Error("Cannot create token - payload is missing");
140
139
  }
@@ -152,13 +151,13 @@ function createPasswordHashAndSalt(password, passwordPolicy) {
152
151
  switch (_a.label) {
153
152
  case 0:
154
153
  if (!passwordPolicy.test(password)) {
155
- flink_1.log.debug("Password does not match password policy '" + passwordPolicy + "'");
154
+ flink_1.log.debug("Password does not match password policy '".concat(passwordPolicy, "'"));
156
155
  return [2 /*return*/, null];
157
156
  }
158
- return [4 /*yield*/, BcryptUtils_1.genSalt(10)];
157
+ return [4 /*yield*/, (0, BcryptUtils_1.genSalt)(10)];
159
158
  case 1:
160
159
  salt = _a.sent();
161
- return [4 /*yield*/, BcryptUtils_1.encrypt(password, salt)];
160
+ return [4 /*yield*/, (0, BcryptUtils_1.encrypt)(password, salt)];
162
161
  case 2:
163
162
  hash = _a.sent();
164
163
  return [2 /*return*/, { salt: salt, hash: hash }];
@@ -171,7 +170,7 @@ function validatePassword(password, passwordHash, salt) {
171
170
  var hashCandidate;
172
171
  return __generator(this, function (_a) {
173
172
  switch (_a.label) {
174
- case 0: return [4 /*yield*/, BcryptUtils_1.encrypt(password, salt)];
173
+ case 0: return [4 /*yield*/, (0, BcryptUtils_1.encrypt)(password, salt)];
175
174
  case 1:
176
175
  hashCandidate = _a.sent();
177
176
  return [2 /*return*/, hashCandidate === passwordHash];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.hasValidPermissions = void 0;
3
+ exports.hasValidPermissions = hasValidPermissions;
4
4
  var flink_1 = require("@flink-app/flink");
5
5
  /**
6
6
  * Checks if provided role has permission to access route
@@ -21,7 +21,7 @@ function hasValidPermissions(roles, rolePermissions, routePermissions) {
21
21
  var _loop_1 = function (role) {
22
22
  var thisRolesPermissions = rolePermissions[role];
23
23
  if (!thisRolesPermissions) {
24
- flink_1.log.warn("Role '" + role + "' does not have any permissions defined");
24
+ flink_1.log.warn("Role '".concat(role, "' does not have any permissions defined"));
25
25
  return "continue";
26
26
  }
27
27
  if (thisRolesPermissions.includes("*")) {
@@ -41,4 +41,3 @@ function hasValidPermissions(roles, rolePermissions, routePermissions) {
41
41
  }
42
42
  return false;
43
43
  }
44
- exports.hasValidPermissions = hasValidPermissions;
package/dist/index.js CHANGED
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ var __importDefault = (this && this.__importDefault) || function (mod) {
39
+ return (mod && mod.__esModule) ? mod : { "default": mod };
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ var jwt_simple_1 = __importDefault(require("jwt-simple"));
43
+ var FlinkJwtAuthPlugin_1 = require("../src/FlinkJwtAuthPlugin");
44
+ describe("FlinkJwtAuthPlugin", function () {
45
+ it("should create and configure plugin", function () {
46
+ var plugin = (0, FlinkJwtAuthPlugin_1.jwtAuthPlugin)({
47
+ secret: "secret",
48
+ getUser: function (id) { return __awaiter(void 0, void 0, void 0, function () {
49
+ return __generator(this, function (_a) {
50
+ return [2 /*return*/, {
51
+ id: id,
52
+ username: "username",
53
+ }];
54
+ });
55
+ }); },
56
+ rolePermissions: {},
57
+ });
58
+ expect(plugin).toBeDefined();
59
+ });
60
+ it("should fail auth if no token was provided", function () { return __awaiter(void 0, void 0, void 0, function () {
61
+ var plugin, mockRequest, authenticated;
62
+ return __generator(this, function (_a) {
63
+ switch (_a.label) {
64
+ case 0:
65
+ plugin = (0, FlinkJwtAuthPlugin_1.jwtAuthPlugin)({
66
+ secret: "secret",
67
+ getUser: function (id) { return __awaiter(void 0, void 0, void 0, function () {
68
+ return __generator(this, function (_a) {
69
+ return [2 /*return*/, {
70
+ id: id,
71
+ username: "username",
72
+ }];
73
+ });
74
+ }); },
75
+ rolePermissions: {
76
+ user: ["*"],
77
+ },
78
+ });
79
+ mockRequest = {
80
+ headers: {
81
+ authorization: "",
82
+ },
83
+ };
84
+ return [4 /*yield*/, plugin.authenticateRequest(mockRequest, "foo")];
85
+ case 1:
86
+ authenticated = _a.sent();
87
+ expect(authenticated).toBeFalse();
88
+ return [2 /*return*/];
89
+ }
90
+ });
91
+ }); });
92
+ it("should fail auth if token is invalid provided", function () { return __awaiter(void 0, void 0, void 0, function () {
93
+ var plugin, mockRequest, authenticated;
94
+ return __generator(this, function (_a) {
95
+ switch (_a.label) {
96
+ case 0:
97
+ plugin = (0, FlinkJwtAuthPlugin_1.jwtAuthPlugin)({
98
+ secret: "secret",
99
+ getUser: function (id) { return __awaiter(void 0, void 0, void 0, function () {
100
+ return __generator(this, function (_a) {
101
+ fail(); // Should not invoke this
102
+ return [2 /*return*/, {
103
+ id: id,
104
+ username: "username",
105
+ }];
106
+ });
107
+ }); },
108
+ rolePermissions: {
109
+ user: ["*"],
110
+ },
111
+ });
112
+ mockRequest = {
113
+ headers: {
114
+ authorization: "Bearer 890suf089sudf0usdf0uf9s",
115
+ },
116
+ };
117
+ return [4 /*yield*/, plugin.authenticateRequest(mockRequest, "foo")];
118
+ case 1:
119
+ authenticated = _a.sent();
120
+ expect(authenticated).toBeFalse();
121
+ return [2 /*return*/];
122
+ }
123
+ });
124
+ }); });
125
+ it("should decode token and authenticate", function () { return __awaiter(void 0, void 0, void 0, function () {
126
+ var secret, userId, encodedToken, plugin, mockRequest, authenticated;
127
+ return __generator(this, function (_a) {
128
+ switch (_a.label) {
129
+ case 0:
130
+ secret = "secret";
131
+ userId = "123";
132
+ encodedToken = jwt_simple_1.default.encode({ id: userId, roles: ["user"] }, secret);
133
+ plugin = (0, FlinkJwtAuthPlugin_1.jwtAuthPlugin)({
134
+ secret: secret,
135
+ getUser: function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
136
+ var id = _b.id;
137
+ return __generator(this, function (_c) {
138
+ expect(id).toBe(userId);
139
+ return [2 /*return*/, {
140
+ id: id,
141
+ username: "username",
142
+ }];
143
+ });
144
+ }); },
145
+ rolePermissions: {
146
+ user: ["*"],
147
+ },
148
+ });
149
+ mockRequest = {
150
+ headers: {
151
+ authorization: "Bearer " + encodedToken,
152
+ },
153
+ };
154
+ return [4 /*yield*/, plugin.authenticateRequest(mockRequest, "foo")];
155
+ case 1:
156
+ authenticated = _a.sent();
157
+ expect(authenticated).toBeTruthy();
158
+ return [2 /*return*/];
159
+ }
160
+ });
161
+ }); });
162
+ it("should generate token", function () { return __awaiter(void 0, void 0, void 0, function () {
163
+ var secret, plugin, token, decoded;
164
+ return __generator(this, function (_a) {
165
+ switch (_a.label) {
166
+ case 0:
167
+ secret = "secret";
168
+ plugin = (0, FlinkJwtAuthPlugin_1.jwtAuthPlugin)({
169
+ secret: secret,
170
+ getUser: function (id) { return __awaiter(void 0, void 0, void 0, function () {
171
+ return __generator(this, function (_a) {
172
+ fail(); // Should not invoke this
173
+ return [2 /*return*/, {
174
+ id: id,
175
+ username: "username",
176
+ }];
177
+ });
178
+ }); },
179
+ rolePermissions: {
180
+ user: ["*"],
181
+ },
182
+ });
183
+ return [4 /*yield*/, plugin.createToken({ id: "123" }, ["user"])];
184
+ case 1:
185
+ token = _a.sent();
186
+ expect(token).toBeDefined();
187
+ decoded = jwt_simple_1.default.decode(token, secret);
188
+ expect(decoded.id).toBe("123");
189
+ return [2 /*return*/];
190
+ }
191
+ });
192
+ }); });
193
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ var PermissionValidator_1 = require("../src/PermissionValidator");
4
+ describe("PermissionValidator", function () {
5
+ it("should validate permissions if *", function () {
6
+ var valid = (0, PermissionValidator_1.hasValidPermissions)(["user"], {
7
+ user: ["foo", "*"],
8
+ }, ["bar"]);
9
+ expect(valid).toBe(true);
10
+ });
11
+ it("should validate permissions if match", function () {
12
+ var valid = (0, PermissionValidator_1.hasValidPermissions)(["user"], {
13
+ user: ["foo"],
14
+ }, ["foo"]);
15
+ expect(valid).toBe(true);
16
+ });
17
+ it("should not validate permissions if no match", function () {
18
+ var valid = (0, PermissionValidator_1.hasValidPermissions)(["user"], {
19
+ user: ["foo"],
20
+ }, ["bar"]);
21
+ expect(valid).toBe(false);
22
+ });
23
+ it("should not validate permissions if role permissions are missing", function () {
24
+ var valid = (0, PermissionValidator_1.hasValidPermissions)(["user"], {}, ["bar"]);
25
+ expect(valid).toBe(false);
26
+ });
27
+ it("should not validate permissions if only partial permission is matching", function () {
28
+ var valid = (0, PermissionValidator_1.hasValidPermissions)(["user"], {
29
+ user: ["foo"],
30
+ }, ["foo", "bar"]);
31
+ expect(valid).toBe(false);
32
+ });
33
+ it("should validate if public route", function () {
34
+ var valid = (0, PermissionValidator_1.hasValidPermissions)([], {}, []);
35
+ expect(valid).toBe(true);
36
+ });
37
+ it("should validate if any authenticated user is allowed", function () {
38
+ var valid = (0, PermissionValidator_1.hasValidPermissions)(["user"], {}, ["*"]);
39
+ expect(valid).toBe(true);
40
+ });
41
+ it("should not validate if any authenticated user is allowed but user not logged in", function () {
42
+ var valid = (0, PermissionValidator_1.hasValidPermissions)([], {}, ["*"]);
43
+ expect(valid).toBe(false);
44
+ });
45
+ });
@@ -0,0 +1,2 @@
1
+ export declare function encrypt(password: string, salt: string): Promise<string>;
2
+ export declare function genSalt(rounds?: number): Promise<string>;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.encrypt = encrypt;
7
+ exports.genSalt = genSalt;
8
+ var bcrypt_1 = __importDefault(require("bcrypt"));
9
+ function encrypt(password, salt) {
10
+ return new Promise(function (resolve, reject) {
11
+ bcrypt_1.default.hash(password, salt, function (err, hash) {
12
+ if (err)
13
+ reject(err);
14
+ resolve(hash);
15
+ });
16
+ });
17
+ }
18
+ function genSalt(rounds) {
19
+ if (rounds === void 0) { rounds = 10; }
20
+ return new Promise(function (resolve, reject) {
21
+ bcrypt_1.default.genSalt(rounds, function (err, salt) {
22
+ if (err)
23
+ reject(err);
24
+ resolve(salt);
25
+ });
26
+ });
27
+ }
@@ -0,0 +1,41 @@
1
+ import { FlinkAuthPlugin, FlinkAuthUser } from "@flink-app/flink";
2
+ import jwtSimple from "jwt-simple";
3
+ export interface JwtAuthPluginOptions {
4
+ secret: string;
5
+ algo?: jwtSimple.TAlgorithm;
6
+ getUser: (tokenData: any) => Promise<FlinkAuthUser>;
7
+ passwordPolicy?: RegExp;
8
+ tokenTTL?: number;
9
+ rolePermissions: {
10
+ [role: string]: string[];
11
+ };
12
+ }
13
+ export interface JwtAuthPlugin extends FlinkAuthPlugin {
14
+ /**
15
+ * Encodes and returns JWT token that includes provided payload.
16
+ *
17
+ * The payload can by anything but should in most cases be and object that
18
+ * holds user information including an identifier such as the username or id.
19
+ */
20
+ createToken: (payload: any, roles: string[]) => Promise<string>;
21
+ /**
22
+ * Generates new password hash and salt for provided password.
23
+ *
24
+ * This method should be used when setting a new password. Both hash and salt needs
25
+ * to be saved in database as both are needed to validate the password.
26
+ *
27
+ * Returns null if password does not match configured `passwordPolicy`.
28
+ */
29
+ createPasswordHashAndSalt: (password: string) => Promise<{
30
+ hash: string;
31
+ salt: string;
32
+ } | null>;
33
+ /**
34
+ * Validates that provided `password` is same as provided `hash`.
35
+ */
36
+ validatePassword: (password: string, passwordHash: string, salt: string) => Promise<boolean>;
37
+ }
38
+ /**
39
+ * Configures and creates authentication plugin.
40
+ */
41
+ export declare function jwtAuthPlugin({ secret, getUser, rolePermissions, algo, passwordPolicy, tokenTTL, }: JwtAuthPluginOptions): JwtAuthPlugin;
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
+ return new (P || (P = Promise))(function (resolve, reject) {
16
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
20
+ });
21
+ };
22
+ var __generator = (this && this.__generator) || function (thisArg, body) {
23
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
24
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
+ function verb(n) { return function (v) { return step([n, v]); }; }
26
+ function step(op) {
27
+ if (f) throw new TypeError("Generator is already executing.");
28
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
29
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
+ if (y = 0, t) op = [op[0] & 2, t.value];
31
+ switch (op[0]) {
32
+ case 0: case 1: t = op; break;
33
+ case 4: _.label++; return { value: op[1], done: false };
34
+ case 5: _.label++; y = op[1]; op = [0]; continue;
35
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
+ default:
37
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
+ if (t[2]) _.ops.pop();
42
+ _.trys.pop(); continue;
43
+ }
44
+ op = body.call(thisArg, _);
45
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
+ }
48
+ };
49
+ var __importDefault = (this && this.__importDefault) || function (mod) {
50
+ return (mod && mod.__esModule) ? mod : { "default": mod };
51
+ };
52
+ Object.defineProperty(exports, "__esModule", { value: true });
53
+ exports.jwtAuthPlugin = jwtAuthPlugin;
54
+ var flink_1 = require("@flink-app/flink");
55
+ var jwt_simple_1 = __importDefault(require("jwt-simple"));
56
+ var BcryptUtils_1 = require("./BcryptUtils");
57
+ var PermissionValidator_1 = require("./PermissionValidator");
58
+ /**
59
+ * Minimum eight characters, at least one letter and one number
60
+ * https://stackoverflow.com/a/21456918
61
+ */
62
+ var defaultPasswordPolicy = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/;
63
+ /**
64
+ * Configures and creates authentication plugin.
65
+ */
66
+ function jwtAuthPlugin(_a) {
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;
69
+ return {
70
+ authenticateRequest: function (req, permissions) { return __awaiter(_this, void 0, void 0, function () {
71
+ return __generator(this, function (_a) {
72
+ return [2 /*return*/, authenticateRequest(req, permissions, rolePermissions, {
73
+ algo: algo,
74
+ secret: secret,
75
+ getUser: getUser,
76
+ })];
77
+ });
78
+ }); },
79
+ createToken: function (payload, roles) {
80
+ return createToken(__assign(__assign({}, payload), { roles: roles }), { algo: algo, secret: secret, tokenTTL: tokenTTL });
81
+ },
82
+ createPasswordHashAndSalt: function (password) {
83
+ return createPasswordHashAndSalt(password, passwordPolicy);
84
+ },
85
+ validatePassword: validatePassword,
86
+ };
87
+ }
88
+ function authenticateRequest(req_1, routePermissions_1, rolePermissions_1, _a) {
89
+ return __awaiter(this, arguments, void 0, function (req, routePermissions, rolePermissions, _b) {
90
+ var token, decodedToken, permissionsArr, validPerms, user;
91
+ var secret = _b.secret, algo = _b.algo, getUser = _b.getUser;
92
+ return __generator(this, function (_c) {
93
+ switch (_c.label) {
94
+ case 0:
95
+ token = getTokenFromReq(req);
96
+ if (!token) return [3 /*break*/, 2];
97
+ decodedToken = void 0;
98
+ try {
99
+ decodedToken = jwt_simple_1.default.decode(token, secret, false, algo);
100
+ }
101
+ catch (err) {
102
+ flink_1.log.debug("Failed to decode token: ".concat(err));
103
+ decodedToken = null;
104
+ }
105
+ if (!decodedToken) return [3 /*break*/, 2];
106
+ permissionsArr = Array.isArray(routePermissions)
107
+ ? routePermissions
108
+ : [routePermissions];
109
+ if (permissionsArr && permissionsArr.length > 0) {
110
+ validPerms = (0, PermissionValidator_1.hasValidPermissions)(decodedToken.roles || [], rolePermissions, permissionsArr);
111
+ if (!validPerms) {
112
+ return [2 /*return*/, false];
113
+ }
114
+ }
115
+ return [4 /*yield*/, getUser(decodedToken)];
116
+ case 1:
117
+ user = _c.sent();
118
+ req.user = user;
119
+ return [2 /*return*/, true];
120
+ case 2: return [2 /*return*/, false];
121
+ }
122
+ });
123
+ });
124
+ }
125
+ function getTokenFromReq(req) {
126
+ var authHeader = req.headers.authorization;
127
+ if (authHeader) {
128
+ var _a = authHeader.split("Bearer "), token = _a[1];
129
+ return token;
130
+ }
131
+ return;
132
+ }
133
+ function createToken(payload_1, _a) {
134
+ return __awaiter(this, arguments, void 0, function (payload, _b) {
135
+ var secret = _b.secret, algo = _b.algo, tokenTTL = _b.tokenTTL;
136
+ return __generator(this, function (_c) {
137
+ if (!payload) {
138
+ throw new Error("Cannot create token - payload is missing");
139
+ }
140
+ return [2 /*return*/, jwt_simple_1.default.encode(__assign({ exp: _calculateExpiration(tokenTTL || 1000 * 60 * 60 * 24 * 365 * 100) }, payload), secret, algo)];
141
+ });
142
+ });
143
+ }
144
+ function _calculateExpiration(expiresInMs) {
145
+ return Math.floor((Date.now() + expiresInMs) / 1000);
146
+ }
147
+ function createPasswordHashAndSalt(password, passwordPolicy) {
148
+ return __awaiter(this, void 0, void 0, function () {
149
+ var salt, hash;
150
+ return __generator(this, function (_a) {
151
+ switch (_a.label) {
152
+ case 0:
153
+ if (!passwordPolicy.test(password)) {
154
+ flink_1.log.debug("Password does not match password policy '".concat(passwordPolicy, "'"));
155
+ return [2 /*return*/, null];
156
+ }
157
+ return [4 /*yield*/, (0, BcryptUtils_1.genSalt)(10)];
158
+ case 1:
159
+ salt = _a.sent();
160
+ return [4 /*yield*/, (0, BcryptUtils_1.encrypt)(password, salt)];
161
+ case 2:
162
+ hash = _a.sent();
163
+ return [2 /*return*/, { salt: salt, hash: hash }];
164
+ }
165
+ });
166
+ });
167
+ }
168
+ function validatePassword(password, passwordHash, salt) {
169
+ return __awaiter(this, void 0, void 0, function () {
170
+ var hashCandidate;
171
+ return __generator(this, function (_a) {
172
+ switch (_a.label) {
173
+ case 0: return [4 /*yield*/, (0, BcryptUtils_1.encrypt)(password, salt)];
174
+ case 1:
175
+ hashCandidate = _a.sent();
176
+ return [2 /*return*/, hashCandidate === passwordHash];
177
+ }
178
+ });
179
+ });
180
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Checks if provided role has permission to access route
3
+ * based on its permissions.
4
+ *
5
+ * @param roles logged in user role(s)
6
+ * @param rolePermissions map containing permissions by role
7
+ * @param routePermissions permission(s) required by route
8
+ * @returns
9
+ */
10
+ export declare function hasValidPermissions(roles: string[], rolePermissions: {
11
+ [x: string]: string[];
12
+ }, routePermissions: string[]): boolean;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasValidPermissions = hasValidPermissions;
4
+ var flink_1 = require("@flink-app/flink");
5
+ /**
6
+ * Checks if provided role has permission to access route
7
+ * based on its permissions.
8
+ *
9
+ * @param roles logged in user role(s)
10
+ * @param rolePermissions map containing permissions by role
11
+ * @param routePermissions permission(s) required by route
12
+ * @returns
13
+ */
14
+ function hasValidPermissions(roles, rolePermissions, routePermissions) {
15
+ if (routePermissions.includes("*")) {
16
+ return roles.length > 0;
17
+ }
18
+ if (routePermissions.length === 0) {
19
+ return true;
20
+ }
21
+ var _loop_1 = function (role) {
22
+ var thisRolesPermissions = rolePermissions[role];
23
+ if (!thisRolesPermissions) {
24
+ flink_1.log.warn("Role '".concat(role, "' does not have any permissions defined"));
25
+ return "continue";
26
+ }
27
+ if (thisRolesPermissions.includes("*")) {
28
+ return { value: true };
29
+ }
30
+ if (routePermissions.every(function (routePerm) {
31
+ return thisRolesPermissions.includes(routePerm);
32
+ })) {
33
+ return { value: true };
34
+ }
35
+ };
36
+ for (var _i = 0, roles_1 = roles; _i < roles_1.length; _i++) {
37
+ var role = roles_1[_i];
38
+ var state_1 = _loop_1(role);
39
+ if (typeof state_1 === "object")
40
+ return state_1.value;
41
+ }
42
+ return false;
43
+ }
@@ -0,0 +1 @@
1
+ export * from "./FlinkJwtAuthPlugin";
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./FlinkJwtAuthPlugin"), exports);
package/package.json CHANGED
@@ -1,35 +1,32 @@
1
1
  {
2
- "name": "@flink-app/jwt-auth-plugin",
3
- "version": "0.11.0",
4
- "description": "Flink plugin for JWT auth",
5
- "scripts": {
6
- "test": "node --preserve-symlinks -r ts-node/register -- node_modules/jasmine/bin/jasmine --config=./spec/support/jasmine.json",
7
- "test:watch": "nodemon --ext ts --exec 'jasmine-ts --config=./spec/support/jasmine.json'",
8
- "prepublish": "tsc --project tsconfig.dist.json",
9
- "watch": "tsc-watch --project tsconfig.dist.json"
10
- },
11
- "author": "joel@frost.se",
12
- "license": "MIT",
13
- "types": "dist/index.d.ts",
14
- "main": "dist/index.js",
15
- "publishConfig": {
16
- "access": "public"
17
- },
18
- "dependencies": {
19
- "bcrypt": "^5.0.1",
20
- "jwt-simple": "^0.5.6"
21
- },
22
- "devDependencies": {
23
- "@flink-app/flink": "^0.11.0",
24
- "@types/bcrypt": "^5.0.0",
25
- "@types/jasmine": "^3.7.1",
26
- "@types/node": "^15.6.2",
27
- "jasmine": "^3.7.0",
28
- "jasmine-spec-reporter": "^7.0.0",
29
- "nodemon": "^2.0.7",
30
- "ts-node": "^9.1.1",
31
- "tsc-watch": "^4.2.9",
32
- "typescript": "^4.2.4"
33
- },
34
- "gitHead": "3869def3e7e7e7105373ad31bc23a8ed68108015"
2
+ "name": "@flink-app/jwt-auth-plugin",
3
+ "version": "0.11.4-next.0",
4
+ "description": "Flink plugin for JWT auth",
5
+ "author": "joel@frost.se",
6
+ "license": "MIT",
7
+ "types": "dist/index.d.ts",
8
+ "main": "dist/index.js",
9
+ "scripts": {
10
+ "build": "tsc"
11
+ },
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "dependencies": {
16
+ "bcrypt": "^5.0.1",
17
+ "jwt-simple": "^0.5.6"
18
+ },
19
+ "devDependencies": {
20
+ "@flink-app/flink": "^0.11.4-next.0",
21
+ "@types/bcrypt": "^5.0.0",
22
+ "@types/jasmine": "^3.7.1",
23
+ "@types/node": "^22.10.1",
24
+ "jasmine": "^3.7.0",
25
+ "jasmine-spec-reporter": "^7.0.0",
26
+ "nodemon": "^2.0.7",
27
+ "ts-node": "^10.9.2",
28
+ "tsc-watch": "^6.2.1",
29
+ "typescript": "5.6.2"
30
+ },
31
+ "gitHead": "6a44df1b6601c86ea3402750b939784d003202c3"
35
32
  }