firstly 0.0.11 → 0.0.13
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 +20 -21
- package/esm/api/index.js +74 -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} +133 -171
- package/esm/auth/server/handleAuth.d.ts +2 -0
- package/esm/auth/server/handleAuth.js +140 -0
- package/esm/auth/server/handleGuard.d.ts +16 -0
- package/esm/auth/server/handleGuard.js +67 -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 +7 -0
- package/esm/auth/server/index.js +7 -0
- package/esm/auth/server/module.d.ts +257 -0
- package/esm/auth/server/module.js +197 -0
- package/esm/auth/{providers → server/providers}/github.d.ts +6 -4
- package/esm/auth/{providers → server/providers}/github.js +29 -20
- 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-BUfjaN-D.d.ts +5 -0
- package/esm/auth/static/assets/Page-BUfjaN-D.js +19 -0
- package/esm/auth/static/assets/Page-CJ58H1vl.css +1 -0
- package/esm/auth/static/assets/Page-CaDAqmBS.d.ts +5 -0
- package/esm/auth/static/assets/Page-CaDAqmBS.js +1 -0
- package/esm/auth/static/assets/Page-DhdZddzJ.d.ts +5 -0
- package/esm/auth/static/assets/Page-DhdZddzJ.js +1 -0
- package/esm/auth/static/assets/index-BDy4A_14.css +4 -0
- package/esm/auth/static/assets/index-D-Ztdt2o.d.ts +54 -0
- package/esm/auth/static/assets/index-D-Ztdt2o.js +2 -0
- package/esm/auth/static/index.html +11 -11
- package/esm/auth/types.d.ts +5 -0
- package/esm/bin/cmd.js +122 -54
- package/esm/cellsBuildor.js +7 -7
- 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/common.d.ts +5 -0
- package/esm/common.js +8 -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 +4 -5
- 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 +52 -56
- package/esm/feedback/ui/DialogIssues.svelte +71 -71
- package/esm/feedback/ui/DialogMilestones.svelte +22 -22
- package/esm/helper.js +3 -3
- package/esm/index.d.ts +3 -20
- package/esm/index.js +3 -10
- 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/mail/templates/DefaultMail.svelte +17 -17
- package/esm/storeItem.js +8 -2
- package/esm/storeList.d.ts +1 -1
- package/esm/storeList.js +1 -1
- package/esm/sveltekit/server/index.d.ts +14 -0
- package/esm/sveltekit/server/index.js +24 -0
- package/esm/ui/Button.svelte +33 -33
- package/esm/ui/Button.svelte.d.ts +2 -2
- package/esm/ui/Clipboardable.svelte +11 -8
- package/esm/ui/Clipboardable.svelte.d.ts +4 -4
- package/esm/ui/Field.svelte +142 -149
- package/esm/ui/Field.svelte.d.ts +2 -2
- package/esm/ui/FieldGroup.svelte +38 -38
- package/esm/ui/Grid.svelte +212 -222
- package/esm/ui/GridLoading.svelte +18 -22
- package/esm/ui/GridPaginate.svelte +38 -38
- package/esm/ui/Icon.svelte +50 -49
- package/esm/ui/Icon.svelte.d.ts +18 -18
- package/esm/ui/Loading.svelte +5 -2
- package/esm/ui/Tooltip.svelte +16 -16
- package/esm/ui/dialog/DialogForm.svelte +23 -23
- package/esm/ui/dialog/DialogManagement.svelte +74 -74
- package/esm/ui/dialog/DialogPrimitive.svelte +50 -51
- package/esm/ui/dialog/FormEditAction.svelte +34 -34
- package/esm/ui/dialog/dialog.d.ts +2 -2
- package/esm/ui/dialog/dialog.js +1 -1
- package/esm/ui/internals/FieldContainer.svelte +11 -12
- package/esm/ui/internals/FieldContainer.svelte.d.ts +3 -3
- package/esm/ui/internals/Input.svelte +25 -25
- package/esm/ui/internals/Input.svelte.d.ts +1 -1
- package/esm/ui/internals/Textarea.svelte +21 -21
- package/esm/ui/internals/Textarea.svelte.d.ts +2 -2
- package/esm/ui/internals/select/MultiSelectMelt.svelte +69 -73
- package/esm/ui/internals/select/SelectMelt.svelte +86 -86
- package/esm/ui/internals/select/SelectRadio.svelte +22 -22
- package/esm/ui/link/Link.svelte +14 -14
- package/esm/ui/link/Link.svelte.d.ts +3 -4
- package/esm/ui/link/LinkPlus.svelte +33 -35
- package/esm/vite/index.js +24 -25
- package/package.json +50 -42
- 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-BEFYPjis.d.ts +0 -4
- package/esm/auth/static/assets/Page-BEFYPjis.js +0 -1
- 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/Page-DtgkOCJs.d.ts +0 -4
- package/esm/auth/static/assets/Page-DtgkOCJs.js +0 -1
- package/esm/auth/static/assets/index-CR_3yNaJ.css +0 -4
- 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,59 @@ 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
|
|
71
|
-
return
|
|
45
|
+
await repo(oSafe.User).save(user);
|
|
46
|
+
await ff_createSession(user.id);
|
|
47
|
+
return `You're in with ${name} 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
|
-
await
|
|
71
|
+
await repo(oSafe.Account).insert({
|
|
95
72
|
provider: FFAuthProvider.PASSWORD.id,
|
|
96
73
|
providerUserId: email,
|
|
97
74
|
// userId: user.id,
|
|
98
75
|
// hashPassword: await passwordHash(password),
|
|
99
76
|
token: token,
|
|
100
|
-
expiresAt: createDate(
|
|
77
|
+
expiresAt: createDate(AUTH_OPTIONS.providers?.password?.mail?.verify?.expiresIn ?? 5 * 60),
|
|
101
78
|
lastVerifiedAt: undefined,
|
|
102
79
|
});
|
|
103
|
-
const url = `${remult.context.url.origin}${oSafe.firstlyData.props.ui?.paths.reset_password}?token=${token}`;
|
|
80
|
+
const url = `${remult.context.request.url.origin}${oSafe.firstlyData.props.ui?.paths.reset_password}?token=${token}`;
|
|
104
81
|
if (AUTH_OPTIONS?.invitationSend) {
|
|
105
82
|
await AUTH_OPTIONS?.invitationSend({ email, url });
|
|
106
|
-
|
|
83
|
+
authModuleRaw.log.success(`${green('[custom]')}${magenta('[invitationSend]')} (${yellow(url)})`);
|
|
107
84
|
return 'Mail sent !';
|
|
108
85
|
}
|
|
109
86
|
else {
|
|
@@ -128,7 +105,7 @@ export class AuthControllerServer {
|
|
|
128
105
|
],
|
|
129
106
|
},
|
|
130
107
|
});
|
|
131
|
-
|
|
108
|
+
authModuleRaw.log.success(`${magenta('[invitationSend]')} (${yellow(url)})`);
|
|
132
109
|
return 'Demo Mail sent !';
|
|
133
110
|
}
|
|
134
111
|
}
|
|
@@ -138,54 +115,55 @@ export class AuthControllerServer {
|
|
|
138
115
|
* This is for login / password authentication SignUp
|
|
139
116
|
* _(The first param `email` can be "anything")_
|
|
140
117
|
*/
|
|
141
|
-
static async signUpPassword(
|
|
118
|
+
static async signUpPassword(emailParam, password) {
|
|
142
119
|
const oSafe = getSafeOptions();
|
|
143
120
|
if (!oSafe.signUp) {
|
|
144
|
-
throw
|
|
121
|
+
throw new EntityError({ message: oSafe.strings.cannotSignUp });
|
|
145
122
|
}
|
|
146
|
-
if (!oSafe.
|
|
147
|
-
throw
|
|
123
|
+
if (!oSafe.password.enabled) {
|
|
124
|
+
throw new EntityError({ message: 'Password is not enabled!' });
|
|
148
125
|
}
|
|
149
|
-
const
|
|
126
|
+
const email = emailParam.toLowerCase();
|
|
127
|
+
oSafe.password.validateInput({ identifier: email, password });
|
|
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 = generateId(40);
|
|
137
|
+
const token = generateAndEncodeToken();
|
|
160
138
|
await remult.dataProvider.transaction(async () => {
|
|
161
|
-
const user = await
|
|
139
|
+
const user = await repo(oSafe.User).insert({
|
|
162
140
|
identifier: email,
|
|
163
141
|
});
|
|
164
|
-
await
|
|
142
|
+
await repo(oSafe.Account).insert({
|
|
165
143
|
provider: FFAuthProvider.PASSWORD.id,
|
|
166
144
|
providerUserId: email,
|
|
167
145
|
userId: user.id,
|
|
168
|
-
hashPassword: await
|
|
146
|
+
hashPassword: await oSafe.password.hash(password),
|
|
169
147
|
token: oSafe.verifiedMethod === 'auto' ? undefined : token,
|
|
170
148
|
expiresAt: oSafe.verifiedMethod === 'auto'
|
|
171
149
|
? undefined
|
|
172
|
-
: createDate(
|
|
150
|
+
: createDate(AUTH_OPTIONS.providers?.password?.mail?.verify?.expiresIn ?? 5 * 60),
|
|
173
151
|
lastVerifiedAt: oSafe.verifiedMethod === 'auto' ? new Date() : undefined,
|
|
174
152
|
});
|
|
175
153
|
});
|
|
176
154
|
if (oSafe.verifiedMethod === 'auto') {
|
|
177
|
-
const user = await
|
|
155
|
+
const user = await repo(oSafe.User).findFirst({
|
|
178
156
|
identifier: email,
|
|
179
157
|
});
|
|
180
158
|
if (user) {
|
|
181
|
-
await
|
|
159
|
+
await ff_createSession(user.id);
|
|
182
160
|
}
|
|
183
161
|
}
|
|
184
162
|
else {
|
|
185
|
-
const url = `${remult.context.url.origin}${oSafe.firstlyData.props.ui?.paths.verify_email}?token=${token}`;
|
|
186
|
-
if (AUTH_OPTIONS.providers?.password?.
|
|
187
|
-
await AUTH_OPTIONS.providers?.password.
|
|
188
|
-
|
|
163
|
+
const url = `${remult.context.request.url.origin}${oSafe.firstlyData.props.ui?.paths.verify_email}?token=${token}`;
|
|
164
|
+
if (AUTH_OPTIONS.providers?.password?.mail?.verify?.send) {
|
|
165
|
+
await AUTH_OPTIONS.providers?.password.mail.verify.send({ email, url });
|
|
166
|
+
authModuleRaw.log.success(`${green('[custom]')}${magenta('[verifyMailSend]')} (${yellow(url)})`);
|
|
189
167
|
}
|
|
190
168
|
else {
|
|
191
169
|
await sendMail('verifyMailSend', {
|
|
@@ -205,7 +183,7 @@ export class AuthControllerServer {
|
|
|
205
183
|
],
|
|
206
184
|
},
|
|
207
185
|
});
|
|
208
|
-
|
|
186
|
+
authModuleRaw.log.success(`${magenta('[verifyMailSend]')} (${yellow(url)})`);
|
|
209
187
|
}
|
|
210
188
|
}
|
|
211
189
|
return 'ok';
|
|
@@ -214,51 +192,56 @@ export class AuthControllerServer {
|
|
|
214
192
|
* This is for login / password authentication SignIn
|
|
215
193
|
* _(The first param `email` can be "anything")_
|
|
216
194
|
*/
|
|
217
|
-
static async signInPassword(
|
|
195
|
+
static async signInPassword(emailParam, password) {
|
|
196
|
+
const email = emailParam.toLowerCase();
|
|
218
197
|
const oSafe = getSafeOptions();
|
|
219
|
-
|
|
220
|
-
|
|
198
|
+
oSafe.password.validateInput({ identifier: email, password });
|
|
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.verify(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}`;
|
|
258
|
-
if (AUTH_OPTIONS.providers?.password?.
|
|
259
|
-
await AUTH_OPTIONS.providers?.password.
|
|
260
|
-
|
|
261
|
-
return
|
|
238
|
+
existingAccount.expiresAt = createDate(AUTH_OPTIONS.providers?.password?.mail?.reset?.expiresIn ?? 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}`;
|
|
241
|
+
if (AUTH_OPTIONS.providers?.password?.mail?.reset?.send) {
|
|
242
|
+
await AUTH_OPTIONS.providers?.password.mail.reset.send({ email, url });
|
|
243
|
+
authModuleRaw.log.success(`${green('[custom]')}${magenta('[resetPasswordSend]')} (${yellow(url)})`);
|
|
244
|
+
return oSafe.strings.resetPasswordSend;
|
|
262
245
|
}
|
|
263
246
|
else {
|
|
264
247
|
await sendMail('resetPasswordSend', {
|
|
@@ -282,119 +265,120 @@ export class AuthControllerServer {
|
|
|
282
265
|
],
|
|
283
266
|
},
|
|
284
267
|
});
|
|
285
|
-
|
|
286
|
-
return
|
|
268
|
+
authModuleRaw.log.success(`${magenta('[resetPasswordSend]')} (${yellow(url)})`);
|
|
269
|
+
return `Demo | ${oSafe.strings.resetPasswordSend}`;
|
|
287
270
|
}
|
|
288
271
|
}
|
|
289
|
-
throw new
|
|
272
|
+
throw new EntityError({ message: oSafe.strings.anErrorOccurred });
|
|
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
|
}
|
|
282
|
+
oSafe.password.validateInput({ identifier: 'resetPassword', password });
|
|
299
283
|
const account = await remult
|
|
300
284
|
.repo(oSafe.Account)
|
|
301
285
|
.findFirst({ token, provider: FFAuthProvider.PASSWORD.id });
|
|
302
286
|
if (!account) {
|
|
303
|
-
throw new
|
|
287
|
+
throw new EntityError({ message: 'Invalid token' });
|
|
304
288
|
}
|
|
305
289
|
if (account.expiresAt && account.expiresAt < new Date()) {
|
|
306
|
-
throw new
|
|
290
|
+
throw new EntityError({ message: 'token expired' });
|
|
307
291
|
}
|
|
308
|
-
checkPassword(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 = await
|
|
298
|
+
account.hashPassword = await oSafe.password.hash(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
|
}
|