@flink-app/generic-auth-plugin 0.12.1-alpha.3 → 0.12.1-alpha.30

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.
Files changed (34) hide show
  1. package/.flink/generatedHandlers.ts +1 -1
  2. package/.flink/generatedJobs.ts +1 -1
  3. package/.flink/generatedRepos.ts +1 -1
  4. package/.flink/schemas/schemas.json +7 -2
  5. package/.flink/schemas/schemas.ts +1 -1
  6. package/.flink/start.ts +2 -1
  7. package/dist/.flink/generatedHandlers.js +1 -1
  8. package/dist/.flink/generatedJobs.js +1 -1
  9. package/dist/.flink/generatedRepos.js +1 -1
  10. package/dist/.flink/schemas/schemas.json +7 -2
  11. package/dist/.flink/start.d.ts +2 -0
  12. package/dist/.flink/start.js +2 -1
  13. package/dist/src/coreFunctions.d.ts +5 -5
  14. package/dist/src/coreFunctions.js +22 -4
  15. package/dist/src/genericAuthContext.d.ts +5 -5
  16. package/dist/src/genericAuthPluginOptions.d.ts +2 -1
  17. package/dist/src/handlers/UserCreate.js +4 -4
  18. package/dist/src/handlers/UserLogin.js +39 -7
  19. package/dist/src/handlers/UserPasswordResetComplete.js +1 -1
  20. package/dist/src/handlers/UserPushRegisterToken.js +1 -1
  21. package/dist/src/schemas/User.d.ts +2 -1
  22. package/dist/src/schemas/UserCreateReq.d.ts +2 -1
  23. package/dist/src/schemas/UserPasswordResetCompleteRes.d.ts +4 -0
  24. package/package.json +8 -8
  25. package/src/coreFunctions.ts +29 -7
  26. package/src/genericAuthContext.ts +7 -5
  27. package/src/genericAuthPluginOptions.ts +2 -1
  28. package/src/handlers/UserCreate.ts +3 -2
  29. package/src/handlers/UserLogin.ts +56 -31
  30. package/src/handlers/UserPushRegisterToken.ts +1 -1
  31. package/src/schemas/User.ts +2 -1
  32. package/src/schemas/UserCreateReq.ts +5 -4
  33. package/src/schemas/UserPasswordResetCompleteRes.ts +8 -3
  34. package/CLAUDE.md +0 -32
@@ -1,4 +1,4 @@
1
- // Generated Thu Mar 20 2025 14:30:08 GMT+0100 (Central European Standard Time)
1
+ // Generated Mon Sep 22 2025 14:18:23 GMT+0200 (Central European Summer Time)
2
2
  import { autoRegisteredHandlers, HttpMethod } from "@flink-app/flink";
3
3
  import * as UserCreate_0 from "../src/handlers/UserCreate";
4
4
  import * as UserLogin_0 from "../src/handlers/UserLogin";
@@ -1,4 +1,4 @@
1
- // Generated Thu Mar 20 2025 14:30:08 GMT+0100 (Central European Standard Time)
1
+ // Generated Mon Sep 22 2025 14:18:23 GMT+0200 (Central European Summer Time)
2
2
  import { autoRegisteredJobs } from "@flink-app/flink";
3
3
  export const jobs = [];
4
4
  autoRegisteredJobs.push(...jobs);
@@ -1,4 +1,4 @@
1
- // Generated Thu Mar 20 2025 14:30:08 GMT+0100 (Central European Standard Time)
1
+ // Generated Mon Sep 22 2025 14:18:23 GMT+0200 (Central European Summer Time)
2
2
  import { autoRegisteredRepos } from "@flink-app/flink";
3
3
  export const repos = [];
4
4
  autoRegisteredRepos.push(...repos);
@@ -13,11 +13,15 @@
13
13
  "password": {
14
14
  "type": "string"
15
15
  },
16
+ "personalNumber": {
17
+ "type": "string"
18
+ },
16
19
  "authentificationMethod": {
17
20
  "type": "string",
18
21
  "enum": [
19
22
  "password",
20
- "sms"
23
+ "sms",
24
+ "bankid"
21
25
  ]
22
26
  },
23
27
  "profile": {
@@ -264,7 +268,8 @@
264
268
  "invalidCode",
265
269
  "passwordError"
266
270
  ]
267
- }
271
+ },
272
+ "user": {}
268
273
  },
