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.
Files changed (139) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/esm/BaseEnum.d.ts +2 -0
  3. package/esm/BaseEnum.js +2 -0
  4. package/esm/FF_Fields.js +0 -1
  5. package/esm/ROUTES.d.ts +2 -2
  6. package/esm/ROUTES.js +10 -5
  7. package/esm/SqlDatabase/FF_LogToConsole.d.ts +1 -0
  8. package/esm/SqlDatabase/FF_LogToConsole.js +22 -16
  9. package/esm/api/index.d.ts +20 -21
  10. package/esm/api/index.js +74 -62
  11. package/esm/auth/{client/Auth.d.ts → AuthController.d.ts} +18 -25
  12. package/esm/auth/{client/Auth.js → AuthController.js} +48 -44
  13. package/esm/auth/{client/Entities.d.ts → Entities.d.ts} +4 -3
  14. package/esm/auth/{client/Entities.js → Entities.js} +7 -7
  15. package/esm/auth/README.md +0 -10
  16. package/esm/auth/index.d.ts +5 -149
  17. package/esm/auth/index.js +5 -316
  18. package/esm/auth/{AuthController.server.d.ts → server/AuthController.server.d.ts} +10 -10
  19. package/esm/auth/{AuthController.server.js → server/AuthController.server.js} +133 -171
  20. package/esm/auth/server/handleAuth.d.ts +2 -0
  21. package/esm/auth/server/handleAuth.js +140 -0
  22. package/esm/auth/server/handleGuard.d.ts +16 -0
  23. package/esm/auth/server/handleGuard.js +67 -0
  24. package/esm/auth/server/helperDb.d.ts +10 -0
  25. package/esm/auth/server/helperDb.js +56 -0
  26. package/esm/auth/server/helperFirstly.d.ts +1 -0
  27. package/esm/auth/server/helperFirstly.js +8 -0
  28. package/esm/auth/server/helperOslo.d.ts +7 -0
  29. package/esm/auth/server/helperOslo.js +24 -0
  30. package/esm/auth/server/helperRemultServer.d.ts +5 -0
  31. package/esm/auth/server/helperRemultServer.js +44 -0
  32. package/esm/auth/{RoleHelpers.d.ts → server/helperRole.d.ts} +1 -1
  33. package/esm/auth/{RoleHelpers.js → server/helperRole.js} +1 -1
  34. package/esm/auth/server/index.d.ts +7 -0
  35. package/esm/auth/server/index.js +7 -0
  36. package/esm/auth/server/module.d.ts +257 -0
  37. package/esm/auth/server/module.js +197 -0
  38. package/esm/auth/{providers → server/providers}/github.d.ts +6 -4
  39. package/esm/auth/{providers → server/providers}/github.js +29 -20
  40. package/esm/auth/{providers/index.d.ts → server/providers/helperProvider.d.ts} +0 -2
  41. package/esm/auth/{providers/index.js → server/providers/helperProvider.js} +5 -6
  42. package/esm/auth/static/assets/Page-BUfjaN-D.d.ts +5 -0
  43. package/esm/auth/static/assets/Page-BUfjaN-D.js +19 -0
  44. package/esm/auth/static/assets/Page-CJ58H1vl.css +1 -0
  45. package/esm/auth/static/assets/Page-CaDAqmBS.d.ts +5 -0
  46. package/esm/auth/static/assets/Page-CaDAqmBS.js +1 -0
  47. package/esm/auth/static/assets/Page-DhdZddzJ.d.ts +5 -0
  48. package/esm/auth/static/assets/Page-DhdZddzJ.js +1 -0
  49. package/esm/auth/static/assets/index-BDy4A_14.css +4 -0
  50. package/esm/auth/static/assets/index-D-Ztdt2o.d.ts +54 -0
  51. package/esm/auth/static/assets/index-D-Ztdt2o.js +2 -0
  52. package/esm/auth/static/index.html +11 -11
  53. package/esm/auth/types.d.ts +5 -0
  54. package/esm/bin/cmd.js +122 -54
  55. package/esm/cellsBuildor.js +7 -7
  56. package/esm/changeLog/index.d.ts +0 -36
  57. package/esm/changeLog/index.js +3 -43
  58. package/esm/changeLog/server/index.d.ts +36 -0
  59. package/esm/changeLog/server/index.js +42 -0
  60. package/esm/common.d.ts +5 -0
  61. package/esm/common.js +8 -0
  62. package/esm/cron/{index.d.ts → server/index.d.ts} +1 -1
  63. package/esm/cron/server/index.js +103 -0
  64. package/esm/feedback/FeedbackController.js +4 -5
  65. package/esm/feedback/index.d.ts +0 -16
  66. package/esm/feedback/index.js +0 -11
  67. package/esm/feedback/server/index.d.ts +17 -0
  68. package/esm/feedback/server/index.js +13 -0
  69. package/esm/feedback/ui/DialogIssue.svelte +52 -56
  70. package/esm/feedback/ui/DialogIssues.svelte +71 -71
  71. package/esm/feedback/ui/DialogMilestones.svelte +22 -22
  72. package/esm/helper.js +3 -3
  73. package/esm/index.d.ts +3 -20
  74. package/esm/index.js +3 -10
  75. package/esm/mail/index.d.ts +2 -30
  76. package/esm/mail/index.js +2 -79
  77. package/esm/mail/server/index.d.ts +31 -0
  78. package/esm/mail/server/index.js +88 -0
  79. package/esm/mail/templates/DefaultMail.svelte +17 -17
  80. package/esm/storeItem.js +8 -2
  81. package/esm/storeList.d.ts +1 -1
  82. package/esm/storeList.js +1 -1
  83. package/esm/sveltekit/server/index.d.ts +14 -0
  84. package/esm/sveltekit/server/index.js +24 -0
  85. package/esm/ui/Button.svelte +33 -33
  86. package/esm/ui/Button.svelte.d.ts +2 -2
  87. package/esm/ui/Clipboardable.svelte +11 -8
  88. package/esm/ui/Clipboardable.svelte.d.ts +4 -4
  89. package/esm/ui/Field.svelte +142 -149
  90. package/esm/ui/Field.svelte.d.ts +2 -2
  91. package/esm/ui/FieldGroup.svelte +38 -38
  92. package/esm/ui/Grid.svelte +212 -222
  93. package/esm/ui/GridLoading.svelte +18 -22
  94. package/esm/ui/GridPaginate.svelte +38 -38
  95. package/esm/ui/Icon.svelte +50 -49
  96. package/esm/ui/Icon.svelte.d.ts +18 -18
  97. package/esm/ui/Loading.svelte +5 -2
  98. package/esm/ui/Tooltip.svelte +16 -16
  99. package/esm/ui/dialog/DialogForm.svelte +23 -23
  100. package/esm/ui/dialog/DialogManagement.svelte +74 -74
  101. package/esm/ui/dialog/DialogPrimitive.svelte +50 -51
  102. package/esm/ui/dialog/FormEditAction.svelte +34 -34
  103. package/esm/ui/dialog/dialog.d.ts +2 -2
  104. package/esm/ui/dialog/dialog.js +1 -1
  105. package/esm/ui/internals/FieldContainer.svelte +11 -12
  106. package/esm/ui/internals/FieldContainer.svelte.d.ts +3 -3
  107. package/esm/ui/internals/Input.svelte +25 -25
  108. package/esm/ui/internals/Input.svelte.d.ts +1 -1
  109. package/esm/ui/internals/Textarea.svelte +21 -21
  110. package/esm/ui/internals/Textarea.svelte.d.ts +2 -2
  111. package/esm/ui/internals/select/MultiSelectMelt.svelte +69 -73
  112. package/esm/ui/internals/select/SelectMelt.svelte +86 -86
  113. package/esm/ui/internals/select/SelectRadio.svelte +22 -22
  114. package/esm/ui/link/Link.svelte +14 -14
  115. package/esm/ui/link/Link.svelte.d.ts +3 -4
  116. package/esm/ui/link/LinkPlus.svelte +33 -35
  117. package/esm/vite/index.js +24 -25
  118. package/package.json +50 -42
  119. package/esm/auth/Adapter.d.ts +0 -10
  120. package/esm/auth/Adapter.js +0 -50
  121. package/esm/auth/client/index.d.ts +0 -7
  122. package/esm/auth/client/index.js +0 -7
  123. package/esm/auth/helper.d.ts +0 -6
  124. package/esm/auth/helper.js +0 -14
  125. package/esm/auth/providers/strava.d.ts +0 -30
  126. package/esm/auth/providers/strava.js +0 -60
  127. package/esm/auth/static/assets/Page-BEFYPjis.d.ts +0 -4
  128. package/esm/auth/static/assets/Page-BEFYPjis.js +0 -1
  129. package/esm/auth/static/assets/Page-BGTO8LC5.css +0 -1
  130. package/esm/auth/static/assets/Page-Cfysx_UV.d.ts +0 -6
  131. package/esm/auth/static/assets/Page-Cfysx_UV.js +0 -18
  132. package/esm/auth/static/assets/Page-DtgkOCJs.d.ts +0 -4
  133. package/esm/auth/static/assets/Page-DtgkOCJs.js +0 -1
  134. package/esm/auth/static/assets/index-CR_3yNaJ.css +0 -4
  135. package/esm/auth/static/assets/index-QypqCYwC.d.ts +0 -63
  136. package/esm/auth/static/assets/index-QypqCYwC.js +0 -2
  137. package/esm/cron/index.js +0 -102
  138. package/esm/handle/index.d.ts +0 -7
  139. package/esm/handle/index.js +0 -40
