firstly 0.0.10 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/esm/BaseEnum.d.ts +2 -0
- package/esm/BaseEnum.js +2 -0
- package/esm/FF_Fields.js +0 -1
- package/esm/ROUTES.d.ts +2 -2
- package/esm/ROUTES.js +10 -5
- package/esm/SqlDatabase/FF_LogToConsole.d.ts +1 -0
- package/esm/SqlDatabase/FF_LogToConsole.js +22 -16
- package/esm/api/index.d.ts +19 -21
- package/esm/api/index.js +72 -62
- package/esm/auth/{client/Auth.d.ts → AuthController.d.ts} +18 -25
- package/esm/auth/{client/Auth.js → AuthController.js} +48 -44
- package/esm/auth/{client/Entities.d.ts → Entities.d.ts} +4 -3
- package/esm/auth/{client/Entities.js → Entities.js} +7 -7
- package/esm/auth/README.md +0 -10
- package/esm/auth/index.d.ts +5 -149
- package/esm/auth/index.js +5 -316
- package/esm/auth/{AuthController.server.d.ts → server/AuthController.server.d.ts} +10 -10
- package/esm/auth/{AuthController.server.js → server/AuthController.server.js} +126 -164
- package/esm/auth/server/handleAuth.d.ts +2 -0
- package/esm/auth/server/handleAuth.js +140 -0
- package/esm/auth/server/helperDb.d.ts +10 -0
- package/esm/auth/server/helperDb.js +56 -0
- package/esm/auth/server/helperFirstly.d.ts +1 -0
- package/esm/auth/server/helperFirstly.js +8 -0
- package/esm/auth/server/helperOslo.d.ts +7 -0
- package/esm/auth/server/helperOslo.js +24 -0
- package/esm/auth/server/helperRemultServer.d.ts +5 -0
- package/esm/auth/server/helperRemultServer.js +44 -0
- package/esm/auth/{RoleHelpers.d.ts → server/helperRole.d.ts} +1 -1
- package/esm/auth/{RoleHelpers.js → server/helperRole.js} +1 -1
- package/esm/auth/server/index.d.ts +5 -0
- package/esm/auth/server/index.js +5 -0
- package/esm/auth/server/module.d.ts +238 -0
- package/esm/auth/server/module.js +184 -0
- package/esm/auth/{providers → server/providers}/github.d.ts +6 -5
- package/esm/auth/{providers → server/providers}/github.js +30 -21
- package/esm/auth/{providers/index.d.ts → server/providers/helperProvider.d.ts} +0 -2
- package/esm/auth/{providers/index.js → server/providers/helperProvider.js} +5 -6
- package/esm/auth/static/assets/{Page-BEFYPjis.d.ts → Page-Bb8bFlrP.d.ts} +1 -1
- package/esm/auth/static/assets/{Page-DtgkOCJs.js → Page-Bb8bFlrP.js} +1 -1
- package/esm/auth/static/assets/{Page-DtgkOCJs.d.ts → Page-BxomFlZ8.d.ts} +1 -1
- package/esm/auth/static/assets/{Page-BEFYPjis.js → Page-BxomFlZ8.js} +1 -1
- package/esm/auth/static/assets/Page-CaIYu0-y.d.ts +6 -0
- package/esm/auth/static/assets/Page-CaIYu0-y.js +19 -0
- package/esm/auth/static/assets/Page-MkYglNtu.css +1 -0
- package/esm/auth/static/assets/index-Bl0Bk5u0.d.ts +64 -0
- package/esm/auth/static/assets/index-Bl0Bk5u0.js +2 -0
- package/esm/auth/static/assets/{index-CR_3yNaJ.css → index-R27C_TlP.css} +1 -1
- package/esm/auth/static/index.html +2 -2
- package/esm/auth/types.d.ts +5 -0
- package/esm/bin/cmd.js +13 -22
- package/esm/cellsBuildor.js +6 -6
- package/esm/changeLog/index.d.ts +0 -36
- package/esm/changeLog/index.js +3 -43
- package/esm/changeLog/server/index.d.ts +36 -0
- package/esm/changeLog/server/index.js +42 -0
- package/esm/cron/{index.d.ts → server/index.d.ts} +1 -1
- package/esm/cron/server/index.js +103 -0
- package/esm/feedback/FeedbackController.js +3 -3
- package/esm/feedback/index.d.ts +0 -16
- package/esm/feedback/index.js +0 -11
- package/esm/feedback/server/index.d.ts +17 -0
- package/esm/feedback/server/index.js +13 -0
- package/esm/feedback/ui/DialogIssue.svelte +4 -2
- package/esm/feedback/ui/DialogIssues.svelte +13 -4
- package/esm/feedback/ui/DialogMilestones.svelte +1 -1
- package/esm/feedback/ui/Feedback.svelte +3 -1
- package/esm/helper.d.ts +0 -1
- package/esm/helper.js +3 -17
- package/esm/index.d.ts +3 -17
- package/esm/index.js +3 -4
- package/esm/mail/index.d.ts +2 -30
- package/esm/mail/index.js +2 -79
- package/esm/mail/server/index.d.ts +31 -0
- package/esm/mail/server/index.js +88 -0
- package/esm/storeItem.d.ts +4 -1
- package/esm/storeItem.js +8 -2
- package/esm/storeList.d.ts +5 -2
- package/esm/storeList.js +1 -1
- package/esm/sveltekit/server/index.d.ts +11 -0
- package/esm/sveltekit/server/index.js +21 -0
- package/esm/ui/Button.svelte +1 -1
- package/esm/ui/Clipboardable.svelte +5 -2
- package/esm/ui/Field.svelte +4 -1
- package/esm/ui/Loading.svelte +4 -1
- package/esm/ui/Tooltip.svelte +2 -2
- package/esm/ui/dialog/DialogForm.svelte +1 -1
- package/esm/ui/dialog/DialogPrimitive.svelte +1 -2
- package/esm/ui/dialog/dialog.d.ts +6 -3
- package/esm/ui/dialog/dialog.js +1 -1
- package/esm/ui/internals/FieldContainer.svelte +0 -1
- package/esm/ui/internals/Input.svelte +3 -1
- package/esm/ui/internals/Textarea.svelte +2 -2
- package/esm/vite/index.js +24 -25
- package/package.json +50 -38
- package/esm/auth/Adapter.d.ts +0 -10
- package/esm/auth/Adapter.js +0 -50
- package/esm/auth/client/index.d.ts +0 -7
- package/esm/auth/client/index.js +0 -7
- package/esm/auth/helper.d.ts +0 -6
- package/esm/auth/helper.js +0 -14
- package/esm/auth/providers/strava.d.ts +0 -30
- package/esm/auth/providers/strava.js +0 -60
- package/esm/auth/static/assets/Page-BGTO8LC5.css +0 -1
- package/esm/auth/static/assets/Page-Cfysx_UV.d.ts +0 -6
- package/esm/auth/static/assets/Page-Cfysx_UV.js +0 -18
- package/esm/auth/static/assets/index-QypqCYwC.d.ts +0 -63
- package/esm/auth/static/assets/index-QypqCYwC.js +0 -2
- package/esm/cron/index.js +0 -102
- package/esm/handle/index.d.ts +0 -7
- package/esm/handle/index.js +0 -40
|
@@ -1,49 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { remult } from 'remult';
|
|
1
|
+
import { decodeHex, encodeHexLowerCase } from '@oslojs/encoding';
|
|
2
|
+
import { createTOTPKeyURI, generateTOTP, verifyTOTPWithGracePeriod } from '@oslojs/otp';
|
|
3
|
+
import { generateState } from 'arctic';
|
|
4
|
+
import { EntityError, remult, repo } from 'remult';
|
|
6
5
|
import { green, magenta, yellow } from '@kitql/helpers';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return new Argon2id({
|
|
16
|
-
...AUTH_OPTIONS.providers?.password?.argon2Settings,
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
async function passwordVerify(hash, password) {
|
|
20
|
-
const argon = await getArgon();
|
|
21
|
-
return await argon.verify(hash, password);
|
|
22
|
-
}
|
|
23
|
-
async function passwordHash(password) {
|
|
24
|
-
const argon = await getArgon();
|
|
25
|
-
return await argon.hash(password);
|
|
26
|
-
}
|
|
27
|
-
function checkPassword(password) {
|
|
28
|
-
if (typeof password !== 'string' || password.length < 6 || password.length > 255) {
|
|
29
|
-
throw Error('Invalid password');
|
|
30
|
-
}
|
|
31
|
-
}
|
|
6
|
+
import { sendMail } from '../../mail/server/index.js';
|
|
7
|
+
import { FFAuthProvider } from '../Entities.js';
|
|
8
|
+
import { invalidateSession } from './helperDb.js';
|
|
9
|
+
import { ff_createSession } from './helperFirstly.js';
|
|
10
|
+
import { createDate, generateAndEncodeToken } from './helperOslo.js';
|
|
11
|
+
import { deleteSessionTokenCookie, setOAuthStateCookie, setRedirectCookie, } from './helperRemultServer.js';
|
|
12
|
+
import { mergeRoles } from './helperRole.js';
|
|
13
|
+
import { AUTH_OPTIONS, authModuleRaw, getSafeOptions } from './module.js';
|
|
32
14
|
export class AuthControllerServer {
|
|
33
15
|
/**
|
|
34
16
|
* Sign out the current user
|
|
35
17
|
*/
|
|
36
18
|
static async signOut() {
|
|
37
19
|
if (remult.user?.session.id) {
|
|
38
|
-
await
|
|
20
|
+
await invalidateSession(remult.user?.session.id);
|
|
39
21
|
}
|
|
40
|
-
|
|
41
|
-
// remult.context.deleteCookie(lucia.sessionCookieName, { path: '/' })
|
|
42
|
-
const sessionCookie = lucia.createBlankSessionCookie();
|
|
43
|
-
remult.context.setCookie(sessionCookie.name, sessionCookie.value, {
|
|
44
|
-
path: '/',
|
|
45
|
-
...sessionCookie.attributes,
|
|
46
|
-
});
|
|
22
|
+
deleteSessionTokenCookie();
|
|
47
23
|
}
|
|
48
24
|
/**
|
|
49
25
|
* Sign in with a demo account
|
|
@@ -52,58 +28,60 @@ export class AuthControllerServer {
|
|
|
52
28
|
static async signInDemo(name) {
|
|
53
29
|
const accounts = AUTH_OPTIONS.providers?.demo ?? [];
|
|
54
30
|
if (accounts.length === 0) {
|
|
55
|
-
throw new
|
|
31
|
+
throw new EntityError({ message: `Demo accounts are not enabled!` });
|
|
56
32
|
}
|
|
57
33
|
const account = accounts.find((a) => a.name === name);
|
|
58
34
|
if (!account) {
|
|
59
|
-
throw new
|
|
35
|
+
throw new EntityError({ message: `${name} not found as demo account!` });
|
|
60
36
|
}
|
|
61
37
|
const oSafe = getSafeOptions();
|
|
62
|
-
let user = await
|
|
38
|
+
let user = await repo(oSafe.User).findFirst({ identifier: name });
|
|
63
39
|
if (!user) {
|
|
64
|
-
user =
|
|
40
|
+
user = repo(oSafe.User).create();
|
|
65
41
|
}
|
|
66
42
|
user.identifier = name;
|
|
67
43
|
const r = mergeRoles(user.roles, account.roles);
|
|
68
44
|
user.roles = r.roles;
|
|
69
|
-
await
|
|
70
|
-
await
|
|
45
|
+
await repo(oSafe.User).save(user);
|
|
46
|
+
await ff_createSession(user.id);
|
|
71
47
|
return "You're in with demo account!";
|
|
72
48
|
}
|
|
73
49
|
/**
|
|
74
50
|
* This is for login / password authentication invite
|
|
75
51
|
*/
|
|
76
|
-
static async invite(
|
|
52
|
+
static async invite(emailParam) {
|
|
53
|
+
const email = emailParam.toLowerCase();
|
|
77
54
|
const oSafe = getSafeOptions();
|
|
78
|
-
const existingAccount = await
|
|
55
|
+
const existingAccount = await repo(oSafe.Account).findOne({
|
|
79
56
|
where: {
|
|
80
57
|
providerUserId: email,
|
|
81
58
|
provider: FFAuthProvider.PASSWORD.id,
|
|
82
59
|
},
|
|
83
60
|
});
|
|
84
61
|
if (existingAccount) {
|
|
85
|
-
//
|
|
62
|
+
// Already invited, it's all good.
|
|
86
63
|
}
|
|
87
64
|
else {
|
|
88
|
-
const token =
|
|
65
|
+
const token = generateAndEncodeToken();
|
|
89
66
|
// TODO: Do we create the user or just the account ?!
|
|
90
67
|
// TODO 2: Invite is by mail... But the invitee can log with another provider... So what do we do?! maybe not checking the provider... and updating?
|
|
91
|
-
// const user = await
|
|
68
|
+
// const user = await repo(oSafe.User).insert({
|
|
92
69
|
// identifier: email,
|
|
93
70
|
// })
|
|
94
|
-
|
|
71
|
+
oSafe.providers?.password?.verifyMailExpiresIn;
|
|
72
|
+
await repo(oSafe.Account).insert({
|
|
95
73
|
provider: FFAuthProvider.PASSWORD.id,
|
|
96
74
|
providerUserId: email,
|
|
97
75
|
// userId: user.id,
|
|
98
76
|
// hashPassword: await passwordHash(password),
|
|
99
77
|
token: token,
|
|
100
|
-
expiresAt: createDate(
|
|
78
|
+
expiresAt: createDate(AUTH_OPTIONS.providers?.password?.verifyMailExpiresIn ?? 5 * 60),
|
|
101
79
|
lastVerifiedAt: undefined,
|
|
102
80
|
});
|
|
103
|
-
const url = `${remult.context.url.origin}${oSafe.firstlyData.props.ui?.paths.reset_password}?token=${token}`;
|
|
81
|
+
const url = `${remult.context.request.url.origin}${oSafe.firstlyData.props.ui?.paths.reset_password}?token=${token}`;
|
|
104
82
|
if (AUTH_OPTIONS?.invitationSend) {
|
|
105
83
|
await AUTH_OPTIONS?.invitationSend({ email, url });
|
|
106
|
-
|
|
84
|
+
authModuleRaw.log.success(`${green('[custom]')}${magenta('[invitationSend]')} (${yellow(url)})`);
|
|
107
85
|
return 'Mail sent !';
|
|
108
86
|
}
|
|
109
87
|
else {
|
|
@@ -128,7 +106,7 @@ export class AuthControllerServer {
|
|
|
128
106
|
],
|
|
129
107
|
},
|
|
130
108
|
});
|
|
131
|
-
|
|
109
|
+
authModuleRaw.log.success(`${magenta('[invitationSend]')} (${yellow(url)})`);
|
|
132
110
|
return 'Demo Mail sent !';
|
|
133
111
|
}
|
|
134
112
|
}
|
|
@@ -138,54 +116,55 @@ export class AuthControllerServer {
|
|
|
138
116
|
* This is for login / password authentication SignUp
|
|
139
117
|
* _(The first param `email` can be "anything")_
|
|
140
118
|
*/
|
|
141
|
-
static async signUpPassword(
|
|
119
|
+
static async signUpPassword(emailParam, password) {
|
|
120
|
+
const email = emailParam.toLowerCase();
|
|
142
121
|
const oSafe = getSafeOptions();
|
|
143
122
|
if (!oSafe.signUp) {
|
|
144
|
-
throw
|
|
123
|
+
throw new EntityError({ message: "You can't signup by yourself! Contact the administrator." });
|
|
145
124
|
}
|
|
146
|
-
if (!oSafe.
|
|
147
|
-
throw
|
|
125
|
+
if (!oSafe.password.enabled) {
|
|
126
|
+
throw new EntityError({ message: 'Password is not enabled!' });
|
|
148
127
|
}
|
|
149
|
-
const existingAccount = await
|
|
128
|
+
const existingAccount = await repo(oSafe.Account).findOne({
|
|
150
129
|
where: {
|
|
151
130
|
providerUserId: email,
|
|
152
131
|
provider: FFAuthProvider.PASSWORD.id,
|
|
153
132
|
},
|
|
154
133
|
});
|
|
155
134
|
if (existingAccount) {
|
|
156
|
-
throw
|
|
135
|
+
throw new EntityError({ message: "You can't signup twice !" });
|
|
157
136
|
}
|
|
158
|
-
|
|
159
|
-
const token =
|
|
137
|
+
oSafe.password.validatePassword(password);
|
|
138
|
+
const token = generateAndEncodeToken();
|
|
160
139
|
await remult.dataProvider.transaction(async () => {
|
|
161
|
-
const user = await
|
|
140
|
+
const user = await repo(oSafe.User).insert({
|
|
162
141
|
identifier: email,
|
|
163
142
|
});
|
|
164
|
-
await
|
|
143
|
+
await repo(oSafe.Account).insert({
|
|
165
144
|
provider: FFAuthProvider.PASSWORD.id,
|
|
166
145
|
providerUserId: email,
|
|
167
146
|
userId: user.id,
|
|
168
|
-
hashPassword:
|
|
147
|
+
hashPassword: oSafe.password.passwordHash(password),
|
|
169
148
|
token: oSafe.verifiedMethod === 'auto' ? undefined : token,
|
|
170
149
|
expiresAt: oSafe.verifiedMethod === 'auto'
|
|
171
150
|
? undefined
|
|
172
|
-
: createDate(
|
|
151
|
+
: createDate(AUTH_OPTIONS.providers?.password?.verifyMailExpiresIn ?? 5 * 60),
|
|
173
152
|
lastVerifiedAt: oSafe.verifiedMethod === 'auto' ? new Date() : undefined,
|
|
174
153
|
});
|
|
175
154
|
});
|
|
176
155
|
if (oSafe.verifiedMethod === 'auto') {
|
|
177
|
-
const user = await
|
|
156
|
+
const user = await repo(oSafe.User).findFirst({
|
|
178
157
|
identifier: email,
|
|
179
158
|
});
|
|
180
159
|
if (user) {
|
|
181
|
-
await
|
|
160
|
+
await ff_createSession(user.id);
|
|
182
161
|
}
|
|
183
162
|
}
|
|
184
163
|
else {
|
|
185
|
-
const url = `${remult.context.url.origin}${oSafe.firstlyData.props.ui?.paths.verify_email}?token=${token}`;
|
|
164
|
+
const url = `${remult.context.request.url.origin}${oSafe.firstlyData.props.ui?.paths.verify_email}?token=${token}`;
|
|
186
165
|
if (AUTH_OPTIONS.providers?.password?.verifyMailSend) {
|
|
187
166
|
await AUTH_OPTIONS.providers?.password.verifyMailSend({ email, url });
|
|
188
|
-
|
|
167
|
+
authModuleRaw.log.success(`${green('[custom]')}${magenta('[verifyMailSend]')} (${yellow(url)})`);
|
|
189
168
|
}
|
|
190
169
|
else {
|
|
191
170
|
await sendMail('verifyMailSend', {
|
|
@@ -205,7 +184,7 @@ export class AuthControllerServer {
|
|
|
205
184
|
],
|
|
206
185
|
},
|
|
207
186
|
});
|
|
208
|
-
|
|
187
|
+
authModuleRaw.log.success(`${magenta('[verifyMailSend]')} (${yellow(url)})`);
|
|
209
188
|
}
|
|
210
189
|
}
|
|
211
190
|
return 'ok';
|
|
@@ -214,50 +193,54 @@ export class AuthControllerServer {
|
|
|
214
193
|
* This is for login / password authentication SignIn
|
|
215
194
|
* _(The first param `email` can be "anything")_
|
|
216
195
|
*/
|
|
217
|
-
static async signInPassword(
|
|
196
|
+
static async signInPassword(emailParam, password) {
|
|
197
|
+
const email = emailParam.toLowerCase();
|
|
218
198
|
const oSafe = getSafeOptions();
|
|
219
|
-
if (!oSafe.
|
|
220
|
-
throw
|
|
199
|
+
if (!oSafe.password.enabled) {
|
|
200
|
+
throw new EntityError({ message: 'Password is not enabled!' });
|
|
221
201
|
}
|
|
222
|
-
const existingAccount = await
|
|
202
|
+
const existingAccount = await repo(oSafe.Account).findOne({
|
|
223
203
|
where: {
|
|
224
204
|
providerUserId: email,
|
|
225
205
|
provider: FFAuthProvider.PASSWORD.id,
|
|
226
206
|
},
|
|
227
207
|
});
|
|
228
208
|
if (existingAccount) {
|
|
229
|
-
const validPassword =
|
|
209
|
+
const validPassword = oSafe.password.passwordVerify(password ?? '', existingAccount?.hashPassword ?? '');
|
|
230
210
|
if (validPassword) {
|
|
231
|
-
await
|
|
211
|
+
await ff_createSession(existingAccount.userId);
|
|
232
212
|
return 'ok';
|
|
233
213
|
}
|
|
234
|
-
|
|
214
|
+
authModuleRaw.log.error({ email, passwordLength: password.length });
|
|
215
|
+
throw new EntityError({ message: 'Incorrect username or password' });
|
|
235
216
|
}
|
|
236
|
-
|
|
217
|
+
authModuleRaw.log.error({ email, passwordLength: password.length });
|
|
218
|
+
throw new EntityError({ message: 'Incorrect username or password.' });
|
|
237
219
|
}
|
|
238
220
|
/**
|
|
239
221
|
* Forgot your password ? Send a mail to reset it.
|
|
240
222
|
*/
|
|
241
|
-
static async forgotPassword(
|
|
223
|
+
static async forgotPassword(emailParam) {
|
|
224
|
+
const email = emailParam.toLowerCase();
|
|
242
225
|
const oSafe = getSafeOptions();
|
|
243
|
-
if (!oSafe.
|
|
244
|
-
throw
|
|
226
|
+
if (!oSafe.password.enabled) {
|
|
227
|
+
throw new EntityError({ message: 'Password is not enabled!' });
|
|
245
228
|
}
|
|
246
|
-
const existingAccount = await
|
|
229
|
+
const existingAccount = await repo(oSafe.Account).findOne({
|
|
247
230
|
where: {
|
|
248
231
|
providerUserId: email,
|
|
249
232
|
provider: FFAuthProvider.PASSWORD.id,
|
|
250
233
|
},
|
|
251
234
|
});
|
|
252
235
|
if (existingAccount) {
|
|
253
|
-
const token =
|
|
236
|
+
const token = generateAndEncodeToken();
|
|
254
237
|
existingAccount.token = token;
|
|
255
|
-
existingAccount.expiresAt = createDate(
|
|
256
|
-
await
|
|
257
|
-
const url = `${remult.context.url.origin}${oSafe.firstlyData.props.ui?.paths.reset_password}?token=${token}`;
|
|
238
|
+
existingAccount.expiresAt = createDate(AUTH_OPTIONS.providers?.password?.resetPasswordExpiresIn ?? 5 * 60);
|
|
239
|
+
await repo(oSafe.Account).save(existingAccount);
|
|
240
|
+
const url = `${remult.context.request.url.origin}${oSafe.firstlyData.props.ui?.paths.reset_password}?token=${token}`;
|
|
258
241
|
if (AUTH_OPTIONS.providers?.password?.resetPasswordSend) {
|
|
259
242
|
await AUTH_OPTIONS.providers?.password.resetPasswordSend({ email, url });
|
|
260
|
-
|
|
243
|
+
authModuleRaw.log.success(`${green('[custom]')}${magenta('[resetPasswordSend]')} (${yellow(url)})`);
|
|
261
244
|
return 'Mail sent !';
|
|
262
245
|
}
|
|
263
246
|
else {
|
|
@@ -282,119 +265,120 @@ export class AuthControllerServer {
|
|
|
282
265
|
],
|
|
283
266
|
},
|
|
284
267
|
});
|
|
285
|
-
|
|
268
|
+
authModuleRaw.log.success(`${magenta('[resetPasswordSend]')} (${yellow(url)})`);
|
|
286
269
|
return 'Demo Mail sent !';
|
|
287
270
|
}
|
|
288
271
|
}
|
|
289
|
-
throw new
|
|
272
|
+
throw new EntityError({ message: "Une erreur est survenue, contacte l'administrateur!" });
|
|
290
273
|
}
|
|
291
274
|
/**
|
|
292
275
|
* Reset your password with a token
|
|
293
276
|
*/
|
|
294
277
|
static async resetPassword(token, password) {
|
|
295
278
|
const oSafe = getSafeOptions();
|
|
296
|
-
if (!oSafe.
|
|
297
|
-
throw
|
|
279
|
+
if (!oSafe.password.enabled) {
|
|
280
|
+
throw new EntityError({ message: 'Password is not enabled!' });
|
|
298
281
|
}
|
|
299
282
|
const account = await remult
|
|
300
283
|
.repo(oSafe.Account)
|
|
301
284
|
.findFirst({ token, provider: FFAuthProvider.PASSWORD.id });
|
|
302
285
|
if (!account) {
|
|
303
|
-
throw new
|
|
286
|
+
throw new EntityError({ message: 'Invalid token' });
|
|
304
287
|
}
|
|
305
288
|
if (account.expiresAt && account.expiresAt < new Date()) {
|
|
306
|
-
throw new
|
|
289
|
+
throw new EntityError({ message: 'token expired' });
|
|
307
290
|
}
|
|
308
|
-
|
|
291
|
+
oSafe.password.validatePassword(password);
|
|
309
292
|
if (account.userId === undefined) {
|
|
310
|
-
const user = await
|
|
293
|
+
const user = await repo(oSafe.User).insert({ identifier: account.providerUserId });
|
|
311
294
|
account.userId = user.id;
|
|
312
295
|
}
|
|
313
|
-
await
|
|
296
|
+
await invalidateSession(account.userId);
|
|
314
297
|
// update elements
|
|
315
|
-
account.hashPassword =
|
|
298
|
+
account.hashPassword = oSafe.password.passwordHash(password);
|
|
316
299
|
account.token = undefined;
|
|
317
300
|
account.expiresAt = undefined;
|
|
318
301
|
account.lastVerifiedAt = new Date();
|
|
319
|
-
await
|
|
320
|
-
await
|
|
302
|
+
await repo(oSafe.Account).save(account);
|
|
303
|
+
await ff_createSession(account.userId);
|
|
321
304
|
return 'reseted';
|
|
322
305
|
}
|
|
323
306
|
/** OTP */
|
|
324
|
-
static async signInOTP(
|
|
307
|
+
static async signInOTP(emailParam) {
|
|
308
|
+
const email = emailParam.toLowerCase();
|
|
325
309
|
const oSafe = getSafeOptions();
|
|
326
|
-
if (!oSafe.
|
|
327
|
-
throw new
|
|
310
|
+
if (!oSafe.otp.enabled) {
|
|
311
|
+
throw new EntityError({ message: `OPT is not enabled!` });
|
|
328
312
|
}
|
|
329
313
|
if (AUTH_OPTIONS.providers?.otp?.send) {
|
|
330
|
-
const
|
|
331
|
-
const
|
|
332
|
-
const
|
|
333
|
-
const
|
|
334
|
-
const
|
|
335
|
-
period: new TimeSpan(AUTH_OPTIONS.providers?.otp.expiresIn ?? 30, 's'),
|
|
336
|
-
digits: AUTH_OPTIONS.providers?.otp.digits ?? 6,
|
|
337
|
-
}).generate(secret);
|
|
338
|
-
const secretEncoded = encodeHex(secret);
|
|
314
|
+
const key = crypto.getRandomValues(new Uint8Array(20));
|
|
315
|
+
const intervalInSeconds = AUTH_OPTIONS.providers?.otp?.expiresIn ?? 30;
|
|
316
|
+
const digits = AUTH_OPTIONS.providers?.otp.digits ?? 6;
|
|
317
|
+
const otp = generateTOTP(key, intervalInSeconds, digits);
|
|
318
|
+
const keyEncoded = encodeHexLowerCase(key);
|
|
339
319
|
const issuer = AUTH_OPTIONS.providers.otp.issuer ?? 'firstly';
|
|
340
|
-
const uri = createTOTPKeyURI(issuer, email,
|
|
320
|
+
const uri = createTOTPKeyURI(issuer, email, key, intervalInSeconds, digits);
|
|
341
321
|
const oSafe = getSafeOptions();
|
|
342
|
-
let user = await
|
|
322
|
+
let user = await repo(oSafe.User).findFirst({ identifier: email });
|
|
343
323
|
if (!user) {
|
|
344
|
-
user =
|
|
324
|
+
user = repo(oSafe.User).create();
|
|
345
325
|
}
|
|
346
326
|
user.identifier = email;
|
|
347
|
-
user = await
|
|
327
|
+
user = await repo(oSafe.User).save(user);
|
|
348
328
|
let account = await remult
|
|
349
329
|
.repo(oSafe.Account)
|
|
350
330
|
.findFirst({ userId: user.id, provider: FFAuthProvider.OTP.id });
|
|
351
331
|
if (!account) {
|
|
352
|
-
account =
|
|
332
|
+
account = repo(oSafe.Account).create();
|
|
353
333
|
}
|
|
354
334
|
account.userId = user.id;
|
|
355
335
|
account.provider = FFAuthProvider.OTP.id;
|
|
356
336
|
account.token = otp;
|
|
357
|
-
account.hashPassword =
|
|
358
|
-
await
|
|
337
|
+
account.hashPassword = keyEncoded;
|
|
338
|
+
await repo(oSafe.Account).save(account);
|
|
359
339
|
await AUTH_OPTIONS.providers.otp?.send({ name: email, otp, uri });
|
|
360
|
-
|
|
340
|
+
authModuleRaw.log.success(`name: ${yellow(email)}, otp: ${yellow(otp)}, uri: ${yellow(uri)}`);
|
|
361
341
|
return 'Mail sent !';
|
|
362
342
|
}
|
|
363
343
|
else {
|
|
364
|
-
|
|
344
|
+
authModuleRaw.log.error(`You need to provide a otp.send hook in the auth options!`);
|
|
365
345
|
}
|
|
366
346
|
return 'Hum, something went wrong !';
|
|
367
347
|
}
|
|
368
348
|
/**
|
|
369
349
|
* Verify the OTP code
|
|
370
350
|
*/
|
|
371
|
-
static async verifyOtp(
|
|
351
|
+
static async verifyOtp(emailParam, otp) {
|
|
352
|
+
const email = emailParam.toLowerCase();
|
|
372
353
|
const oSafe = getSafeOptions();
|
|
373
|
-
|
|
354
|
+
if (!oSafe.otp.enabled) {
|
|
355
|
+
throw new EntityError({ message: `OPT is not enabled!` });
|
|
356
|
+
}
|
|
357
|
+
const accounts = await repo(oSafe.Account).find({
|
|
374
358
|
where: { token: String(otp), provider: FFAuthProvider.OTP.id },
|
|
375
359
|
});
|
|
376
360
|
if (accounts.length === 0) {
|
|
377
|
-
throw new
|
|
361
|
+
throw new EntityError({ message: 'Invalid otp' });
|
|
378
362
|
}
|
|
379
363
|
const account = accounts[0];
|
|
380
|
-
const user = await
|
|
364
|
+
const user = await repo(oSafe.User).findId(account.userId);
|
|
381
365
|
if (user?.identifier !== email) {
|
|
382
|
-
throw new
|
|
366
|
+
throw new EntityError({ message: 'Invalid otp.' });
|
|
383
367
|
}
|
|
384
|
-
const
|
|
385
|
-
const
|
|
386
|
-
const
|
|
387
|
-
const validOTP =
|
|
368
|
+
const intervalInSeconds = oSafe.providers?.otp?.expiresIn ?? 30;
|
|
369
|
+
const digits = oSafe.providers?.otp?.digits ?? 6;
|
|
370
|
+
const keyDecoded = decodeHex(account.hashPassword ?? '');
|
|
371
|
+
const validOTP = verifyTOTPWithGracePeriod(keyDecoded, intervalInSeconds, digits, otp, 30);
|
|
388
372
|
if (!validOTP) {
|
|
389
|
-
throw new
|
|
373
|
+
throw new EntityError({ message: 'Invalid otp!' });
|
|
390
374
|
}
|
|
391
|
-
await
|
|
375
|
+
await invalidateSession(account.userId);
|
|
392
376
|
// update elements
|
|
393
377
|
account.hashPassword = undefined;
|
|
394
378
|
account.token = undefined;
|
|
395
379
|
account.expiresAt = undefined;
|
|
396
|
-
await
|
|
397
|
-
await
|
|
380
|
+
await repo(oSafe.Account).save(account);
|
|
381
|
+
await ff_createSession(account.userId);
|
|
398
382
|
return 'verified';
|
|
399
383
|
}
|
|
400
384
|
/** OAUTH */
|
|
@@ -417,17 +401,6 @@ export class AuthControllerServer {
|
|
|
417
401
|
try {
|
|
418
402
|
const arcticProvider = selectedOAuth.getArcticProvider();
|
|
419
403
|
const args = [state];
|
|
420
|
-
if (selectedOAuth.isPKCE) {
|
|
421
|
-
const codeVerifier = generateCodeVerifier();
|
|
422
|
-
args.push(codeVerifier);
|
|
423
|
-
// store code verifier as cookie
|
|
424
|
-
remult.context.setCookie('code_verifier', codeVerifier, {
|
|
425
|
-
secure: true, // set to false in localhost
|
|
426
|
-
path: '/',
|
|
427
|
-
httpOnly: true,
|
|
428
|
-
maxAge: 60 * 10, // 10 min
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
404
|
if (o.options) {
|
|
432
405
|
args.push(o.options);
|
|
433
406
|
}
|
|
@@ -436,35 +409,24 @@ export class AuthControllerServer {
|
|
|
436
409
|
args.push(selectedOAuth.authorizationURLOptions());
|
|
437
410
|
}
|
|
438
411
|
}
|
|
439
|
-
// @ts-ignore
|
|
440
412
|
const url = await arcticProvider.createAuthorizationURL(...args);
|
|
441
413
|
if (!url) {
|
|
442
|
-
throw new
|
|
414
|
+
throw new EntityError({ message: 'No url returned' });
|
|
443
415
|
}
|
|
444
|
-
|
|
445
|
-
path: '/',
|
|
446
|
-
secure: !DEV,
|
|
447
|
-
httpOnly: true,
|
|
448
|
-
maxAge: 60 * 10,
|
|
449
|
-
sameSite: 'lax',
|
|
450
|
-
});
|
|
416
|
+
setOAuthStateCookie(selectedOAuth.name, state);
|
|
451
417
|
if (o.redirect) {
|
|
452
|
-
|
|
453
|
-
path: '/',
|
|
454
|
-
secure: !DEV,
|
|
455
|
-
httpOnly: true,
|
|
456
|
-
maxAge: 60 * 10,
|
|
457
|
-
sameSite: 'lax',
|
|
458
|
-
});
|
|
418
|
+
setRedirectCookie(o.redirect);
|
|
459
419
|
}
|
|
460
420
|
return url.toString();
|
|
461
421
|
}
|
|
462
422
|
catch (error) {
|
|
463
423
|
// display error for the server only
|
|
464
|
-
|
|
465
|
-
throw new
|
|
424
|
+
authModuleRaw.log.error(error);
|
|
425
|
+
throw new EntityError({ message: `${selectedOAuth.name} not well configured!` });
|
|
466
426
|
}
|
|
467
427
|
}
|
|
468
|
-
throw new
|
|
428
|
+
throw new EntityError({
|
|
429
|
+
message: `${o.provider} is not configured! (Module: auth, section: providers.oAuths: [${o.provider}] missing)`,
|
|
430
|
+
});
|
|
469
431
|
}
|
|
470
432
|
}
|