269
274
  "required": [
270
275
  "status"
@@ -29,7 +29,7 @@ import { PutManagementUserRolesByUseridRes } from "../../src/schemas/Management/
29
29
  import { PutManagementUserUsernameByUseridReq } from "../../src/schemas/Management/PutUserUsernameByUseridReq";
30
30
  import { PutManagementUserUsernameByUseridRes } from "../../src/schemas/Management/PutUserUsernameByUseridRes";
31
31
 
32
- // Generated Thu Mar 20 2025 14:30:08 GMT+0100 (Central European Standard Time)
32
+ // Generated Mon Sep 22 2025 14:18:23 GMT+0200 (Central European Summer Time)
33
33
  export interface UserCreate_7_ReqSchema extends UserCreateReq {}
34
34
 
35
35
  export interface UserCreate_7_ResSchema extends UserCreateRes {}
package/.flink/start.ts CHANGED
@@ -1,5 +1,6 @@
1
- // Generated Thu Mar 20 2025 14:30:08 GMT+0100 (Central European Standard Time)
1
+ // Generated Mon Sep 22 2025 14:18:23 GMT+0200 (Central European Summer Time)
2
2
  import "./generatedHandlers";
3
3
  import "./generatedRepos";
4
4
  import "./generatedJobs";
5
5
  import "../src/index";
6
+ export default {}; // Export an empty object to make it a module
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.handlers = void 0;
4
- // Generated Thu Mar 20 2025 14:30:08 GMT+0100 (Central European Standard Time)
4
+ // Generated Mon Sep 22 2025 14:18:23 GMT+0200 (Central European Summer Time)
5
5
  var flink_1 = require("@flink-app/flink");
6
6
  exports.handlers = [];
7
7
  flink_1.autoRegisteredHandlers.push.apply(flink_1.autoRegisteredHandlers, exports.handlers);
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.jobs = void 0;
4
- // Generated Thu Mar 20 2025 14:30:08 GMT+0100 (Central European Standard Time)
4
+ // Generated Mon Sep 22 2025 14:18:23 GMT+0200 (Central European Summer Time)
5
5
  var flink_1 = require("@flink-app/flink");
6
6
  exports.jobs = [];
7
7
  flink_1.autoRegisteredJobs.push.apply(flink_1.autoRegisteredJobs, exports.jobs);
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.repos = void 0;
4
- // Generated Thu Mar 20 2025 14:30:08 GMT+0100 (Central European Standard Time)
4
+ // Generated Mon Sep 22 2025 14:18:23 GMT+0200 (Central European Summer Time)
5
5
  var flink_1 = require("@flink-app/flink");
6
6
  exports.repos = [];
7
7
  flink_1.autoRegisteredRepos.push.apply(flink_1.autoRegisteredRepos, exports.repos);
@@ -13,11 +13,15 @@
13
13
  "password": {
14
14
  "type": "string"
15
15
  },
16
+ "personalNumber": {
17
+ "type": "string"
18
+ },
16
19
  "authentificationMethod": {
17
20
  "type": "string",
18
21
  "enum": [
19
22
  "password",
20
- "sms"
23
+ "sms",
24
+ "bankid"
21
25
  ]
22
26
  },
23
27
  "profile": {
@@ -264,7 +268,8 @@
264
268
  "invalidCode",
265
269
  "passwordError"
266
270
  ]
267
- }
271
+ },
272
+ "user": {}
268
273
  },