@@ -1,49 +1,25 @@
1
- import { generateCodeVerifier, generateState } from 'arctic';
2
- import { DEV } from 'esm-env';
3
- import { generateId } from 'lucia';
4
- import { createDate, TimeSpan } from 'oslo';
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 { AUTH_OPTIONS, getSafeOptions, lucia } from '.';
8
- import { sendMail } from '../mail';
9
- import { logAuth } from './client';
10
- import { FFAuthProvider } from './client/Entities.js';
11
- import { createOrExtendSession } from './helper';
12
- import { mergeRoles } from './RoleHelpers';
13
- async function getArgon() {
14
- const { Argon2id } = await import('oslo/password');
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 lucia.invalidateSession(remult.user?.session.id);
20
+ await invalidateSession(remult.user?.session.id);
39
21
  }
40
- // Lucia is advertising for createBlankSessionCookie (and not delete Cookie)
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 Error(`Demo accounts are not enabled!`);
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 Error(`${name} not found as demo account!`);
35
+ throw new EntityError({ message: `${name} not found as demo account!` });
60
36
  }
61
37
  const oSafe = getSafeOptions();
62
- let user = await remult.repo(oSafe.User).findFirst({ identifier: name });
38
+ let user = await repo(oSafe.User).findFirst({ identifier: name });
63
39
  if (!user) {
64
- user = remult.repo(oSafe.User).create();
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 remult.repo(oSafe.User).save(user);
70
- await createOrExtendSession(user.id);
71
- return "You're in with demo account!";
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(email) {
52
+ static async invite(emailParam) {
53
+ const email = emailParam.toLowerCase();
77
54
  const oSafe = getSafeOptions();
78
- const existingAccount = await remult.repo(oSafe.Account).findOne({
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
- // throw Error("Already invited !")
62
+ // Already invited, it's all good.
86
63
  }
87
64
  else {
88
- const token = generateId(40);
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 remult.repo(oSafe.User).insert({
68
+ // const user = await repo(oSafe.User).insert({
92
69
  // identifier: email,
93
70
  // })
94
- await remult.repo(oSafe.Account).insert({
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(new TimeSpan(AUTH_OPTIONS.providers?.password?.verifyMailExpiresIn ?? 5 * 60, 's')),
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
- logAuth.success(`${green('[custom]')}${magenta('[invitationSend]')} (${yellow(url)})`);
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
- logAuth.success(`${magenta('[invitationSend]')} (${yellow(url)})`);
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(email, password) {
118
+ static async signUpPassword(emailParam, password) {
142
119
  const oSafe = getSafeOptions();
143
120
  if (!oSafe.signUp) {
144
- throw Error("You can't signup by yourself! Contact the administrator.");
121
+ throw new EntityError({ message: oSafe.strings.cannotSignUp });
145
122
  }
146
- if (!oSafe.password_enabled) {
147
- throw Error('Password is not enabled!');
123
+ if (!oSafe.password.enabled) {
124
+ throw new EntityError({ message: 'Password is not enabled!' });
148
125
  }
149
- const existingAccount = await remult.repo(oSafe.Account).findOne({
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 Error("You can't signup twice !");
135
+ throw new EntityError({ message: "You can't signup twice !" });
157
136
  }
158
- checkPassword(password);
159
- const token = generateId(40);
137
+ const token = generateAndEncodeToken();
160
138
  await remult.dataProvider.transaction(async () => {
161
- const user = await remult.repo(oSafe.User).insert({
139
+ const user = await repo(oSafe.User).insert({
162
140
  identifier: email,
163
141
  });
164
- await remult.repo(oSafe.Account).insert({
142
+ await repo(oSafe.Account).insert({
165
143
  provider: FFAuthProvider.PASSWORD.id,
166
144
  providerUserId: email,
167
145
  userId: user.id,
168
- hashPassword: await passwordHash(password),
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(new TimeSpan(AUTH_OPTIONS.providers?.password?.verifyMailExpiresIn ?? 5 * 60, 's')),
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 remult.repo(oSafe.User).findFirst({
155
+ const user = await repo(oSafe.User).findFirst({
178
156
  identifier: email,
179
157
  });
180
158
  if (user) {
181
- await createOrExtendSession(user.id);
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?.verifyMailSend) {
187
- await AUTH_OPTIONS.providers?.password.verifyMailSend({ email, url });
188
- logAuth.success(`${green('[custom]')}${magenta('[verifyMailSend]')} (${yellow(url)})`);
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
- logAuth.success(`${magenta('[verifyMailSend]')} (${yellow(url)})`);
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(email, password) {
195
+ static async signInPassword(emailParam, password) {
196
+ const email = emailParam.toLowerCase();
218
197
  const oSafe = getSafeOptions();
219
- if (!oSafe.password_enabled) {
220
- throw Error('Password is not enabled!');
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 remult.repo(oSafe.Account).findOne({
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 = await passwordVerify(existingAccount?.hashPassword ?? '', password ?? '');
209
+ const validPassword = oSafe.password.verify(password ?? '', existingAccount?.hashPassword ?? '');
230
210
  if (validPassword) {
231
- await createOrExtendSession(existingAccount.userId);
211
+ await ff_createSession(existingAccount.userId);
232
212
  return 'ok';
233
213
  }
234
- throw Error('Incorrect username or password');
214
+ authModuleRaw.log.error({ email, passwordLength: password.length });
215
+ throw new EntityError({ message: 'Incorrect username or password' });
235
216
  }
236
- throw Error('Incorrect username or password.');
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(email) {
223
+ static async forgotPassword(emailParam) {
224
+ const email = emailParam.toLowerCase();
242
225
  const oSafe = getSafeOptions();
243
- if (!oSafe.password_enabled) {
244
- throw Error('Password is not enabled!');
226
+ if (!oSafe.password.enabled) {
227
+ throw new EntityError({ message: 'Password is not enabled!' });
245
228
  }
246
- const existingAccount = await remult.repo(oSafe.Account).findOne({
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 = generateId(40);
236
+ const token = generateAndEncodeToken();
254
237
  existingAccount.token = token;
255
- existingAccount.expiresAt = createDate(new TimeSpan(AUTH_OPTIONS.providers?.password?.resetPasswordExpiresIn ?? 5 * 60, 's'));
256
- await remult.repo(oSafe.Account).save(existingAccount);
257
- const url = `${remult.context.url.origin}${oSafe.firstlyData.props.ui?.paths.reset_password}?token=${token}`;
258
- if (AUTH_OPTIONS.providers?.password?.resetPasswordSend) {
259
- await AUTH_OPTIONS.providers?.password.resetPasswordSend({ email, url });
260
- logAuth.success(`${green('[custom]')}${magenta('[resetPasswordSend]')} (${yellow(url)})`);
261
- return 'Mail sent !';
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
- logAuth.success(`${magenta('[resetPasswordSend]')} (${yellow(url)})`);
286
- return 'Demo Mail sent !';
268
+ authModuleRaw.log.success(`${magenta('[resetPasswordSend]')} (${yellow(url)})`);
269
+ return `Demo | ${oSafe.strings.resetPasswordSend}`;
287
270
  }
288
271
  }
289
- throw new Error("Une erreur est survenue, contacte l'administrateur!");
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.password_enabled) {
297
- throw Error('Password is not enabled!');
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 Error('Invalid token');
287
+ throw new EntityError({ message: 'Invalid token' });
304
288
  }
305
289
  if (account.expiresAt && account.expiresAt < new Date()) {
306
- throw new Error('token expired');
290
+ throw new EntityError({ message: 'token expired' });
307
291
  }
308
- checkPassword(password);
309
292
  if (account.userId === undefined) {
310
- const user = await remult.repo(oSafe.User).insert({ identifier: account.providerUserId });
293
+ const user = await repo(oSafe.User).insert({ identifier: account.providerUserId });
311
294
  account.userId = user.id;
312
295
  }
313
- await lucia.invalidateUserSessions(account.userId);
296
+ await invalidateSession(account.userId);
314
297
  // update elements
315
- account.hashPassword = await passwordHash(password);
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 remult.repo(oSafe.Account).save(account);
320
- await createOrExtendSession(account.userId);
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(email) {
307
+ static async signInOTP(emailParam) {
308
+ const email = emailParam.toLowerCase();
325
309
  const oSafe = getSafeOptions();
326
- if (!oSafe.otp_enabled) {
327
- throw new Error(`OPT is not enabled!`);
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 { createTOTPKeyURI } = await import('oslo/otp');
331
- const { encodeHex } = await import('oslo/encoding');
332
- const { TOTPController } = await import('oslo/otp');
333
- const secret = crypto.getRandomValues(new Uint8Array(20));
334
- const otp = await new TOTPController({
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, secret);
320
+ const uri = createTOTPKeyURI(issuer, email, key, intervalInSeconds, digits);
341
321
  const oSafe = getSafeOptions();
342
- let user = await remult.repo(oSafe.User).findFirst({ identifier: email });
322
+ let user = await repo(oSafe.User).findFirst({ identifier: email });
343
323
  if (!user) {
344
- user = remult.repo(oSafe.User).create();
324
+ user = repo(oSafe.User).create();
345
325
  }
346
326
  user.identifier = email;
347
- user = await remult.repo(oSafe.User).save(user);
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 = remult.repo(oSafe.Account).create();
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 = secretEncoded;
358
- await remult.repo(oSafe.Account).save(account);
337
+ account.hashPassword = keyEncoded;
338
+ await repo(oSafe.Account).save(account);
359
339
  await AUTH_OPTIONS.providers.otp?.send({ name: email, otp, uri });
360
- logAuth.success(`name: ${yellow(email)}, otp: ${yellow(otp)}, uri: ${yellow(uri)}`);
340
+ authModuleRaw.log.success(`name: ${yellow(email)}, otp: ${yellow(otp)}, uri: ${yellow(uri)}`);
361
341
  return 'Mail sent !';
362
342
  }
363
343
  else {
364
- logAuth.error(`You need to provide a otp.send hook in the auth options!`);
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(email, otp) {
351
+ static async verifyOtp(emailParam, otp) {
352
+ const email = emailParam.toLowerCase();
372
353
  const oSafe = getSafeOptions();
373
- const accounts = await remult.repo(oSafe.Account).find({
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 Error('Invalid otp');
361
+ throw new EntityError({ message: 'Invalid otp' });
378
362
  }
379
363
  const account = accounts[0];
380
- const user = await remult.repo(oSafe.User).findId(account.userId);
364
+ const user = await repo(oSafe.User).findId(account.userId);
381
365
  if (user?.identifier !== email) {
382
- throw new Error('Invalid otp.');
366
+ throw new EntityError({ message: 'Invalid otp.' });
383
367
  }
384
- const { decodeHex } = await import('oslo/encoding');
385
- const { TOTPController } = await import('oslo/otp');
386
- const secretDecoded = decodeHex(account.hashPassword ?? '');
387
- const validOTP = await new TOTPController().verify(String(otp), secretDecoded);
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 Error('Invalid otp!');
373
+ throw new EntityError({ message: 'Invalid otp!' });
390
374
  }
391
- await lucia.invalidateUserSessions(account.userId);
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 remult.repo(oSafe.Account).save(account);
397
- await createOrExtendSession(account.userId);
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 Error('No url returned');
414
+ throw new EntityError({ message: 'No url returned' });
443
415
  }
444
- remult.context.setCookie(`${o.provider}_oauth_state`, state, {
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
- remult.context.setCookie(`remult_redirect`, o.redirect, {
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
- logAuth.error(error);
465
- throw new Error(`${o.provider} not well configured!`);
424
+ authModuleRaw.log.error(error);
425
+ throw new EntityError({ message: `${selectedOAuth.name} not well configured!` });
466
426
  }
467
427
  }
468
- throw new Error(`${o.provider} is not configured! (Module: auth, section: providers.oAuths: [${o.provider}] missing)`);
428
+ throw new EntityError({
429
+ message: `${o.provider} is not configured! (Module: auth, section: providers.oAuths: [${o.provider}] missing)`,
430
+ });
469
431
  }
470
432
  }
@@ -0,0 +1,2 @@
1
+ import { type Handle } from '@sveltejs/kit';
2
+ export declare const handleAuth: Handle;