@flink-app/generic-auth-plugin 0.3.7 → 0.3.9
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/.flink/generatedHandlers.ts +2 -1
- package/.flink/generatedRepos.ts +1 -1
- package/.flink/schemas/schemas.json +60 -0
- package/.flink/schemas/schemas.ts +6 -1
- package/.flink/start.ts +1 -1
- package/dist/.flink/generatedHandlers.js +1 -1
- package/dist/.flink/generatedRepos.js +1 -1
- package/dist/.flink/schemas/schemas.d.ts +5 -0
- package/dist/.flink/schemas/schemas.json +60 -0
- package/dist/.flink/start.js +1 -1
- package/dist/src/coreFunctions.d.ts +3 -1
- package/dist/src/coreFunctions.js +76 -6
- package/dist/src/genericAuthContext.d.ts +4 -1
- package/dist/src/genericAuthPluginOptions.d.ts +10 -0
- package/dist/src/handlers/UserLogin.js +1 -1
- package/dist/src/handlers/UserLoginByToken.d.ts +8 -0
- package/dist/src/handlers/UserLoginByToken.js +69 -0
- package/dist/src/index.js +2 -0
- package/dist/src/init.js +9 -0
- package/dist/src/schemas/UserLoginByTokenReq.d.ts +4 -0
- package/dist/src/schemas/UserLoginByTokenReq.js +2 -0
- package/package.json +7 -6
- package/readme.md +147 -0
- package/src/coreFunctions.ts +97 -3
- package/src/genericAuthContext.ts +4 -1
- package/src/genericAuthPluginOptions.ts +29 -18
- package/src/handlers/UserLogin.ts +3 -1
- package/src/handlers/UserLoginByToken.ts +39 -0
- package/src/index.ts +3 -0
- package/src/init.ts +9 -0
- package/src/schemas/UserCreateRes.ts +7 -7
- package/src/schemas/UserLoginByTokenReq.ts +4 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
// Generated
|
|
1
|
+
// Generated Sun Oct 16 2022 13:16:37 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";
|
|
5
|
+
import * as UserLoginByToken_0 from "../src/handlers/UserLoginByToken";
|
|
5
6
|
import * as UserPasswordPut_0 from "../src/handlers/UserPasswordPut";
|
|
6
7
|
import * as UserPasswordResetComplete_0 from "../src/handlers/UserPasswordResetComplete";
|
|
7
8
|
import * as UserPasswordResetForm_0 from "../src/handlers/UserPasswordResetForm";
|
package/.flink/generatedRepos.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Generated
|
|
1
|
+
// Generated Sun Oct 16 2022 13:16:37 GMT+0200 (Central European Summer Time)
|
|
2
2
|
import { autoRegisteredRepos } from "@flink-app/flink";
|
|
3
3
|
export const repos = [];
|
|
4
4
|
autoRegisteredRepos.push(...repos);
|
|
@@ -127,6 +127,66 @@
|
|
|
127
127
|
"status"
|
|
128
128
|
]
|
|
129
129
|
},
|
|
130
|
+
"UserLoginByToken_7_ReqSchema": {
|
|
131
|
+
"type": "object",
|
|
132
|
+
"additionalProperties": false,
|
|
133
|
+
"properties": {
|
|
134
|
+
"token": {
|
|
135
|
+
"type": "string"
|
|
136
|
+
},
|
|
137
|
+
"code": {
|
|
138
|
+
"type": "string"
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
"required": [
|
|
142
|
+
"code",
|
|
143
|
+
"token"
|
|
144
|
+
]
|
|
145
|
+
},
|
|
146
|
+
"UserLoginByToken_7_ResSchema": {
|
|
147
|
+
"type": "object",
|
|
148
|
+
"additionalProperties": false,
|
|
149
|
+
"properties": {
|
|
150
|
+
"status": {
|
|
151
|
+
"type": "string",
|
|
152
|
+
"enum": [
|
|
153
|
+
"success",
|
|
154
|
+
"failed",
|
|
155
|
+
"requiresValidation"
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
"user": {
|
|
159
|
+
"type": "object",
|
|
160
|
+
"properties": {
|
|
161
|
+
"_id": {
|
|
162
|
+
"type": "string"
|
|
163
|
+
},
|
|
164
|
+
"username": {
|
|
165
|
+
"type": "string"
|
|
166
|
+
},
|
|
167
|
+
"token": {
|
|
168
|
+
"type": "string"
|
|
169
|
+
},
|
|
170
|
+
"profile": {
|
|
171
|
+
"$ref": "#/definitions/UserProfile"
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
"required": [
|
|
175
|
+
"_id",
|
|
176
|
+
"username",
|
|
177
|
+
"token",
|
|
178
|
+
"profile"
|
|
179
|
+
],
|
|
180
|
+
"additionalProperties": false
|
|
181
|
+
},
|
|
182
|
+
"validationToken": {
|
|
183
|
+
"type": "string"
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
"required": [
|
|
187
|
+
"status"
|
|
188
|
+
]
|
|
189
|
+
},
|
|
130
190
|
"UserPasswordPut_7_ReqSchema": {
|
|
131
191
|
"type": "object",
|
|
132
192
|
"additionalProperties": false,
|
|
@@ -2,6 +2,7 @@ import { UserCreateReq } from "../../src/schemas/UserCreateReq";
|
|
|
2
2
|
import { UserCreateRes } from "../../src/schemas/UserCreateRes";
|
|
3
3
|
import { UserLoginReq } from "../../src/schemas/UserLoginReq";
|
|
4
4
|
import { UserLoginRes } from "../../src/schemas/UserLoginRes";
|
|
5
|
+
import { UserLoginByTokenReq } from "../../src/schemas/UserLoginByTokenReq";
|
|
5
6
|
import { UserPasswordChangeReq } from "../../src/schemas/UserPasswordChangeReq";
|
|
6
7
|
import { UserPasswordChangeRes } from "../../src/schemas/UserPasswordChangeRes";
|
|
7
8
|
import { UserPasswordResetCompleteReq } from "../../src/schemas/UserPasswordResetCompleteReq";
|
|
@@ -29,7 +30,7 @@ import { PutManagementUserRolesByUseridRes } from "../../src/schemas/Management/
|
|
|
29
30
|
import { PutManagementUserUsernameByUseridReq } from "../../src/schemas/Management/PutUserUsernameByUseridReq";
|
|
30
31
|
import { PutManagementUserUsernameByUseridRes } from "../../src/schemas/Management/PutUserUsernameByUseridRes";
|
|
31
32
|
|
|
32
|
-
// Generated
|
|
33
|
+
// Generated Sun Oct 16 2022 13:16:45 GMT+0200 (Central European Summer Time)
|
|
33
34
|
export interface UserCreate_7_ReqSchema extends UserCreateReq {}
|
|
34
35
|
|
|
35
36
|
export interface UserCreate_7_ResSchema extends UserCreateRes {}
|
|
@@ -38,6 +39,10 @@ export interface UserLogin_7_ReqSchema extends UserLoginReq {}
|
|
|
38
39
|
|
|
39
40
|
export interface UserLogin_7_ResSchema extends UserLoginRes {}
|
|
40
41
|
|
|
42
|
+
export interface UserLoginByToken_7_ReqSchema extends UserLoginByTokenReq {}
|
|
43
|
+
|
|
44
|
+
export interface UserLoginByToken_7_ResSchema extends UserLoginRes {}
|
|
45
|
+
|
|
41
46
|
export interface UserPasswordPut_7_ReqSchema extends UserPasswordChangeReq {}
|
|
42
47
|
|
|
43
48
|
export interface UserPasswordPut_7_ResSchema extends UserPasswordChangeRes {}
|
package/.flink/start.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.handlers = void 0;
|
|
4
|
-
// Generated
|
|
4
|
+
// Generated Sun Oct 16 2022 13:16:37 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.repos = void 0;
|
|
4
|
-
// Generated
|
|
4
|
+
// Generated Sun Oct 16 2022 13:16:37 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);
|
|
@@ -2,6 +2,7 @@ import { UserCreateReq } from "../../src/schemas/UserCreateReq";
|
|
|
2
2
|
import { UserCreateRes } from "../../src/schemas/UserCreateRes";
|
|
3
3
|
import { UserLoginReq } from "../../src/schemas/UserLoginReq";
|
|
4
4
|
import { UserLoginRes } from "../../src/schemas/UserLoginRes";
|
|
5
|
+
import { UserLoginByTokenReq } from "../../src/schemas/UserLoginByTokenReq";
|
|
5
6
|
import { UserPasswordChangeReq } from "../../src/schemas/UserPasswordChangeReq";
|
|
6
7
|
import { UserPasswordChangeRes } from "../../src/schemas/UserPasswordChangeRes";
|
|
7
8
|
import { UserPasswordResetCompleteReq } from "../../src/schemas/UserPasswordResetCompleteReq";
|
|
@@ -36,6 +37,10 @@ export interface UserLogin_7_ReqSchema extends UserLoginReq {
|
|
|
36
37
|
}
|
|
37
38
|
export interface UserLogin_7_ResSchema extends UserLoginRes {
|
|
38
39
|
}
|
|
40
|
+
export interface UserLoginByToken_7_ReqSchema extends UserLoginByTokenReq {
|
|
41
|
+
}
|
|
42
|
+
export interface UserLoginByToken_7_ResSchema extends UserLoginRes {
|
|
43
|
+
}
|
|
39
44
|
export interface UserPasswordPut_7_ReqSchema extends UserPasswordChangeReq {
|
|
40
45
|
}
|
|
41
46
|
export interface UserPasswordPut_7_ResSchema extends UserPasswordChangeRes {
|
|
@@ -127,6 +127,66 @@
|
|
|
127
127
|
"status"
|
|
128
128
|
]
|
|
129
129
|
},
|
|
130
|
+
"UserLoginByToken_7_ReqSchema": {
|
|
131
|
+
"type": "object",
|
|
132
|
+
"additionalProperties": false,
|
|
133
|
+
"properties": {
|
|
134
|
+
"token": {
|
|
135
|
+
"type": "string"
|
|
136
|
+
},
|
|
137
|
+
"code": {
|
|
138
|
+
"type": "string"
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
"required": [
|
|
142
|
+
"code",
|
|
143
|
+
"token"
|
|
144
|
+
]
|
|
145
|
+
},
|
|
146
|
+
"UserLoginByToken_7_ResSchema": {
|
|
147
|
+
"type": "object",
|
|
148
|
+
"additionalProperties": false,
|
|
149
|
+
"properties": {
|
|
150
|
+
"status": {
|
|
151
|
+
"type": "string",
|
|
152
|
+
"enum": [
|
|
153
|
+
"success",
|
|
154
|
+
"failed",
|
|
155
|
+
"requiresValidation"
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
"user": {
|
|
159
|
+
"type": "object",
|
|
160
|
+
"properties": {
|
|
161
|
+
"_id": {
|
|
162
|
+
"type": "string"
|
|
163
|
+
},
|
|
164
|
+
"username": {
|
|
165
|
+
"type": "string"
|
|
166
|
+
},
|
|
167
|
+
"token": {
|
|
168
|
+
"type": "string"
|
|
169
|
+
},
|
|
170
|
+
"profile": {
|
|
171
|
+
"$ref": "#/definitions/UserProfile"
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
"required": [
|
|
175
|
+
"_id",
|
|
176
|
+
"username",
|
|
177
|
+
"token",
|
|
178
|
+
"profile"
|
|
179
|
+
],
|
|
180
|
+
"additionalProperties": false
|
|
181
|
+
},
|
|
182
|
+
"validationToken": {
|
|
183
|
+
"type": "string"
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
"required": [
|
|
187
|
+
"status"
|
|
188
|
+
]
|
|
189
|
+
},
|
|
130
190
|
"UserPasswordPut_7_ReqSchema": {
|
|
131
191
|
"type": "object",
|
|
132
192
|
"additionalProperties": false,
|
package/dist/.flink/start.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
// Generated
|
|
3
|
+
// Generated Sun Oct 16 2022 13:16:38 GMT+0200 (Central European Summer Time)
|
|
4
4
|
require("./generatedHandlers");
|
|
5
5
|
require("./generatedRepos");
|
|
6
6
|
require("../src/index");
|
|
@@ -7,6 +7,7 @@ import { UserProfile } from "./schemas/UserProfile";
|
|
|
7
7
|
import { UserPasswordChangeRes } from "./schemas/UserPasswordChangeRes";
|
|
8
8
|
import { UserPasswordResetStartRes } from "./schemas/UserPasswordResetStartRes";
|
|
9
9
|
import { UserPasswordResetCompleteRes } from "./schemas/UserPasswordResetCompleteRes";
|
|
10
|
+
import { GenericAuthsmsOptions } from "./genericAuthPluginOptions";
|
|
10
11
|
export declare function getJtwTokenPlugin(secret: string, rolePermissions?: {
|
|
11
12
|
[role: string]: string[];
|
|
12
13
|
}, passwordPolicy?: RegExp): JwtAuthPlugin;
|
|
@@ -16,9 +17,10 @@ export declare function createUser(repo: FlinkRepo<any, User>, auth: JwtAuthPlug
|
|
|
16
17
|
salt: string;
|
|
17
18
|
} | null>;
|
|
18
19
|
}): Promise<UserCreateRes>;
|
|
20
|
+
export declare function loginByToken(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, token: string, code: string, jwtSecret: string): Promise<UserLoginRes>;
|
|
19
21
|
export declare function loginUser(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, username: string, password: string | undefined, validatePasswordMethod?: {
|
|
20
22
|
(password: string, hash: string, salt: string): Promise<boolean>;
|
|
21
|
-
}): Promise<UserLoginRes>;
|
|
23
|
+
}, smsOptions?: GenericAuthsmsOptions): Promise<UserLoginRes>;
|
|
22
24
|
export declare function changePassword(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, userId: string, newPassword: string, createPasswordHashAndSaltMethod?: {
|
|
23
25
|
(password: string): Promise<{
|
|
24
26
|
hash: string;
|
|
@@ -39,8 +39,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
39
39
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
40
|
};
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
-
exports.passwordResetComplete = exports.passwordResetStart = exports.changePassword = exports.loginUser = exports.createUser = exports.getJtwTokenPlugin = void 0;
|
|
43
|
-
var flink_1 = require("@flink-app/flink");
|
|
42
|
+
exports.passwordResetComplete = exports.passwordResetStart = exports.changePassword = exports.loginUser = exports.loginByToken = exports.createUser = exports.getJtwTokenPlugin = void 0;
|
|
44
43
|
var jwt_auth_plugin_1 = require("@flink-app/jwt-auth-plugin");
|
|
45
44
|
var jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
46
45
|
function getJtwTokenPlugin(secret, rolePermissions, passwordPolicy) {
|
|
@@ -119,6 +118,11 @@ function createUser(repo, auth, username, password, authentificationMethod, role
|
|
|
119
118
|
return [4 /*yield*/, auth.createToken({ username: username.toLowerCase(), _id: user._id }, roles)];
|
|
120
119
|
case 8:
|
|
121
120
|
token = _a.sent();
|
|
121
|
+
if (user.authentificationMethod == "sms") {
|
|
122
|
+
return [2 /*return*/, {
|
|
123
|
+
status: "success",
|
|
124
|
+
}];
|
|
125
|
+
}
|
|
122
126
|
return [2 /*return*/, {
|
|
123
127
|
status: "success",
|
|
124
128
|
user: {
|
|
@@ -132,9 +136,47 @@ function createUser(repo, auth, username, password, authentificationMethod, role
|
|
|
132
136
|
});
|
|
133
137
|
}
|
|
134
138
|
exports.createUser = createUser;
|
|
135
|
-
function
|
|
139
|
+
function loginByToken(repo, auth, token, code, jwtSecret) {
|
|
140
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
141
|
+
var payload, user, authToken;
|
|
142
|
+
return __generator(this, function (_a) {
|
|
143
|
+
switch (_a.label) {
|
|
144
|
+
case 0:
|
|
145
|
+
try {
|
|
146
|
+
payload = jsonwebtoken_1.default.verify(token, jwtSecret + ":" + code);
|
|
147
|
+
}
|
|
148
|
+
catch (ex) {
|
|
149
|
+
return [2 /*return*/, { status: "failed" }];
|
|
150
|
+
}
|
|
151
|
+
if (payload.type != "smsLogin") {
|
|
152
|
+
return [2 /*return*/, { status: "failed" }];
|
|
153
|
+
}
|
|
154
|
+
return [4 /*yield*/, repo.getById(payload.userId)];
|
|
155
|
+
case 1:
|
|
156
|
+
user = _a.sent();
|
|
157
|
+
if (user == null) {
|
|
158
|
+
return [2 /*return*/, { status: "failed" }];
|
|
159
|
+
}
|
|
160
|
+
return [4 /*yield*/, auth.createToken({ username: user.username.toLowerCase(), _id: user._id }, user.roles)];
|
|
161
|
+
case 2:
|
|
162
|
+
authToken = _a.sent();
|
|
163
|
+
return [2 /*return*/, {
|
|
164
|
+
status: "success",
|
|
165
|
+
user: {
|
|
166
|
+
_id: user._id,
|
|
167
|
+
username: user.username,
|
|
168
|
+
token: authToken,
|
|
169
|
+
profile: user.profile,
|
|
170
|
+
},
|
|
171
|
+
}];
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
exports.loginByToken = loginByToken;
|
|
177
|
+
function loginUser(repo, auth, username, password, validatePasswordMethod, smsOptions) {
|
|
136
178
|
return __awaiter(this, void 0, void 0, function () {
|
|
137
|
-
var user, valid, ex_1, token;
|
|
179
|
+
var user, valid, ex_1, code, payload, secret, options, token, token;
|
|
138
180
|
return __generator(this, function (_a) {
|
|
139
181
|
switch (_a.label) {
|
|
140
182
|
case 0: return [4 /*yield*/, repo.getOne({ username: username.toLowerCase() })];
|
|
@@ -169,8 +211,27 @@ function loginUser(repo, auth, username, password, validatePasswordMethod) {
|
|
|
169
211
|
_a.label = 9;
|
|
170
212
|
case 9:
|
|
171
213
|
if (user.authentificationMethod == "sms") {
|
|
172
|
-
|
|
173
|
-
|
|
214
|
+
if (!smsOptions)
|
|
215
|
+
throw "SMS options must be specified to use SMS login";
|
|
216
|
+
code = smsOptions.codeType == "numeric" ? generate(smsOptions.codeLength) : generateString(smsOptions.codeLength);
|
|
217
|
+
smsOptions.smsClient.send({
|
|
218
|
+
to: [user.username],
|
|
219
|
+
from: smsOptions.smsFrom,
|
|
220
|
+
message: smsOptions.smsMessage.replace("{{code}}", code)
|
|
221
|
+
});
|
|
222
|
+
payload = {
|
|
223
|
+
type: "smsLogin",
|
|
224
|
+
userId: user._id,
|
|
225
|
+
};
|
|
226
|
+
secret = smsOptions.jwtToken + ":" + code;
|
|
227
|
+
options = {
|
|
228
|
+
expiresIn: "1h",
|
|
229
|
+
};
|
|
230
|
+
token = jsonwebtoken_1.default.sign(payload, secret, options);
|
|
231
|
+
return [2 /*return*/, {
|
|
232
|
+
status: "success",
|
|
233
|
+
validationToken: token
|
|
234
|
+
}];
|
|
174
235
|
}
|
|
175
236
|
if (!valid) return [3 /*break*/, 11];
|
|
176
237
|
return [4 /*yield*/, auth.createToken({ username: username.toLowerCase(), _id: user._id }, user.roles)];
|
|
@@ -333,3 +394,12 @@ function generate(n) {
|
|
|
333
394
|
var number = Math.floor(Math.random() * (max - min + 1)) + min;
|
|
334
395
|
return ("" + number).substring(add);
|
|
335
396
|
}
|
|
397
|
+
function generateString(length) {
|
|
398
|
+
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
399
|
+
var result = ' ';
|
|
400
|
+
var charactersLength = characters.length;
|
|
401
|
+
for (var i = 0; i < length; i++) {
|
|
402
|
+
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
|
403
|
+
}
|
|
404
|
+
return result;
|
|
405
|
+
}
|
|
@@ -8,11 +8,13 @@ import { UserPasswordChangeRes } from "./schemas/UserPasswordChangeRes";
|
|
|
8
8
|
import { UserPasswordResetSettings } from "./schemas/UserPasswordResetSettings";
|
|
9
9
|
import { UserPasswordResetStartRes } from "./schemas/UserPasswordResetStartRes";
|
|
10
10
|
import { UserPasswordResetCompleteRes } from "./schemas/UserPasswordResetCompleteRes";
|
|
11
|
+
import { GenericAuthsmsOptions } from "./genericAuthPluginOptions";
|
|
11
12
|
export interface genericAuthContext {
|
|
12
13
|
genericAuthPlugin: {
|
|
13
14
|
loginUser(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, username: string, password?: string, validatePasswordMethod?: {
|
|
14
15
|
(password: string, hash: string, salt: string): Promise<boolean>;
|
|
15
|
-
}): Promise<UserLoginRes>;
|
|
16
|
+
}, smsOptions?: GenericAuthsmsOptions): Promise<UserLoginRes>;
|
|
17
|
+
loginByToken(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, token: string, code: string, jwtSecret: string): Promise<UserLoginRes>;
|
|
16
18
|
createUser(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, username: string, password: string, authentificationMethod: "password" | "sms", roles: string[], profile: UserProfile, createPasswordHashAndSaltMethod?: {
|
|
17
19
|
(password: string): Promise<{
|
|
18
20
|
hash: string;
|
|
@@ -44,5 +46,6 @@ export interface genericAuthContext {
|
|
|
44
46
|
(password: string, hash: string, salt: string): Promise<boolean>;
|
|
45
47
|
};
|
|
46
48
|
usernameFormat: RegExp;
|
|
49
|
+
smsOptions?: GenericAuthsmsOptions;
|
|
47
50
|
};
|
|
48
51
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { UserPasswordResetSettings } from "./schemas/UserPasswordResetSettings";
|
|
2
|
+
import { client as smsClient } from "@flink-app/sms-plugin";
|
|
2
3
|
export interface GenericAuthPluginOptions {
|
|
3
4
|
repoName: string;
|
|
4
5
|
enableRoutes?: boolean;
|
|
@@ -20,4 +21,13 @@ export interface GenericAuthPluginOptions {
|
|
|
20
21
|
(password: string, hash: string, salt: string): Promise<boolean>;
|
|
21
22
|
};
|
|
22
23
|
usernameFormat?: RegExp;
|
|
24
|
+
sms?: GenericAuthsmsOptions;
|
|
25
|
+
}
|
|
26
|
+
export interface GenericAuthsmsOptions {
|
|
27
|
+
smsClient: smsClient;
|
|
28
|
+
smsFrom: string;
|
|
29
|
+
smsMessage: string;
|
|
30
|
+
jwtToken: string;
|
|
31
|
+
codeType: "numeric" | "alphanumeric";
|
|
32
|
+
codeLength: number;
|
|
23
33
|
}
|
|
@@ -47,7 +47,7 @@ var userLoginHandler = function (_a) {
|
|
|
47
47
|
case 0:
|
|
48
48
|
pluginName = origin || "genericAuthPlugin";
|
|
49
49
|
repo = ctx.repos[ctx.plugins[pluginName].repoName];
|
|
50
|
-
return [4 /*yield*/, ctx.plugins.genericAuthPlugin.loginUser(repo, ctx.auth, req.body.username, req.body.password, ctx.plugins.genericAuthPlugin.validatePasswordMethod)];
|
|
50
|
+
return [4 /*yield*/, ctx.plugins.genericAuthPlugin.loginUser(repo, ctx.auth, req.body.username, req.body.password, ctx.plugins.genericAuthPlugin.validatePasswordMethod, ctx.plugins[pluginName].smsOptions)];
|
|
51
51
|
case 1:
|
|
52
52
|
loginRespons = _b.sent();
|
|
53
53
|
if (loginRespons.status != "success") {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { FlinkContext, Handler } from "@flink-app/flink";
|
|
2
|
+
import { genericAuthContext } from "../genericAuthContext";
|
|
3
|
+
import { UserLoginByTokenReq } from "../schemas/UserLoginByTokenReq";
|
|
4
|
+
import { UserLoginRes } from "../schemas/UserLoginRes";
|
|
5
|
+
declare const userLoginHandler: Handler<FlinkContext<genericAuthContext>, UserLoginByTokenReq, UserLoginRes>;
|
|
6
|
+
export default userLoginHandler;
|
|
7
|
+
export declare const __assumedHttpMethod = "", __file = "UserLoginByToken.ts", __query: never[], __params: never[];
|
|
8
|
+
export declare const __schemas: any;
|
|
@@ -0,0 +1,69 @@
|
|
|
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;
|
|
13
|
+
return g = { next: verb(0), "throw": verb(1), "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 (_) 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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.__schemas = exports.__params = exports.__query = exports.__file = exports.__assumedHttpMethod = void 0;
|
|
40
|
+
var flink_1 = require("@flink-app/flink");
|
|
41
|
+
var userLoginHandler = function (_a) {
|
|
42
|
+
var ctx = _a.ctx, req = _a.req, origin = _a.origin;
|
|
43
|
+
return __awaiter(void 0, void 0, void 0, function () {
|
|
44
|
+
var pluginName, repo, loginRespons;
|
|
45
|
+
return __generator(this, function (_b) {
|
|
46
|
+
switch (_b.label) {
|
|
47
|
+
case 0:
|
|
48
|
+
pluginName = origin || "genericAuthPlugin";
|
|
49
|
+
repo = ctx.repos[ctx.plugins[pluginName].repoName];
|
|
50
|
+
return [4 /*yield*/, ctx.plugins.genericAuthPlugin.loginByToken(repo, ctx.auth, req.body.token, req.body.code, ctx.plugins[pluginName].smsOptions.jwtToken)];
|
|
51
|
+
case 1:
|
|
52
|
+
loginRespons = _b.sent();
|
|
53
|
+
if (loginRespons.status != "success") {
|
|
54
|
+
switch (loginRespons.status) {
|
|
55
|
+
case "failed":
|
|
56
|
+
return [2 /*return*/, flink_1.unauthorized("Invalid token or code", loginRespons.status)];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return [2 /*return*/, {
|
|
60
|
+
data: loginRespons,
|
|
61
|
+
status: 200,
|
|
62
|
+
}];
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
exports.default = userLoginHandler;
|
|
68
|
+
exports.__assumedHttpMethod = "", exports.__file = "UserLoginByToken.ts", exports.__query = [], exports.__params = [];
|
|
69
|
+
exports.__schemas = { reqSchema: { "type": "object", "additionalProperties": false, "properties": { "token": { "type": "string" }, "code": { "type": "string" } }, "required": ["code", "token"] }, resSchema: { "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"] } };
|
package/dist/src/index.js
CHANGED
|
@@ -26,6 +26,7 @@ var genericAuthPlugin = function (options) {
|
|
|
26
26
|
init: function (app) { return init_1.init(app, options); },
|
|
27
27
|
ctx: {
|
|
28
28
|
loginUser: coreFunctions_1.loginUser,
|
|
29
|
+
loginByToken: coreFunctions_1.loginByToken,
|
|
29
30
|
createUser: coreFunctions_1.createUser,
|
|
30
31
|
changePassword: coreFunctions_1.changePassword,
|
|
31
32
|
passwordResetStart: coreFunctions_1.passwordResetStart,
|
|
@@ -35,6 +36,7 @@ var genericAuthPlugin = function (options) {
|
|
|
35
36
|
createPasswordHashAndSaltMethod: options.createPasswordHashAndSaltMethod,
|
|
36
37
|
validatePasswordMethod: options.validatePasswordMethod,
|
|
37
38
|
usernameFormat: options.usernameFormat || /.{1,}$/,
|
|
39
|
+
smsOptions: options.sms
|
|
38
40
|
},
|
|
39
41
|
};
|
|
40
42
|
};
|
package/dist/src/init.js
CHANGED
|
@@ -22,6 +22,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
22
22
|
exports.init = void 0;
|
|
23
23
|
var flink_1 = require("@flink-app/flink");
|
|
24
24
|
var userLoginHandler = __importStar(require("./handlers/UserLogin"));
|
|
25
|
+
var userLoginByTokenHandler = __importStar(require("./handlers/UserLoginByToken"));
|
|
25
26
|
var userCreateHandler = __importStar(require("./handlers/UserCreate"));
|
|
26
27
|
var getProfileHandler = __importStar(require("./handlers/UserProfileGet"));
|
|
27
28
|
var putUserProfileHandler = __importStar(require("./handlers/UserProfilePut"));
|
|
@@ -49,6 +50,14 @@ function init(app, options) {
|
|
|
49
50
|
docs: "Authenticates a user",
|
|
50
51
|
origin: options.pluginId,
|
|
51
52
|
});
|
|
53
|
+
if (options.sms) {
|
|
54
|
+
app.addHandler(userLoginByTokenHandler, {
|
|
55
|
+
method: flink_1.HttpMethod.post,
|
|
56
|
+
path: options.baseUrl + "/login-by-token",
|
|
57
|
+
docs: "Authenticates a user by token",
|
|
58
|
+
origin: options.pluginId,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
52
61
|
if (options.enableUserCreation) {
|
|
53
62
|
app.addHandler(userCreateHandler, {
|
|
54
63
|
method: flink_1.HttpMethod.post,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flink-app/generic-auth-plugin",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"description": "Flink plugin that provides a generic user authentification solution.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "echo \"Error: no test specified\"",
|
|
@@ -16,18 +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.3.
|
|
20
|
-
"@flink-app/jwt-auth-plugin": "^0.3.
|
|
21
|
-
"@flink-app/management-api-plugin": "^0.3.
|
|
19
|
+
"@flink-app/email-plugin": "^0.3.8",
|
|
20
|
+
"@flink-app/jwt-auth-plugin": "^0.3.8",
|
|
21
|
+
"@flink-app/management-api-plugin": "^0.3.8",
|
|
22
|
+
"@flink-app/sms-plugin": "^0.3.9",
|
|
22
23
|
"handlebars": "^4.7.7",
|
|
23
24
|
"jsonwebtoken": "^8.5.1"
|
|
24
25
|
},
|
|
25
26
|
"devDependencies": {
|
|
26
|
-
"@flink-app/flink": "^0.3.
|
|
27
|
+
"@flink-app/flink": "^0.3.8",
|
|
27
28
|
"@types/jsonwebtoken": "^8.5.2",
|
|
28
29
|
"@types/node": "^15.6.2",
|
|
29
30
|
"ts-node": "^9.1.1",
|
|
30
31
|
"typescript": "^4.2.4"
|
|
31
32
|
},
|
|
32
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "660a90494e67b768a560c3618d8a3da3cc5d5144"
|
|
33
34
|
}
|
package/readme.md
CHANGED
|
@@ -742,3 +742,150 @@ export interface Profile{
|
|
|
742
742
|
type : myType
|
|
743
743
|
}
|
|
744
744
|
```
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
## Using SMS login
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
### prerequisites
|
|
752
|
+
- A SMS client must be setup using the [sms-plugin](https://github.com/FrostDigital/flink-framework/tree/main/packages/sms-plugin)
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
### Setup
|
|
756
|
+
- Configure this plugin by setting the sms option:
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
```
|
|
760
|
+
import { FlinkApp } from "@flink-app/flink";
|
|
761
|
+
import { Ctx } from "./Ctx";
|
|
762
|
+
|
|
763
|
+
import { getJtwTokenPlugin, genericAuthPlugin } from "@flink-app/generic-auth-plugin"
|
|
764
|
+
|
|
765
|
+
const authPlugin = getJtwTokenPlugin("secret");
|
|
766
|
+
|
|
767
|
+
function start() {
|
|
768
|
+
var app = new FlinkApp<Ctx>({
|
|
769
|
+
name: "My flink app",
|
|
770
|
+
debug: true,
|
|
771
|
+
auth : authPlugin,
|
|
772
|
+
db: {
|
|
773
|
+
uri: "mongodb://localhost:27017/my-flink-app",
|
|
774
|
+
},
|
|
775
|
+
plugins: [
|
|
776
|
+
genericAuthPlugin({
|
|
777
|
+
...
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
sms : {
|
|
781
|
+
smsClient: new sms46elksClient({
|
|
782
|
+
username: "XXX",
|
|
783
|
+
password: "YYY",
|
|
784
|
+
}),
|
|
785
|
+
smsFrom: "AUTHMSG",
|
|
786
|
+
smsMessage: "Your code is {{code}}",
|
|
787
|
+
jwtToken: "secret-to-sign-jwt-tokens",
|
|
788
|
+
codeType: "numeric",
|
|
789
|
+
codeLength: 6
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
...
|
|
794
|
+
|
|
795
|
+
}
|
|
796
|
+
}),
|
|
797
|
+
],
|
|
798
|
+
})
|
|
799
|
+
app.start();
|
|
800
|
+
}
|
|
801
|
+
start();
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
### Register users with SMS-login
|
|
807
|
+
To use SMS-login on a user, the user must be created with the `authentificationMethod` option set to sms.
|
|
808
|
+
Username also have to be the users phone number in the "+4671234567" format.
|
|
809
|
+
|
|
810
|
+
### POST /user/create
|
|
811
|
+
|
|
812
|
+
Create a user that can login via SMS
|
|
813
|
+
|
|
814
|
+
#### Request data:
|
|
815
|
+
|
|
816
|
+
```
|
|
817
|
+
{
|
|
818
|
+
"username" : "+4671234567",
|
|
819
|
+
"authentificationMethod" : "sms"
|
|
820
|
+
}
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
#### Response example
|
|
824
|
+
|
|
825
|
+
```
|
|
826
|
+
{
|
|
827
|
+
"data": {
|
|
828
|
+
"status": "success",
|
|
829
|
+
},
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
### Initiate login
|
|
835
|
+
Initiate a user login by sending a SMS with the code to the user.
|
|
836
|
+
Please note that the user HAVE to be created with the `authentificationMethod` option set to sms.
|
|
837
|
+
|
|
838
|
+
### POST /user/login
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
#### Request data:
|
|
842
|
+
|
|
843
|
+
```
|
|
844
|
+
{
|
|
845
|
+
"username" : "+4671234567",
|
|
846
|
+
}
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
#### Response example
|
|
850
|
+
|
|
851
|
+
```
|
|
852
|
+
{
|
|
853
|
+
"data": {
|
|
854
|
+
"status": "success",
|
|
855
|
+
"validationToken": "TOKEN"
|
|
856
|
+
},
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
### Login
|
|
862
|
+
Finalize the login by sending the token received above, and the code received via SMS.
|
|
863
|
+
|
|
864
|
+
### POST /user/login-by-token
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
#### Request data:
|
|
868
|
+
|
|
869
|
+
```
|
|
870
|
+
{
|
|
871
|
+
"token" : "TOKEN",
|
|
872
|
+
"code" : "code"
|
|
873
|
+
}
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
#### Response example
|
|
877
|
+
|
|
878
|
+
```
|
|
879
|
+
{
|
|
880
|
+
"data": {
|
|
881
|
+
"status": "success",
|
|
882
|
+
"user": {
|
|
883
|
+
"_id": "1234...",
|
|
884
|
+
"username": "+4671234567",
|
|
885
|
+
"token": "Token",
|
|
886
|
+
"profile": {}
|
|
887
|
+
}
|
|
888
|
+
},
|
|
889
|
+
"status": 200
|
|
890
|
+
}
|
|
891
|
+
```
|
package/src/coreFunctions.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { UserPasswordResetStartRes } from "./schemas/UserPasswordResetStartRes";
|
|
|
10
10
|
import { UserPasswordResetCompleteRes } from "./schemas/UserPasswordResetCompleteRes";
|
|
11
11
|
|
|
12
12
|
import jsonwebtoken from "jsonwebtoken";
|
|
13
|
+
import { GenericAuthsmsOptions } from "./genericAuthPluginOptions";
|
|
13
14
|
|
|
14
15
|
export function getJtwTokenPlugin(secret: string, rolePermissions?: { [role: string]: string[] }, passwordPolicy?: RegExp) {
|
|
15
16
|
if (passwordPolicy == undefined) {
|
|
@@ -87,6 +88,12 @@ export async function createUser(
|
|
|
87
88
|
|
|
88
89
|
const token = await auth.createToken({ username: username.toLowerCase(), _id: user._id }, roles);
|
|
89
90
|
|
|
91
|
+
if (user.authentificationMethod == "sms") {
|
|
92
|
+
return {
|
|
93
|
+
status: "success",
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
90
97
|
return {
|
|
91
98
|
status: "success",
|
|
92
99
|
user: {
|
|
@@ -97,6 +104,51 @@ export async function createUser(
|
|
|
97
104
|
};
|
|
98
105
|
}
|
|
99
106
|
|
|
107
|
+
export async function loginByToken(
|
|
108
|
+
repo: FlinkRepo<any, User>,
|
|
109
|
+
auth: JwtAuthPlugin,
|
|
110
|
+
token : string,
|
|
111
|
+
code : string,
|
|
112
|
+
jwtSecret : string
|
|
113
|
+
|
|
114
|
+
): Promise<UserLoginRes> {
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
let payload : { type : string, userId : string};
|
|
118
|
+
try{
|
|
119
|
+
payload = jsonwebtoken.verify(token, jwtSecret + ":" + code) as { type : string, userId : string};
|
|
120
|
+
}catch(ex){
|
|
121
|
+
return { status: "failed" };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
if(payload.type != "smsLogin"){
|
|
126
|
+
return { status: "failed" };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
const user = await repo.getById(payload.userId)
|
|
132
|
+
if (user == null) {
|
|
133
|
+
return { status: "failed" };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
const authToken = await auth.createToken({ username: user.username.toLowerCase(), _id: user._id }, user.roles);
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
status: "success",
|
|
141
|
+
user: {
|
|
142
|
+
_id: user._id,
|
|
143
|
+
username: user.username,
|
|
144
|
+
token : authToken,
|
|
145
|
+
profile: user.profile,
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
100
152
|
export async function loginUser(
|
|
101
153
|
repo: FlinkRepo<any, User>,
|
|
102
154
|
auth: JwtAuthPlugin,
|
|
@@ -104,7 +156,8 @@ export async function loginUser(
|
|
|
104
156
|
password: string | undefined,
|
|
105
157
|
validatePasswordMethod?: {
|
|
106
158
|
(password: string, hash: string, salt: string): Promise<boolean>;
|
|
107
|
-
}
|
|
159
|
+
},
|
|
160
|
+
smsOptions? : GenericAuthsmsOptions
|
|
108
161
|
): Promise<UserLoginRes> {
|
|
109
162
|
const user = await repo.getOne({ username: username.toLowerCase() });
|
|
110
163
|
if (user == null) {
|
|
@@ -130,8 +183,35 @@ export async function loginUser(
|
|
|
130
183
|
}
|
|
131
184
|
}
|
|
132
185
|
if (user.authentificationMethod == "sms") {
|
|
133
|
-
|
|
134
|
-
|
|
186
|
+
if(!smsOptions) throw "SMS options must be specified to use SMS login"
|
|
187
|
+
let code = smsOptions.codeType == "numeric" ? generate(smsOptions.codeLength) : generateString(smsOptions.codeLength);
|
|
188
|
+
smsOptions.smsClient.send({
|
|
189
|
+
to : [user.username],
|
|
190
|
+
from : smsOptions.smsFrom,
|
|
191
|
+
message : smsOptions.smsMessage.replace("{{code}}", code)
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
const payload = {
|
|
195
|
+
type: "smsLogin",
|
|
196
|
+
userId: user._id,
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const secret = smsOptions.jwtToken + ":" + code;
|
|
200
|
+
|
|
201
|
+
const options: jsonwebtoken.SignOptions = {
|
|
202
|
+
expiresIn: "1h",
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const token = jsonwebtoken.sign(payload, secret, options);
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
status: "success",
|
|
210
|
+
validationToken : token
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
|
|
135
215
|
}
|
|
136
216
|
|
|
137
217
|
if (valid) {
|
|
@@ -297,3 +377,17 @@ function generate(n: number): string {
|
|
|
297
377
|
|
|
298
378
|
return ("" + number).substring(add);
|
|
299
379
|
}
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
function generateString(length : number) {
|
|
385
|
+
const characters ='ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
386
|
+
let result = ' ';
|
|
387
|
+
const charactersLength = characters.length;
|
|
388
|
+
for ( let i = 0; i < length; i++ ) {
|
|
389
|
+
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return result;
|
|
393
|
+
}
|
|
@@ -8,12 +8,14 @@ import { UserPasswordChangeRes } from "./schemas/UserPasswordChangeRes";
|
|
|
8
8
|
import { UserPasswordResetSettings} from "./schemas/UserPasswordResetSettings"
|
|
9
9
|
import { UserPasswordResetStartRes } from "./schemas/UserPasswordResetStartRes"
|
|
10
10
|
import { UserPasswordResetCompleteRes } from "./schemas/UserPasswordResetCompleteRes"
|
|
11
|
+
import { GenericAuthsmsOptions } from "./genericAuthPluginOptions";
|
|
11
12
|
|
|
12
13
|
export interface genericAuthContext{
|
|
13
14
|
genericAuthPlugin : {
|
|
14
15
|
|
|
15
16
|
|
|
16
|
-
loginUser( repo : FlinkRepo<any, User>, auth : JwtAuthPlugin, username : string, password? : string, validatePasswordMethod? : { (password : string, hash : string, salt : string) : Promise<boolean> } ) : Promise<UserLoginRes>,
|
|
17
|
+
loginUser( repo : FlinkRepo<any, User>, auth : JwtAuthPlugin, username : string, password? : string, validatePasswordMethod? : { (password : string, hash : string, salt : string) : Promise<boolean> }, smsOptions? : GenericAuthsmsOptions ) : Promise<UserLoginRes>,
|
|
18
|
+
loginByToken(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, token : string, code : string, jwtSecret : string) : Promise<UserLoginRes>,
|
|
17
19
|
createUser( repo : FlinkRepo<any, User>, auth : JwtAuthPlugin, username : string, password : string, authentificationMethod : "password" | "sms", roles : string[], profile : UserProfile, createPasswordHashAndSaltMethod? : { (password : string) : Promise<{ hash: string; salt: string;} | null> } ) : Promise<UserCreateRes>,
|
|
18
20
|
changePassword( repo : FlinkRepo<any, User>, auth : JwtAuthPlugin, userId : string, newPassword : string, createPasswordHashAndSaltMethod? : { (password : string) : Promise<{ hash: string; salt: string;} | null> } ) : Promise<UserPasswordChangeRes>,
|
|
19
21
|
passwordResetStart( repo : FlinkRepo<any, User>, auth : JwtAuthPlugin, jwtSecret : string, username : string, numberOfDigits? : number, lifeTime? : string) : Promise<UserPasswordResetStartRes>,
|
|
@@ -23,6 +25,7 @@ export interface genericAuthContext{
|
|
|
23
25
|
createPasswordHashAndSaltMethod? : { (password : string) : Promise<{ hash: string; salt: string;} | null> },
|
|
24
26
|
validatePasswordMethod? : { (password : string, hash : string, salt : string) : Promise<boolean> },
|
|
25
27
|
usernameFormat : RegExp
|
|
28
|
+
smsOptions? : GenericAuthsmsOptions
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
}
|
|
@@ -1,21 +1,32 @@
|
|
|
1
1
|
import { UserPasswordResetSettings } from "./schemas/UserPasswordResetSettings";
|
|
2
|
-
|
|
2
|
+
import { client as smsClient } from "@flink-app/sms-plugin";
|
|
3
3
|
export interface GenericAuthPluginOptions {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
4
|
+
repoName: string;
|
|
5
|
+
enableRoutes?: boolean;
|
|
6
|
+
enablePasswordReset?: boolean;
|
|
7
|
+
enablePushNotificationTokens?: boolean;
|
|
8
|
+
passwordResetSettings?: UserPasswordResetSettings;
|
|
9
|
+
enableUserCreation?: boolean;
|
|
10
|
+
enableProfileUpdate?: boolean;
|
|
11
|
+
enablePasswordUpdate?: boolean;
|
|
12
|
+
baseUrl?: string;
|
|
13
|
+
pluginId?: string;
|
|
14
|
+
createPasswordHashAndSaltMethod?: {
|
|
15
|
+
(password: string): Promise<{ hash: string; salt: string } | null>;
|
|
16
|
+
};
|
|
17
|
+
validatePasswordMethod?: {
|
|
18
|
+
(password: string, hash: string, salt: string): Promise<boolean>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
usernameFormat?: RegExp;
|
|
22
|
+
sms?: GenericAuthsmsOptions;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface GenericAuthsmsOptions {
|
|
26
|
+
smsClient: smsClient;
|
|
27
|
+
smsFrom: string;
|
|
28
|
+
smsMessage: string;
|
|
29
|
+
jwtToken: string;
|
|
30
|
+
codeType: "numeric" | "alphanumeric";
|
|
31
|
+
codeLength: number;
|
|
21
32
|
}
|
|
@@ -11,12 +11,14 @@ const userLoginHandler: Handler<
|
|
|
11
11
|
> = async ({ ctx, req, origin }) => {
|
|
12
12
|
let pluginName = origin || "genericAuthPlugin";
|
|
13
13
|
let repo = ctx.repos[(<any>ctx.plugins)[pluginName].repoName];
|
|
14
|
+
|
|
14
15
|
const loginRespons = await ctx.plugins.genericAuthPlugin.loginUser(
|
|
15
16
|
repo,
|
|
16
17
|
<JwtAuthPlugin>ctx.auth,
|
|
17
18
|
req.body.username,
|
|
18
19
|
req.body.password,
|
|
19
|
-
ctx.plugins.genericAuthPlugin.validatePasswordMethod
|
|
20
|
+
ctx.plugins.genericAuthPlugin.validatePasswordMethod,
|
|
21
|
+
(<any>ctx.plugins)[pluginName].smsOptions
|
|
20
22
|
);
|
|
21
23
|
|
|
22
24
|
if (loginRespons.status != "success") {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { FlinkContext, Handler, unauthorized } from "@flink-app/flink";
|
|
2
|
+
import { JwtAuthPlugin } from "@flink-app/jwt-auth-plugin";
|
|
3
|
+
import { genericAuthContext } from "../genericAuthContext";
|
|
4
|
+
import { UserLoginByTokenReq } from "../schemas/UserLoginByTokenReq";
|
|
5
|
+
import { UserLoginRes } from "../schemas/UserLoginRes";
|
|
6
|
+
|
|
7
|
+
const userLoginHandler: Handler<
|
|
8
|
+
FlinkContext<genericAuthContext>,
|
|
9
|
+
UserLoginByTokenReq,
|
|
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.loginByToken(
|
|
16
|
+
repo,
|
|
17
|
+
<JwtAuthPlugin>ctx.auth,
|
|
18
|
+
req.body.token,
|
|
19
|
+
req.body.code,
|
|
20
|
+
(<any>ctx.plugins)[pluginName].smsOptions.jwtToken
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
if (loginRespons.status != "success") {
|
|
24
|
+
switch (loginRespons.status) {
|
|
25
|
+
case "failed":
|
|
26
|
+
return unauthorized(
|
|
27
|
+
"Invalid token or code",
|
|
28
|
+
loginRespons.status
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
data: loginRespons,
|
|
35
|
+
status: 200,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default userLoginHandler;
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
changePassword,
|
|
6
6
|
passwordResetComplete,
|
|
7
7
|
passwordResetStart,
|
|
8
|
+
loginByToken
|
|
8
9
|
} from "./coreFunctions";
|
|
9
10
|
import { init } from "./init";
|
|
10
11
|
import { GenericAuthPluginOptions } from "./genericAuthPluginOptions";
|
|
@@ -24,6 +25,7 @@ export const genericAuthPlugin = (
|
|
|
24
25
|
init: (app) => init(app, options),
|
|
25
26
|
ctx: {
|
|
26
27
|
loginUser,
|
|
28
|
+
loginByToken,
|
|
27
29
|
createUser,
|
|
28
30
|
changePassword,
|
|
29
31
|
passwordResetStart,
|
|
@@ -33,6 +35,7 @@ export const genericAuthPlugin = (
|
|
|
33
35
|
createPasswordHashAndSaltMethod: options.createPasswordHashAndSaltMethod,
|
|
34
36
|
validatePasswordMethod: options.validatePasswordMethod,
|
|
35
37
|
usernameFormat: options.usernameFormat || /.{1,}$/,
|
|
38
|
+
smsOptions : options.sms
|
|
36
39
|
},
|
|
37
40
|
};
|
|
38
41
|
};
|
package/src/init.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FlinkApp, HttpMethod } from "@flink-app/flink";
|
|
2
2
|
import * as userLoginHandler from "./handlers/UserLogin";
|
|
3
|
+
import * as userLoginByTokenHandler from "./handlers/UserLoginByToken";
|
|
3
4
|
import * as userCreateHandler from "./handlers/UserCreate";
|
|
4
5
|
import * as getProfileHandler from "./handlers/UserProfileGet";
|
|
5
6
|
import * as putUserProfileHandler from "./handlers/UserProfilePut";
|
|
@@ -25,6 +26,14 @@ export function init(app: FlinkApp<any>, options: GenericAuthPluginOptions) {
|
|
|
25
26
|
docs: "Authenticates a user",
|
|
26
27
|
origin: options.pluginId,
|
|
27
28
|
});
|
|
29
|
+
if(options.sms){
|
|
30
|
+
app.addHandler(userLoginByTokenHandler, {
|
|
31
|
+
method: HttpMethod.post,
|
|
32
|
+
path: options.baseUrl + "/login-by-token",
|
|
33
|
+
docs: "Authenticates a user by token",
|
|
34
|
+
origin: options.pluginId,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
28
37
|
if (options.enableUserCreation) {
|
|
29
38
|
app.addHandler(userCreateHandler, {
|
|
30
39
|
method: HttpMethod.post,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export interface UserCreateRes {
|
|
2
|
-
status
|
|
3
|
-
user
|
|
4
|
-
_id
|
|
5
|
-
username
|
|
6
|
-
token
|
|
7
|
-
}
|
|
8
|
-
}
|
|
2
|
+
status: "success" | "error" | "userExists" | "passwordError";
|
|
3
|
+
user?: {
|
|
4
|
+
_id: string;
|
|
5
|
+
username: string;
|
|
6
|
+
token: string;
|
|
7
|
+
};
|
|
8
|
+
}
|