269
274
  "required": [
270
275
  "status"
@@ -2,3 +2,5 @@ import "./generatedHandlers";
2
2
  import "./generatedRepos";
3
3
  import "./generatedJobs";
4
4
  import "../src/index";
5
+ declare const _default: {};
6
+ export default _default;
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- // Generated Thu Mar 20 2025 14:30:08 GMT+0100 (Central European Standard Time)
3
+ // Generated Mon Sep 22 2025 14:18:23 GMT+0200 (Central European Summer Time)
4
4
  require("./generatedHandlers");
5
5
  require("./generatedRepos");
6
6
  require("./generatedJobs");
7
7
  require("../src/index");
8
+ exports.default = {}; // Export an empty object to make it a module
@@ -1,4 +1,4 @@
1
- import { FlinkRepo } from "@flink-app/flink";
1
+ import { FlinkRepo, FlinkRequest } from "@flink-app/flink";
2
2
  import { JwtAuthPlugin } from "@flink-app/jwt-auth-plugin";
3
3
  import { User } from "./schemas/User";
4
4
  import { UserCreateRes } from "./schemas/UserCreateRes";
@@ -11,20 +11,20 @@ import { GenericAuthsmsOptions } from "./genericAuthPluginOptions";
11
11
  export declare function getJtwTokenPlugin(secret: string, rolePermissions?: {
12
12
  [role: string]: string[];
13
13
  }, passwordPolicy?: RegExp, tokenTTL?: number): JwtAuthPlugin;
14
- export declare function createUser(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, username: string, password: string, authentificationMethod: "password" | "sms", roles: string[], profile: UserProfile, createPasswordHashAndSaltMethod?: {
14
+ export declare function createUser(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, username: string, password: string, authentificationMethod: "password" | "sms" | "bankid", roles: string[], profile: UserProfile, createPasswordHashAndSaltMethod?: {
15
15
  (password: string): Promise<{
16
16
  hash: string;
17
17
  salt: string;
18
18
  } | null>;
19
19
  }, onUserCreated?: {
20
20
  (user: User): Promise<void>;
21
- }): Promise<UserCreateRes>;
21
+ }, personalNumber?: string): Promise<UserCreateRes>;
22
22
  export declare function loginByToken(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, token: string, code: string, jwtSecret: string): Promise<UserLoginRes>;
23
23
  export declare function loginUser(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, username: string, password: string | undefined, validatePasswordMethod?: {
24
24
  (password: string, hash: string, salt: string): Promise<boolean>;
25
25
  }, smsOptions?: GenericAuthsmsOptions, onSuccessfulLogin?: {
26
- (user: User): Promise<void>;
27
- }): Promise<UserLoginRes>;
26
+ (user: User, req?: FlinkRequest): Promise<void>;
27
+ }, req?: FlinkRequest): Promise<UserLoginRes>;
28
28
  export declare function changePassword(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, userId: string, newPassword: string, createPasswordHashAndSaltMethod?: {
29
29
  (password: string): Promise<{
30
30
  hash: string;
@@ -40,6 +40,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
40
40
  };
41
41
  Object.defineProperty(exports, "__esModule", { value: true });
42
42
  exports.passwordResetComplete = exports.passwordResetStart = exports.changePassword = exports.loginUser = exports.loginByToken = exports.createUser = exports.getJtwTokenPlugin = void 0;
43
+ var flink_1 = require("@flink-app/flink");
43
44
  var jwt_auth_plugin_1 = require("@flink-app/jwt-auth-plugin");
44
45
  var jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
45
46
  function getJtwTokenPlugin(secret, rolePermissions, passwordPolicy, tokenTTL) {
@@ -70,7 +71,7 @@ function getJtwTokenPlugin(secret, rolePermissions, passwordPolicy, tokenTTL) {
70
71
  });
71
72
  }
72
73
  exports.getJtwTokenPlugin = getJtwTokenPlugin;
73
- function createUser(repo, auth, username, password, authentificationMethod, roles, profile, createPasswordHashAndSaltMethod, onUserCreated) {
74
+ function createUser(repo, auth, username, password, authentificationMethod, roles, profile, createPasswordHashAndSaltMethod, onUserCreated, personalNumber) {
74
75
  return __awaiter(this, void 0, void 0, function () {
75
76
  var existingUser, userData, passwordAndSalt, user, token;
76
77
  return __generator(this, function (_a) {
@@ -93,6 +94,15 @@ function createUser(repo, auth, username, password, authentificationMethod, role
93
94
  authentificationMethod: authentificationMethod,
94
95
  pushNotificationTokens: [],
95
96
  };
97
+ if (personalNumber) {
98
+ userData.personalNumber = personalNumber;
99
+ }
100
+ if (authentificationMethod == "bankid") {
101
+ if (!personalNumber) {
102
+ flink_1.log.warn("BankID login requested but no personal number found for user");
103
+ return [2 /*return*/, { status: "error" }];
104
+ }
105
+ }
96
106
  if (!(authentificationMethod == "password")) return [3 /*break*/, 6];
97
107
  passwordAndSalt = null;
98
108
  if (!(createPasswordHashAndSaltMethod != null)) return [3 /*break*/, 3];
@@ -180,7 +190,7 @@ function loginByToken(repo, auth, token, code, jwtSecret) {
180
190
  });
181
191
  }
182
192
  exports.loginByToken = loginByToken;
183
- function loginUser(repo, auth, username, password, validatePasswordMethod, smsOptions, onSuccessfulLogin) {
193
+ function loginUser(repo, auth, username, password, validatePasswordMethod, smsOptions, onSuccessfulLogin, req) {
184
194
  return __awaiter(this, void 0, void 0, function () {
185
195
  var user, valid, ex_1, code, payload, secret, options, token, token;
186
196
  return __generator(this, function (_a) {
@@ -239,12 +249,20 @@ function loginUser(repo, auth, username, password, validatePasswordMethod, smsOp
239
249
  validationToken: token,
240
250
  }];
241
251
  }
252
+ if (user.authentificationMethod == "bankid") {
253
+ if (!user.personalNumber) {
254
+ flink_1.log.warn("BankID login requested but no personal number found for user");
255
+ return [2 /*return*/, { status: "failed" }];
256
+ }
257
+ flink_1.log.warn("BankID login required to be handled in other way, i.e. using flink bankid plugin");
258
+ return [2 /*return*/, { status: "failed" }];
259
+ }
242
260
  if (!valid) return [3 /*break*/, 13];
243
261
  return [4 /*yield*/, auth.createToken({ username: username.toLowerCase(), _id: user._id }, user.roles)];
244
262
  case 10:
245
263
  token = _a.sent();
246
264
  if (!onSuccessfulLogin) return [3 /*break*/, 12];
247
- return [4 /*yield*/, onSuccessfulLogin(user)];
265
+ return [4 /*yield*/, onSuccessfulLogin(user, req)];
248
266
  case 11:
249
267
  _a.sent();
250
268
  _a.label = 12;
@@ -414,7 +432,7 @@ function passwordResetComplete(repo_1, auth_1, jwtSecret_1, passwordResetToken_1
414
432
  })];
415
433
  case 6:
416
434
  _a.sent();
417
- return [2 /*return*/, { status: "success" }];
435
+ return [2 /*return*/, { status: "success", user: user }];
418
436
  }
419
437
  });
420
438
  });
@@ -1,4 +1,4 @@
1
- import { FlinkRepo } from "@flink-app/flink";
1
+ import { FlinkRepo, FlinkRequest } from "@flink-app/flink";
2
2
  import { JwtAuthPlugin } from "@flink-app/jwt-auth-plugin";
3
3
  import { User } from "./schemas/User";
4
4
  import { UserCreateRes } from "./schemas/UserCreateRes";
@@ -13,14 +13,14 @@ export interface genericAuthContext {
13
13
  genericAuthPlugin: {
14
14
  loginUser(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, username: string, password?: string, validatePasswordMethod?: {
15
15
  (password: string, hash: string, salt: string): Promise<boolean>;
16
- }, smsOptions?: GenericAuthsmsOptions, onSuccessfulLogin?: (user: User) => Promise<void>): Promise<UserLoginRes>;
16
+ }, smsOptions?: GenericAuthsmsOptions, onSuccessfulLogin?: (user: User, req?: FlinkRequest) => Promise<void>, req?: FlinkRequest): Promise<UserLoginRes>;
17
17
  loginByToken(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, token: string, code: string, jwtSecret: string): Promise<UserLoginRes>;
18
- createUser(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, username: string, password: string, authentificationMethod: "password" | "sms", roles: string[], profile: UserProfile, createPasswordHashAndSaltMethod?: {
18
+ createUser(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, username: string, password: string, authentificationMethod: "password" | "sms" | "bankid", roles: string[], profile: UserProfile, createPasswordHashAndSaltMethod?: {
19
19
  (password: string): Promise<{
20
20
  hash: string;
21
21
  salt: string;
22
22
  } | null>;
23
- }, onUserCreated?: (user: User) => Promise<void>): Promise<UserCreateRes>;
23
+ }, onUserCreated?: (user: User) => Promise<void>, personalNumber?: string): Promise<UserCreateRes>;
24
24
  changePassword(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, userId: string, newPassword: string, createPasswordHashAndSaltMethod?: {
25
25
  (password: string): Promise<{
26
26
  hash: string;
@@ -48,7 +48,7 @@ export interface genericAuthContext {
48
48
  usernameFormat: RegExp;
49
49
  smsOptions?: GenericAuthsmsOptions;
50
50
  onSuccessfulLogin?: {
51
- (user: User): Promise<void>;
51
+ (user: User, req?: FlinkRequest): Promise<void>;
52
52
  };
53
53
  onUserCreated?: {
54
54
  (user: User): Promise<void>;
@@ -1,3 +1,4 @@
1
+ import { FlinkRequest } from "@flink-app/flink";
1
2
  import { User } from "./schemas/User";
2
3
  import { UserPasswordResetSettings } from "./schemas/UserPasswordResetSettings";
3
4
  import { client as smsClient } from "@flink-app/sms-plugin";
@@ -26,7 +27,7 @@ export interface GenericAuthPluginOptions {
26
27
  usernameFormat?: RegExp;
27
28
  sms?: GenericAuthsmsOptions;
28
29
  onSuccessfulLogin?: {
29
- (user: User): Promise<void>;
30
+ (user: User, req?: FlinkRequest): Promise<void>;
30
31
  };
31
32
  onUserCreated?: {
32
33
  (user: User): Promise<void>;
@@ -39,12 +39,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.__schemas = exports.__params = exports.__query = exports.__file = exports.__assumedHttpMethod = void 0;
40
40
  var flink_1 = require("@flink-app/flink");
41
41
  var userCreateHandler = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
42
- var _c, password, username, authentificationMethod, profile, roles, pluginName, repo, re, createUserResponse;
42
+ var _c, password, username, authentificationMethod, profile, personalNumber, roles, pluginName, repo, re, createUserResponse;
43
43
  var ctx = _b.ctx, req = _b.req, origin = _b.origin;
44
44
  return __generator(this, function (_d) {
45
45
  switch (_d.label) {
46
46
  case 0:
47
- _c = req.body, password = _c.password, username = _c.username, authentificationMethod = _c.authentificationMethod, profile = _c.profile;
47
+ _c = req.body, password = _c.password, username = _c.username, authentificationMethod = _c.authentificationMethod, profile = _c.profile, personalNumber = _c.personalNumber;
48
48
  if (authentificationMethod == null) {
49
49
  authentificationMethod = "password";
50
50
  }
@@ -63,7 +63,7 @@ var userCreateHandler = function (_a) { return __awaiter(void 0, [_a], void 0, f
63
63
  if (!re.test(username)) {
64
64
  return [2 /*return*/, (0, flink_1.badRequest)("Username does not meet requirements", "usernameError")];
65
65
  }
66
- return [4 /*yield*/, ctx.plugins.genericAuthPlugin.createUser(repo, ctx.auth, username.toLocaleLowerCase(), password, authentificationMethod, roles, profile, ctx.plugins[pluginName].createPasswordHashAndSaltMethod, ctx.plugins[pluginName].onUserCreated)];
66
+ return [4 /*yield*/, ctx.plugins.genericAuthPlugin.createUser(repo, ctx.auth, username.toLocaleLowerCase(), password, authentificationMethod, roles, profile, ctx.plugins[pluginName].createPasswordHashAndSaltMethod, ctx.plugins[pluginName].onUserCreated, personalNumber)];
67
67
  case 1:
68
68
  createUserResponse = _d.sent();
69
69
  if (createUserResponse.status != "success") {
@@ -85,4 +85,4 @@ var userCreateHandler = function (_a) { return __awaiter(void 0, [_a], void 0, f
85
85
  }); };
86
86
  exports.default = userCreateHandler;
87
87
  exports.__assumedHttpMethod = "", exports.__file = "UserCreate.ts", exports.__query = [], exports.__params = [];
88
- exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "username": { "type": "string" }, "password": { "type": "string" }, "authentificationMethod": { "type": "string", "enum": ["password", "sms"] }, "profile": { "type": "object" } }, "required": ["username"], "definitions": {} }, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "status": { "type": "string", "enum": ["success", "error", "userExists", "passwordError"] }, "user": { "type": "object", "properties": { "_id": { "type": "string" }, "username": { "type": "string" }, "token": { "type": "string" } }, "required": ["_id", "username", "token"], "additionalProperties": false } }, "required": ["status"], "definitions": {} } };
88
+ exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "username": { "type": "string" }, "password": { "type": "string" }, "personalNumber": { "type": "string" }, "authentificationMethod": { "type": "string", "enum": ["password", "sms", "bankid"] }, "profile": { "type": "object" } }, "required": ["username"], "definitions": {} }, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "status": { "type": "string", "enum": ["success", "error", "userExists", "passwordError"] }, "user": { "type": "object", "properties": { "_id": { "type": "string" }, "username": { "type": "string" }, "token": { "type": "string" } }, "required": ["_id", "username", "token"], "additionalProperties": false } }, "required": ["status"], "definitions": {} } };
@@ -39,29 +39,61 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.__schemas = exports.__params = exports.__query = exports.__file = exports.__assumedHttpMethod = void 0;
40
40
  var flink_1 = require("@flink-app/flink");
41
41
  var userLoginHandler = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
42
- var pluginName, repo, loginRespons;
42
+ var pluginName, repo, loginResponse, error_1;
43
43
  var ctx = _b.ctx, req = _b.req, origin = _b.origin;
44
44
  return __generator(this, function (_c) {
45
45
  switch (_c.label) {
46
46
  case 0:
47
47
  pluginName = origin || "genericAuthPlugin";
48
48
  repo = ctx.repos[ctx.plugins[pluginName].repoName];
49
- return [4 /*yield*/, ctx.plugins.genericAuthPlugin.loginUser(repo, ctx.auth, req.body.username, req.body.password, ctx.plugins.genericAuthPlugin.validatePasswordMethod, ctx.plugins[pluginName].smsOptions, ctx.plugins[pluginName].onSuccessfulLogin)];
49
+ loginResponse = undefined;
50
+ _c.label = 1;
50
51
  case 1:
51
- loginRespons = _c.sent();
52
- if (loginRespons.status != "success") {
53
- switch (loginRespons.status) {
52
+ _c.trys.push([1, 3, , 4]);
53
+ return [4 /*yield*/, ctx.plugins.genericAuthPlugin.loginUser(repo, ctx.auth, req.body.username, req.body.password, ctx.plugins.genericAuthPlugin.validatePasswordMethod, ctx.plugins[pluginName].smsOptions, ctx.plugins[pluginName].onSuccessfulLogin, req)];
54
+ case 2:
55
+ loginResponse = _c.sent();
56
+ return [3 /*break*/, 4];
57
+ case 3:
58
+ error_1 = _c.sent();
59
+ // Convert any thrown error that conforms to flink error structure to a proper response
60
+ // Note that any auth failures would not have been thrown, but returned as part of loginResponse
61
+ // but with this it is possible to throw errors from callbacks like onSuccessfulLogin
62
+ if (isFlinkError(error_1)) {
63
+ flink_1.log.debug("Caught FlinkError in userLoginHandler:", error_1);
64
+ return [2 /*return*/, {
65
+ status: error_1.status,
66
+ error: {
67
+ id: error_1.error.id,
68
+ title: error_1.error.title,
69
+ code: error_1.error.code,
70
+ detail: error_1.error.detail,
71
+ },
72
+ }];
73
+ }
74
+ // For other errors, return a generic 500 response
75
+ flink_1.log.error("Error in userLoginHandler:", error_1);
76
+ return [2 /*return*/, (0, flink_1.internalServerError)()];
77
+ case 4:
78
+ if ((loginResponse === null || loginResponse === void 0 ? void 0 : loginResponse.status) != "success") {
79
+ switch (loginResponse === null || loginResponse === void 0 ? void 0 : loginResponse.status) {
54
80
  case "failed":
55
- return [2 /*return*/, (0, flink_1.unauthorized)("Invalid username or password", loginRespons.status)];
81
+ return [2 /*return*/, (0, flink_1.unauthorized)("Invalid username or password", loginResponse.status)];
56
82
  }
57
83
  }
58
84
  return [2 /*return*/, {
59
- data: loginRespons,
85
+ data: loginResponse,
60
86
  status: 200,
61
87
  }];
62
88
  }
63
89
  });
64
90
  }); };
65
91
  exports.default = userLoginHandler;
92
+ function isFlinkError(res) {
93
+ if (res && res.status && typeof res.status === "number" && res.error && res.error.id) {
94
+ return true;
95
+ }
96
+ return false;
97
+ }
66
98
  exports.__assumedHttpMethod = "", exports.__file = "UserLogin.ts", exports.__query = [], exports.__params = [];
67
99
  exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "username": { "type": "string" }, "password": { "type": "string" } }, "required": ["username"], "definitions": {} }, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "status": { "type": "string", "enum": ["success", "failed", "requiresValidation"] }, "user": { "type": "object", "properties": { "_id": { "type": "string" }, "username": { "type": "string" }, "token": { "type": "string" }, "profile": { "type": "object" } }, "required": ["_id", "username", "token", "profile"], "additionalProperties": false }, "validationToken": { "type": "string" } }, "required": ["status"], "definitions": {} } };
@@ -67,4 +67,4 @@ var postPasswordResetCompleteHandler = function (_a) { return __awaiter(void 0,
67
67
  }); };
68
68
  exports.default = postPasswordResetCompleteHandler;
69
69
  exports.__assumedHttpMethod = "", exports.__file = "UserPasswordResetComplete.ts", exports.__query = [], exports.__params = [];
70
- exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "passwordResetToken": { "type": "string" }, "code": { "type": "string" }, "password": { "type": "string" } }, "required": ["code", "password", "passwordResetToken"], "definitions": {} }, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "status": { "type": "string", "enum": ["success", "userNotFound", "invalidCode", "passwordError"] } }, "required": ["status"], "definitions": {} } };
70
+ exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "passwordResetToken": { "type": "string" }, "code": { "type": "string" }, "password": { "type": "string" } }, "required": ["code", "password", "passwordResetToken"], "definitions": {} }, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "status": { "type": "string", "enum": ["success", "userNotFound", "invalidCode", "passwordError"] }, "user": {} }, "required": ["status"], "definitions": {} } };
@@ -87,7 +87,7 @@ var postUserPushRegisterTokenHandler = function (_a) { return __awaiter(void 0,
87
87
  if (!deregisterOtherDevices) return [3 /*break*/, 9];
88
88
  return [4 /*yield*/, repo.findAll({
89
89
  $or: [{ "pushNotificationTokens.deviceId": req.body.deviceId }, { "pushNotificationTokens.token": req.body.token }],
90
- _id: { $ne: user._id },
90
+ _id: { $ne: repo.buildId(user._id) },
91
91
  })];
92
92
  case 3:
93
93
  otherRegistrations = _d.sent();
@@ -3,11 +3,12 @@ import { UserProfile } from "./UserProfile";
3
3
  export interface User {
4
4
  _id: string;
5
5
  username: string;
6
+ personalNumber?: string;
6
7
  password?: string;
7
8
  salt?: string;
8
9
  pwdResetStartedAt?: string | null;
9
10
  roles: string[];
10
- authentificationMethod: "password" | "sms";
11
+ authentificationMethod: "password" | "sms" | "bankid";
11
12
  profile: UserProfile;
12
13
  pushNotificationTokens: Array<PushNotificationToken>;
13
14
  }
@@ -2,6 +2,7 @@ import { UserProfile } from "./UserProfile";
2
2
  export interface UserCreateReq {
3
3
  username: string;
4
4
  password?: string;
5
- authentificationMethod?: "password" | "sms";
5
+ personalNumber?: string;
6
+ authentificationMethod?: "password" | "sms" | "bankid";
6
7
  profile?: UserProfile;
7
8
  }
@@ -1,3 +1,7 @@
1
1
  export interface UserPasswordResetCompleteRes {
2
2
  status: "success" | "userNotFound" | "invalidCode" | "passwordError";
3
+ /**
4
+ * The user object is returned only if the status is "success".
5
+ */
6
+ user?: any;
3
7
  }
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@flink-app/generic-auth-plugin",
3
- "version": "0.12.1-alpha.3",
3
+ "version": "0.12.1-alpha.30",
4
4
  "description": "Flink plugin that provides a generic user authentification solution.",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\"",
7
7
  "build": "flink build",
8
- "prepublish": "npm run build",
8
+ "prepare": "npm run build",
9
9
  "watch": "nodemon --exec \"flink build\""
10
10
  },
11
11
  "author": "johan@frost.se",
@@ -16,19 +16,19 @@
16
16
  "types": "dist/src/index.d.ts",
17
17
  "main": "dist/src/index.js",
18
18
  "dependencies": {
19
- "@flink-app/email-plugin": "^0.12.1-alpha.3",
20
- "@flink-app/jwt-auth-plugin": "^0.12.1-alpha.3",
21
- "@flink-app/management-api-plugin": "^0.12.1-alpha.3",
22
- "@flink-app/sms-plugin": "^0.12.1-alpha.3",
19
+ "@flink-app/email-plugin": "^0.12.1-alpha.23",
20
+ "@flink-app/jwt-auth-plugin": "^0.12.1-alpha.23",
21
+ "@flink-app/management-api-plugin": "^0.12.1-alpha.23",
22
+ "@flink-app/sms-plugin": "^0.12.1-alpha.23",
23
23
  "handlebars": "^4.7.7",
24
24
  "jsonwebtoken": "^8.5.1"
25
25
  },
26
26
  "devDependencies": {
27
- "@flink-app/flink": "^0.12.1-alpha.3",
27
+ "@flink-app/flink": "^0.12.1-alpha.23",
28
28
  "@types/jsonwebtoken": "^8.5.2",
29
29
  "@types/node": "22.13.10",
30
30
  "ts-node": "^9.1.1",
31
31
  "typescript": "5.4.5"
32
32
  },
33
- "gitHead": "51b6524e233acb2430953ffaed6382909f34db8e"
33
+ "gitHead": "29be118cfa4f008e3b8e0b22d08374880222124e"
34
34
  }
@@ -1,4 +1,4 @@
1
- import { FlinkRepo, FlinkAuthUser, log } from "@flink-app/flink";
1
+ import { FlinkRepo, FlinkAuthUser, log, FlinkRequest } from "@flink-app/flink";
2
2
  import { JwtAuthPlugin, jwtAuthPlugin } from "@flink-app/jwt-auth-plugin";
3
3
 
4
4
  import { User } from "./schemas/User";
@@ -45,7 +45,7 @@ export async function createUser(
45
45
  auth: JwtAuthPlugin,
46
46
  username: string,
47
47
  password: string,
48
- authentificationMethod: "password" | "sms",
48
+ authentificationMethod: "password" | "sms" | "bankid",
49
49
  roles: string[],
50
50
  profile: UserProfile,
51
51
  createPasswordHashAndSaltMethod?: {
@@ -53,7 +53,8 @@ export async function createUser(
53
53
  },
54
54
  onUserCreated?: {
55
55
  (user: User): Promise<void>;
56
- }
56
+ },
57
+ personalNumber?: string
57
58
  ): Promise<UserCreateRes> {
58
59
  if (!roles.includes("user")) roles.push("user");
59
60
 
@@ -71,6 +72,17 @@ export async function createUser(
71
72
  pushNotificationTokens: [],
72
73
  };
73
74
 
75
+ if (personalNumber) {
76
+ userData.personalNumber = personalNumber;
77
+ }
78
+
79
+ if (authentificationMethod == "bankid") {
80
+ if (!personalNumber) {
81
+ log.warn("BankID login requested but no personal number found for user");
82
+ return { status: "error" };
83
+ }
84
+ }
85
+
74
86
  if (authentificationMethod == "password") {
75
87
  let passwordAndSalt = null;
76
88
  if (createPasswordHashAndSaltMethod != null) {
@@ -152,8 +164,9 @@ export async function loginUser(
152
164
  },
153
165
  smsOptions?: GenericAuthsmsOptions,
154
166
  onSuccessfulLogin?: {
155
- (user: User): Promise<void>;
156
- }
167
+ (user: User, req?: FlinkRequest): Promise<void>;
168
+ },
169
+ req?: FlinkRequest
157
170
  ): Promise<UserLoginRes> {
158
171
  const user = await repo.getOne({ username: username.toLowerCase() });
159
172
  if (user == null) {
@@ -205,12 +218,21 @@ export async function loginUser(
205
218
  validationToken: token,
206
219
  };
207
220
  }
221
+ if (user.authentificationMethod == "bankid") {
222
+ if (!user.personalNumber) {
223
+ log.warn("BankID login requested but no personal number found for user");
224
+ return { status: "failed" };
225
+ }
226
+
227
+ log.warn("BankID login required to be handled in other way, i.e. using flink bankid plugin");
228
+ return { status: "failed" };
229
+ }
208
230
 
209
231
  if (valid) {
210
232
  const token = await auth.createToken({ username: username.toLowerCase(), _id: user._id }, user.roles);
211
233
 
212
234
  if (onSuccessfulLogin) {
213
- await onSuccessfulLogin(user);
235
+ await onSuccessfulLogin(user, req);
214
236
  }
215
237
 
216
238
  return {
@@ -382,7 +404,7 @@ export async function passwordResetComplete(
382
404
  pwdResetStartedAt: null,
383
405
  });
384
406
 
385
- return { status: "success" };
407
+ return { status: "success", user };
386
408
  }
387
409
 
388
410
  function generate(n: number): string {
@@ -1,4 +1,4 @@
1
- import { FlinkRepo } from "@flink-app/flink";
1
+ import { FlinkRepo, FlinkRequest } from "@flink-app/flink";
2
2
  import { JwtAuthPlugin } from "@flink-app/jwt-auth-plugin";
3
3
  import { User } from "./schemas/User";
4
4
  import { UserCreateRes } from "./schemas/UserCreateRes";
@@ -19,7 +19,8 @@ export interface genericAuthContext {
19
19
  password?: string,
20
20
  validatePasswordMethod?: { (password: string, hash: string, salt: string): Promise<boolean> },
21
21
  smsOptions?: GenericAuthsmsOptions,
22
- onSuccessfulLogin?: (user: User) => Promise<void>
22
+ onSuccessfulLogin?: (user: User, req?: FlinkRequest) => Promise<void>,
23
+ req?: FlinkRequest
23
24
  ): Promise<UserLoginRes>;
24
25
  loginByToken(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, token: string, code: string, jwtSecret: string): Promise<UserLoginRes>;
25
26
  createUser(
@@ -27,13 +28,14 @@ export interface genericAuthContext {
27
28
  auth: JwtAuthPlugin,
28
29
  username: string,
29
30
  password: string,
30
- authentificationMethod: "password" | "sms",
31
+ authentificationMethod: "password" | "sms" | "bankid",
31
32
  roles: string[],
32
33
  profile: UserProfile,
33
34
  createPasswordHashAndSaltMethod?: {
34
35
  (password: string): Promise<{ hash: string; salt: string } | null>;
35
36
  },
36
- onUserCreated?: (user: User) => Promise<void>
37
+ onUserCreated?: (user: User) => Promise<void>,
38
+ personalNumber?: string
37
39
  ): Promise<UserCreateRes>;
38
40
  changePassword(
39
41
  repo: FlinkRepo<any, User>,
@@ -67,7 +69,7 @@ export interface genericAuthContext {
67
69
  validatePasswordMethod?: { (password: string, hash: string, salt: string): Promise<boolean> };
68
70
  usernameFormat: RegExp;
69
71
  smsOptions?: GenericAuthsmsOptions;
70
- onSuccessfulLogin?: { (user: User): Promise<void> };
72
+ onSuccessfulLogin?: { (user: User, req?: FlinkRequest): Promise<void> };
71
73
  onUserCreated?: { (user: User): Promise<void> };
72
74
  };
73
75
  }
@@ -1,3 +1,4 @@
1
+ import { FlinkRequest } from "@flink-app/flink";
1
2
  import { User } from "./schemas/User";
2
3
  import { UserPasswordResetSettings } from "./schemas/UserPasswordResetSettings";
3
4
  import { client as smsClient } from "@flink-app/sms-plugin";
@@ -23,7 +24,7 @@ export interface GenericAuthPluginOptions {
23
24
  usernameFormat?: RegExp;
24
25
  sms?: GenericAuthsmsOptions;
25
26
  onSuccessfulLogin?: {
26
- (user: User): Promise<void>;
27
+ (user: User, req?: FlinkRequest): Promise<void>;
27
28
  };
28
29
  onUserCreated?: {
29
30
  (user: User): Promise<void>;
@@ -5,7 +5,7 @@ import { UserCreateReq } from "../schemas/UserCreateReq";
5
5
  import { UserCreateRes } from "../schemas/UserCreateRes";
6
6
 
7
7
  const userCreateHandler: Handler<FlinkContext<genericAuthContext>, UserCreateReq, UserCreateRes> = async ({ ctx, req, origin }) => {
8
- let { password, username, authentificationMethod, profile } = req.body;
8
+ let { password, username, authentificationMethod, profile, personalNumber } = req.body;
9
9
  if (authentificationMethod == null) {
10
10
  authentificationMethod = "password";
11
11
  }
@@ -36,7 +36,8 @@ const userCreateHandler: Handler<FlinkContext<genericAuthContext>, UserCreateReq
36
36
  roles,
37
37
  profile,
38
38
  (<any>ctx.plugins)[pluginName].createPasswordHashAndSaltMethod,
39
- (<any>ctx.plugins)[pluginName].onUserCreated
39
+ (<any>ctx.plugins)[pluginName].onUserCreated,
40
+ personalNumber
40
41
  );
41
42
  if (createUserResponse.status != "success") {
42
43
  switch (createUserResponse.status) {
@@ -1,41 +1,66 @@
1
- import { FlinkContext, Handler, unauthorized } from "@flink-app/flink";
1
+ import { FlinkContext, FlinkResponse, Handler, internalServerError, log, unauthorized } from "@flink-app/flink";
2
+ import { JwtAuthPlugin } from "@flink-app/jwt-auth-plugin";
2
3
  import { genericAuthContext } from "../genericAuthContext";
3
4
  import { UserLoginReq } from "../schemas/UserLoginReq";
4
5
  import { UserLoginRes } from "../schemas/UserLoginRes";
5
- import { JwtAuthPlugin } from "@flink-app/jwt-auth-plugin";
6
6
 
7
- const userLoginHandler: Handler<
8
- FlinkContext<genericAuthContext>,
9
- UserLoginReq,
10
- UserLoginRes
11
- > = async ({ ctx, req, origin }) => {
12
- let pluginName = origin || "genericAuthPlugin";
13
- let repo = ctx.repos[(<any>ctx.plugins)[pluginName].repoName];
14
-
15
- const loginRespons = await ctx.plugins.genericAuthPlugin.loginUser(
16
- repo,
17
- <JwtAuthPlugin>ctx.auth,
18
- req.body.username,
19
- req.body.password,
20
- ctx.plugins.genericAuthPlugin.validatePasswordMethod,
21
- (<any>ctx.plugins)[pluginName].smsOptions,
22
- (<any>ctx.plugins)[pluginName].onSuccessfulLogin
23
- );
24
-
25
- if (loginRespons.status != "success") {
26
- switch (loginRespons.status) {
27
- case "failed":
28
- return unauthorized(
29
- "Invalid username or password",
30
- loginRespons.status
7
+ const userLoginHandler: Handler<FlinkContext<genericAuthContext>, UserLoginReq, UserLoginRes> = async ({ ctx, req, origin }) => {
8
+ let pluginName = origin || "genericAuthPlugin";
9
+ let repo = ctx.repos[(<any>ctx.plugins)[pluginName].repoName];
10
+
11
+ let loginResponse: UserLoginRes | undefined = undefined;
12
+
13
+ try {
14
+ loginResponse = await ctx.plugins.genericAuthPlugin.loginUser(
15
+ repo,
16
+ <JwtAuthPlugin>ctx.auth,
17
+ req.body.username,
18
+ req.body.password,
19
+ ctx.plugins.genericAuthPlugin.validatePasswordMethod,
20
+ (<any>ctx.plugins)[pluginName].smsOptions,
21
+ (<any>ctx.plugins)[pluginName].onSuccessfulLogin,
22
+ req
31
23
  );
24
+ } catch (error: any) {
25
+ // Convert any thrown error that conforms to flink error structure to a proper response
26
+ // Note that any auth failures would not have been thrown, but returned as part of loginResponse
27
+ // but with this it is possible to throw errors from callbacks like onSuccessfulLogin
28
+ if (isFlinkError(error)) {
29
+ log.debug("Caught FlinkError in userLoginHandler:", error);
30
+ return {
31
+ status: error.status,
32
+ error: {
33
+ id: error.error.id,
34
+ title: error.error.title,
35
+ code: error.error.code,
36
+ detail: error.error.detail,
37
+ },
38
+ } as FlinkResponse;
39
+ }
40
+
41
+ // For other errors, return a generic 500 response
42
+ log.error("Error in userLoginHandler:", error);
43
+ return internalServerError();
32
44
  }
33
- }
34
45
 
35
- return {
36
- data: loginRespons,
37
- status: 200,
38
- };
46
+ if (loginResponse?.status != "success") {
47
+ switch (loginResponse?.status) {
48
+ case "failed":
49
+ return unauthorized("Invalid username or password", loginResponse.status);
50
+ }
51
+ }
52
+
53
+ return {
54
+ data: loginResponse,
55
+ status: 200,
56
+ };
39
57
  };
40
58
 
41
59
  export default userLoginHandler;
60
+
61
+ function isFlinkError(res: any) {
62
+ if (res && res.status && typeof res.status === "number" && res.error && res.error.id) {
63
+ return true;
64
+ }
65
+ return false;
66
+ }
@@ -43,7 +43,7 @@ const postUserPushRegisterTokenHandler: Handler<FlinkContext<genericAuthContext>
43
43
  if (deregisterOtherDevices) {
44
44
  const otherRegistrations = <User[]>await repo.findAll({
45
45
  $or: [{ "pushNotificationTokens.deviceId": req.body.deviceId }, { "pushNotificationTokens.token": req.body.token }],
46
- _id: { $ne: user._id },
46
+ _id: { $ne: repo.buildId(user._id) },
47
47
  });
48
48
 
49
49
  log.debug(`Found ${otherRegistrations.length} other registrations for device ${req.body.deviceId} or token ${req.body.token}`);
@@ -4,6 +4,7 @@ import { UserProfile } from "./UserProfile";
4
4
  export interface User {
5
5
  _id: string;
6
6
  username: string;
7
+ personalNumber?: string;
7
8
 
8
9
  password?: string;
9
10
  salt?: string;
@@ -11,7 +12,7 @@ export interface User {
11
12
  pwdResetStartedAt?: string | null;
12
13
  roles: string[];
13
14
 
14
- authentificationMethod: "password" | "sms";
15
+ authentificationMethod: "password" | "sms" | "bankid";
15
16
  profile: UserProfile;
16
17
  pushNotificationTokens: Array<PushNotificationToken>;
17
18
  }
@@ -1,8 +1,9 @@
1
1
  import { UserProfile } from "./UserProfile";
2
2
 
3
- export interface UserCreateReq{
3
+ export interface UserCreateReq {
4
4
  username: string;
5
5
  password?: string;
6
- authentificationMethod? : "password" | "sms"
7
- profile? : UserProfile
8
- }
6
+ personalNumber?: string;
7
+ authentificationMethod?: "password" | "sms" | "bankid";
8
+ profile?: UserProfile;
9
+ }
@@ -1,3 +1,8 @@
1
- export interface UserPasswordResetCompleteRes{
2
- status : "success" | "userNotFound" | "invalidCode" | "passwordError";
3
- }
1
+ export interface UserPasswordResetCompleteRes {
2
+ status: "success" | "userNotFound" | "invalidCode" | "passwordError";
3
+
4
+ /**
5
+ * The user object is returned only if the status is "success".
6
+ */
7
+ user?: any;
8
+ }
package/CLAUDE.md DELETED
@@ -1,32 +0,0 @@
1
- # CLAUDE.md - Guidelines for Generic Auth Plugin
2
-
3
- ## Build Commands
4
- - Build: `npm run build` (runs `flink build`)
5
- - Watch mode: `npm run watch` (uses nodemon to watch files and trigger builds)
6
- - Publish: `npm run prepublish` (runs build before publishing)
7
-
8
- ## Code Style
9
-
10
- ### TypeScript
11
- - Target: ES5, CommonJS modules, strict typing enabled
12
- - Keep type definitions in separate schema files in the `/schemas` directory
13
- - Use explicit return types on all functions (eg: `Promise<UserLoginRes>`)
14
- - Always handle optionality with `?` and `undefined`/`null` checks
15
-
16
- ### Naming Conventions
17
- - camelCase for variables, functions, and methods
18
- - PascalCase for interfaces and types
19
- - Request types use `Req` suffix (UserLoginReq)
20
- - Response types use `Res` suffix (UserLoginRes)
21
- - Handler files match their function name (UserLogin.ts)
22
-
23
- ### Error Handling
24
- - Use structured error responses with status fields and messages
25
- - Return typed response objects rather than throwing exceptions
26
- - Use try/catch blocks for operations that might fail (JWT verification)
27
-
28
- ### Code Structure
29
- - Group imports: framework first, then local, then third-party
30
- - Separate authentication methods and utility functions
31
- - Use standard 4-space indentation with semicolons
32
- - Maintain consistent function parameter ordering