@adonis-agora/authkit-server 0.46.0 → 0.48.0
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/build/commands/clients_create.js +4 -4
- package/build/commands/configure.js +3 -2
- package/build/commands/doctor.js +8 -5
- package/build/commands/eject.js +1 -1
- package/build/commands/expire_scan.js +7 -5
- package/build/commands/import_users.js +1 -1
- package/build/commands/keys_rotate.js +7 -4
- package/build/host/views/account/apps.edge +5 -5
- package/build/host/views/account/login.edge +1 -1
- package/build/host/views/account/mfa.edge +7 -7
- package/build/host/views/account/orgs.edge +5 -5
- package/build/host/views/account/security.edge +10 -10
- package/build/host/views/account/tokens.edge +5 -5
- package/build/host/views/otp-unlock.edge +2 -2
- package/build/index.d.ts +7 -7
- package/build/index.js +5 -5
- package/build/providers/authkit_server_provider.d.ts +2 -2
- package/build/providers/authkit_server_provider.js +26 -10
- package/build/services/booted_app.d.ts +8 -0
- package/build/services/booted_app.js +27 -0
- package/build/services/main.d.ts +8 -1
- package/build/services/main.js +10 -2
- package/build/src/accounts/lucid_account_store.d.ts +3 -3
- package/build/src/accounts/lucid_account_store.js +6 -8
- package/build/src/accounts/lucid_store/core.js +1 -1
- package/build/src/accounts/lucid_store/mfa.js +1 -1
- package/build/src/accounts/lucid_store/organizations.js +2 -4
- package/build/src/accounts/lucid_store/password_hygiene.js +1 -4
- package/build/src/accounts/lucid_store/shared.d.ts +1 -1
- package/build/src/accounts/lucid_stores.d.ts +3 -3
- package/build/src/accounts/lucid_stores.js +2 -2
- package/build/src/adapters/database_adapter.js +3 -1
- package/build/src/adapters/factory.js +1 -1
- package/build/src/adapters/redis_adapter.js +2 -1
- package/build/src/commands/expire_scan_command.js +19 -5
- package/build/src/commands/settings_commands.js +22 -5
- package/build/src/define_config.d.ts +20 -20
- package/build/src/define_config.js +62 -67
- package/build/src/doctor/checks.js +45 -20
- package/build/src/events/dispatcher.d.ts +1 -1
- package/build/src/events/dispatcher.js +10 -10
- package/build/src/host/account_api/account_api_controller.js +95 -39
- package/build/src/host/account_deletion_ops.d.ts +4 -4
- package/build/src/host/account_deletion_ops.js +5 -5
- package/build/src/host/account_deletion_service.d.ts +2 -2
- package/build/src/host/account_deletion_service.js +2 -2
- package/build/src/host/account_export_service.d.ts +1 -1
- package/build/src/host/account_export_service.js +4 -6
- package/build/src/host/account_home.d.ts +0 -8
- package/build/src/host/account_home.js +6 -4
- package/build/src/host/account_login_url.d.ts +44 -0
- package/build/src/host/account_login_url.js +58 -0
- package/build/src/host/account_paths.d.ts +120 -0
- package/build/src/host/account_paths.js +161 -0
- package/build/src/host/admin_api/admin_orgs_service.d.ts +2 -2
- package/build/src/host/admin_api/admin_orgs_service.js +3 -2
- package/build/src/host/admin_api/admin_users_service.d.ts +12 -12
- package/build/src/host/admin_api/admin_users_service.js +20 -20
- package/build/src/host/admin_api/api_clients_controller.js +2 -2
- package/build/src/host/admin_api/api_misc_controller.js +2 -2
- package/build/src/host/admin_api/api_orgs_controller.js +3 -3
- package/build/src/host/admin_api/api_settings_controller.js +11 -5
- package/build/src/host/admin_api/api_users_controller.d.ts +2 -2
- package/build/src/host/admin_api/api_users_controller.js +37 -38
- package/build/src/host/admin_api/dto.d.ts +4 -4
- package/build/src/host/admin_clients_service.js +5 -4
- package/build/src/host/admin_console/admin_shell_controller.js +4 -2
- package/build/src/host/admin_console/console_audit_controller.js +1 -1
- package/build/src/host/admin_console/console_clients_controller.js +5 -3
- package/build/src/host/admin_console/console_impersonation_controller.js +1 -1
- package/build/src/host/admin_console/console_keys_controller.js +2 -4
- package/build/src/host/admin_console/console_orgs_controller.js +3 -3
- package/build/src/host/admin_console/console_roles_controller.js +30 -12
- package/build/src/host/admin_console/console_sessions_controller.js +3 -3
- package/build/src/host/admin_console/console_settings_controller.js +11 -5
- package/build/src/host/admin_console/console_users_controller.d.ts +2 -2
- package/build/src/host/admin_console/console_users_controller.js +46 -47
- package/build/src/host/admin_prefix.js +2 -2
- package/build/src/host/admin_sessions_service.d.ts +3 -1
- package/build/src/host/admin_sessions_service.js +36 -6
- package/build/src/host/adonis_auth_sync.d.ts +1 -1
- package/build/src/host/adonis_auth_user_provider.d.ts +1 -1
- package/build/src/host/app_key.js +1 -1
- package/build/src/host/avatar_storage.js +1 -3
- package/build/src/host/config_locks.js +1 -3
- package/build/src/host/console_session.d.ts +4 -0
- package/build/src/host/console_session.js +9 -2
- package/build/src/host/controllers/account_apps_controller.js +3 -2
- package/build/src/host/controllers/account_confirm_controller.js +2 -6
- package/build/src/host/controllers/account_mfa_controller.js +63 -12
- package/build/src/host/controllers/account_orgs_controller.js +53 -30
- package/build/src/host/controllers/account_security_controller.d.ts +2 -2
- package/build/src/host/controllers/account_security_controller.js +116 -116
- package/build/src/host/controllers/account_session_controller.js +7 -5
- package/build/src/host/controllers/account_tokens_controller.js +14 -2
- package/build/src/host/controllers/interaction_controller.js +59 -22
- package/build/src/host/controllers/pat_introspection_controller.js +10 -0
- package/build/src/host/controllers/registration_controller.js +28 -13
- package/build/src/host/default_mailer.js +11 -7
- package/build/src/host/durable/account_deletion_workflow.d.ts +2 -2
- package/build/src/host/durable/account_deletion_workflow.js +14 -14
- package/build/src/host/durable/account_export_workflow.d.ts +2 -2
- package/build/src/host/durable/account_export_workflow.js +12 -12
- package/build/src/host/durable/index.d.ts +5 -5
- package/build/src/host/durable/index.js +6 -6
- package/build/src/host/i18n.d.ts +1376 -1376
- package/build/src/host/i18n.js +1380 -1380
- package/build/src/host/impersonation.js +1 -7
- package/build/src/host/key_rotation_actions.d.ts +1 -1
- package/build/src/host/login_attempt.js +35 -6
- package/build/src/host/login_notify.js +1 -1
- package/build/src/host/middleware/account_auth.js +3 -1
- package/build/src/host/otp_lockout.js +1 -3
- package/build/src/host/rate_limit.js +1 -1
- package/build/src/host/register_auth_host.d.ts +96 -2
- package/build/src/host/register_auth_host.js +196 -91
- package/build/src/host/renderers/edge_renderer.js +19 -1
- package/build/src/host/renderers/inertia_renderer.d.ts +63 -0
- package/build/src/host/renderers/inertia_renderer.js +63 -0
- package/build/src/host/runtime_settings.js +1 -1
- package/build/src/host/runtime_toggles.d.ts +3 -3
- package/build/src/host/runtime_toggles.js +69 -24
- package/build/src/host/security_notice_service.d.ts +1 -1
- package/build/src/host/security_notice_service.js +1 -1
- package/build/src/host/session_context.js +1 -1
- package/build/src/host/sudo/index.d.ts +3 -3
- package/build/src/host/sudo/index.js +3 -3
- package/build/src/host/sudo/methods/magic_link.js +6 -5
- package/build/src/host/sudo/methods/passkey.js +7 -4
- package/build/src/host/sudo/methods/password.js +8 -3
- package/build/src/host/sudo/runtime.js +6 -13
- package/build/src/host/sudo_mode.js +3 -2
- package/build/src/host/ui-dist/assets/index-BQoTDiLV.js +137 -0
- package/build/src/host/ui-dist/index.html +1 -1
- package/build/src/keys/jwks_manager.js +1 -1
- package/build/src/keys/keystore.js +1 -1
- package/build/src/keys/keystore_manager.d.ts +2 -2
- package/build/src/keys/keystore_manager.js +2 -2
- package/build/src/keys/keystore_vault.js +8 -4
- package/build/src/mixins/with_audit_log.d.ts +1 -1
- package/build/src/mixins/with_auth_user.d.ts +1 -1
- package/build/src/mixins/with_auth_user.js +1 -1
- package/build/src/mixins/with_credentials.d.ts +1 -1
- package/build/src/mixins/with_mfa.d.ts +1 -1
- package/build/src/mixins/with_personal_access_token.d.ts +1 -1
- package/build/src/mixins/with_provider_identity.d.ts +1 -1
- package/build/src/mixins/with_webauthn_credential.d.ts +1 -1
- package/build/src/observability/diagnostics_bridge.js +2 -2
- package/build/src/observability/metrics_service.js +1 -1
- package/build/src/observability/telescope/data_providers.d.ts +1 -1
- package/build/src/observability/telescope/data_providers.js +29 -43
- package/build/src/observability/telescope/extension.d.ts +1 -1
- package/build/src/observability/telescope/extension.js +58 -58
- package/build/src/observability/telescope/index.d.ts +2 -2
- package/build/src/observability/telescope/index.js +2 -2
- package/build/src/observability/wire_provider_events.d.ts +1 -1
- package/build/src/observability/wire_provider_events.js +11 -11
- package/build/src/password/common_passwords.js +1 -1
- package/build/src/password/password_manager.d.ts +1 -1
- package/build/src/password/password_manager.js +4 -4
- package/build/src/pat/lucid_pat_store.js +2 -6
- package/build/src/pat/pat_tokens.js +1 -1
- package/build/src/provider/interaction_actions.js +1 -1
- package/build/src/provider/key_rotation_scheduler.js +3 -1
- package/build/src/provider/logout_sources.js +2 -8
- package/build/src/provider/oidc_service.d.ts +2 -2
- package/build/src/provider/oidc_service.js +22 -14
- package/build/src/provider/single_flight_lock.js +1 -3
- package/build/src/provider/token_exchange.d.ts +1 -1
- package/build/src/schema/ensure.js +3 -1
- package/build/stubs/ui/react/components/auth_shell.tsx +19 -19
- package/build/stubs/ui/react/pages/account/login.tsx +6 -7
- package/build/stubs/ui/react/pages/account/mfa.tsx +16 -15
- package/build/stubs/ui/react/pages/account/tokens.tsx +27 -17
- package/build/stubs/ui/react/pages/consent.tsx +12 -12
- package/build/stubs/ui/react/pages/forgot.tsx +7 -7
- package/build/stubs/ui/react/pages/login.tsx +22 -24
- package/build/stubs/ui/react/pages/mfa-challenge.tsx +9 -10
- package/build/stubs/ui/react/pages/reset.tsx +10 -10
- package/build/stubs/ui/react/pages/signup.tsx +30 -16
- package/build/stubs/ui/react/pages/verify-email.tsx +6 -6
- package/package.json +1 -1
- package/stubs/main.ts +2 -2
- package/stubs/ui/react/components/auth_shell.tsx +19 -19
- package/stubs/ui/react/pages/account/login.tsx +6 -7
- package/stubs/ui/react/pages/account/mfa.tsx +16 -15
- package/stubs/ui/react/pages/account/tokens.tsx +27 -17
- package/stubs/ui/react/pages/consent.tsx +12 -12
- package/stubs/ui/react/pages/forgot.tsx +7 -7
- package/stubs/ui/react/pages/login.tsx +22 -24
- package/stubs/ui/react/pages/mfa-challenge.tsx +9 -10
- package/stubs/ui/react/pages/reset.tsx +10 -10
- package/stubs/ui/react/pages/signup.tsx +30 -16
- package/stubs/ui/react/pages/verify-email.tsx +6 -6
- package/build/src/host/ui-dist/assets/index-BmI_J87g.js +0 -137
|
@@ -28,21 +28,22 @@
|
|
|
28
28
|
* GET /account/api/orgs/invitations → convites pendentes
|
|
29
29
|
*/
|
|
30
30
|
import '../augmentations.js';
|
|
31
|
-
import {
|
|
32
|
-
import { supportsAccountSecurity, supportsProfile, supportsPasskeys, supportsOrganizations, } from '../../accounts/account_store.js';
|
|
33
|
-
import { changePasswordValidator, changeEmailValidator, updateProfileValidator, } from '../validators.js';
|
|
34
|
-
import { AdminSessionsService } from '../admin_sessions_service.js';
|
|
35
|
-
import { enrichSessionsWithContext } from '../session_context.js';
|
|
31
|
+
import { supportsAccountSecurity, supportsOrganizations, supportsPasskeys, supportsProfile, } from '../../accounts/account_store.js';
|
|
36
32
|
import { PasswordPolicyError } from '../../password/password_manager.js';
|
|
37
|
-
import {
|
|
33
|
+
import { accountPath } from '../account_paths.js';
|
|
34
|
+
import { ACTIVE_ORG_COOKIE } from '../active_org_cookie.js';
|
|
35
|
+
import { AdminSessionsService } from '../admin_sessions_service.js';
|
|
38
36
|
import { syncAdonisAuthLogout } from '../adonis_auth_sync.js';
|
|
37
|
+
import { AvatarUploadError, isAvatarUploadSupported, storeAvatar } from '../avatar_storage.js';
|
|
38
|
+
import { sendEmailChangeConfirmationEmail, sendEmailChangeNoticeEmail } from '../default_mailer.js';
|
|
39
|
+
import { translate } from '../i18n.js';
|
|
40
|
+
import { ACCOUNT_SESSION_KEY } from '../middleware/account_auth.js';
|
|
41
|
+
import { resolveRuntimeSettings } from '../runtime_settings.js';
|
|
39
42
|
import { resolveEffectiveEmailChange, resolveEffectivePasswordHistory, } from '../runtime_toggles.js';
|
|
40
43
|
import { dispatchSecurityNotice } from '../security_notice_service.js';
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
44
|
-
import { sendEmailChangeConfirmationEmail, sendEmailChangeNoticeEmail, } from '../default_mailer.js';
|
|
45
|
-
import { ACTIVE_ORG_COOKIE } from '../active_org_cookie.js';
|
|
44
|
+
import { enrichSessionsWithContext } from '../session_context.js';
|
|
45
|
+
import { SUDO_MODE_DEFAULTS, isSudoActive, requireSudo, resolveEffectiveSudoMode, } from '../sudo_mode.js';
|
|
46
|
+
import { changeEmailValidator, changePasswordValidator, updateProfileValidator, } from '../validators.js';
|
|
46
47
|
// ---------------------------------------------------------------------------
|
|
47
48
|
// Helpers (shared with existing controllers — kept local to avoid coupling)
|
|
48
49
|
// ---------------------------------------------------------------------------
|
|
@@ -91,7 +92,9 @@ export default class AccountApiController {
|
|
|
91
92
|
let sudoActive = false;
|
|
92
93
|
try {
|
|
93
94
|
const sudoSettings = await resolveRuntimeSettings(ctx);
|
|
94
|
-
const resolved = sudoSettings
|
|
95
|
+
const resolved = sudoSettings
|
|
96
|
+
? await resolveEffectiveSudoMode(sudoSettings)
|
|
97
|
+
: SUDO_MODE_DEFAULTS;
|
|
95
98
|
sudoActive = resolved.enabled ? isSudoActive(ctx, resolved.graceMinutes) : true;
|
|
96
99
|
}
|
|
97
100
|
catch {
|
|
@@ -212,7 +215,9 @@ export default class AccountApiController {
|
|
|
212
215
|
const store = cfg.accountStore;
|
|
213
216
|
const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
|
|
214
217
|
if (!supportsProfile(store)) {
|
|
215
|
-
return ctx.response
|
|
218
|
+
return ctx.response
|
|
219
|
+
.status(422)
|
|
220
|
+
.send(apiErr('capability_unsupported', translate(cfg.messages, 'account.profile.not_supported')));
|
|
216
221
|
}
|
|
217
222
|
const { name, avatarUrl } = await ctx.request.validateUsing(updateProfileValidator);
|
|
218
223
|
let resolvedAvatarUrl = avatarUrl ?? null;
|
|
@@ -263,30 +268,39 @@ export default class AccountApiController {
|
|
|
263
268
|
const store = cfg.accountStore;
|
|
264
269
|
const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
|
|
265
270
|
if (!supportsAccountSecurity(store)) {
|
|
266
|
-
return ctx.response
|
|
271
|
+
return ctx.response
|
|
272
|
+
.status(422)
|
|
273
|
+
.send(apiErr('capability_unsupported', translate(cfg.messages, 'account.security.not_supported')));
|
|
267
274
|
}
|
|
268
275
|
// Sudo gate.
|
|
269
276
|
const sudoSettings = await resolveRuntimeSettings(ctx);
|
|
270
277
|
const sudoResult = await requireSudo(ctx, sudoSettings);
|
|
271
278
|
if (sudoResult !== true) {
|
|
272
279
|
// Para a API JSON, devolvemos 403 em vez de redirecionar.
|
|
273
|
-
return ctx.response
|
|
280
|
+
return ctx.response
|
|
281
|
+
.status(403)
|
|
282
|
+
.send(apiErr('sudo_required', 'Identity confirmation required.'));
|
|
274
283
|
}
|
|
275
284
|
const { currentPassword, newPassword } = await ctx.request.validateUsing(changePasswordValidator);
|
|
276
285
|
const account = await store.findById(userId);
|
|
277
286
|
const verified = account ? await store.verifyCredentials(account.email, currentPassword) : null;
|
|
278
287
|
if (!verified) {
|
|
279
|
-
return ctx.response
|
|
288
|
+
return ctx.response
|
|
289
|
+
.status(422)
|
|
290
|
+
.send(apiErr('invalid_credentials', translate(cfg.messages, 'errors.invalid_credentials')));
|
|
280
291
|
}
|
|
281
292
|
// Histórico de senhas.
|
|
282
293
|
const { supportsPasswordHistory } = await import('../../accounts/account_store.js');
|
|
283
294
|
if (supportsPasswordHistory(store)) {
|
|
284
295
|
const histSettings = await resolvePasswordHistorySettings(ctx);
|
|
285
296
|
if (histSettings.enabled) {
|
|
286
|
-
const pepperedNew = cfg.accountStore.__passwordManager?.applyCurrentPepper?.(newPassword) ??
|
|
297
|
+
const pepperedNew = cfg.accountStore.__passwordManager?.applyCurrentPepper?.(newPassword) ??
|
|
298
|
+
newPassword;
|
|
287
299
|
const reused = await store.isPasswordReused(userId, pepperedNew, histSettings.count);
|
|
288
300
|
if (reused) {
|
|
289
|
-
return ctx.response
|
|
301
|
+
return ctx.response
|
|
302
|
+
.status(422)
|
|
303
|
+
.send(apiErr('password_reused', translate(cfg.messages, 'password.reused', { count: histSettings.count })));
|
|
290
304
|
}
|
|
291
305
|
const rawRow = await cfg.accountStore.__getRawRow?.(userId);
|
|
292
306
|
if (rawRow?.password) {
|
|
@@ -300,7 +314,9 @@ export default class AccountApiController {
|
|
|
300
314
|
}
|
|
301
315
|
catch (error) {
|
|
302
316
|
if (error instanceof PasswordPolicyError) {
|
|
303
|
-
return ctx.response
|
|
317
|
+
return ctx.response
|
|
318
|
+
.status(422)
|
|
319
|
+
.send(apiErr('password_policy', translate(cfg.messages, error.key, error.params)));
|
|
304
320
|
}
|
|
305
321
|
throw error;
|
|
306
322
|
}
|
|
@@ -327,31 +343,45 @@ export default class AccountApiController {
|
|
|
327
343
|
const store = cfg.accountStore;
|
|
328
344
|
const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
|
|
329
345
|
if (!supportsAccountSecurity(store)) {
|
|
330
|
-
return ctx.response
|
|
346
|
+
return ctx.response
|
|
347
|
+
.status(422)
|
|
348
|
+
.send(apiErr('capability_unsupported', translate(cfg.messages, 'account.security.not_supported')));
|
|
331
349
|
}
|
|
332
350
|
const sudoSettings = await resolveRuntimeSettings(ctx);
|
|
333
351
|
const sudoResult = await requireSudo(ctx, sudoSettings);
|
|
334
352
|
if (sudoResult !== true) {
|
|
335
|
-
return ctx.response
|
|
353
|
+
return ctx.response
|
|
354
|
+
.status(403)
|
|
355
|
+
.send(apiErr('sudo_required', 'Identity confirmation required.'));
|
|
336
356
|
}
|
|
337
357
|
const emailChangeSettings = await resolveEmailChangeSettings(ctx);
|
|
338
358
|
if (!emailChangeSettings.enabled) {
|
|
339
|
-
return ctx.response
|
|
359
|
+
return ctx.response
|
|
360
|
+
.status(422)
|
|
361
|
+
.send(apiErr('disabled', translate(cfg.messages, 'account.security.email_change_disabled')));
|
|
340
362
|
}
|
|
341
363
|
const { currentPassword, newEmail } = await ctx.request.validateUsing(changeEmailValidator);
|
|
342
364
|
const account = await store.findById(userId);
|
|
343
365
|
if (emailChangeSettings.requirePassword) {
|
|
344
366
|
if (!currentPassword) {
|
|
345
|
-
return ctx.response
|
|
367
|
+
return ctx.response
|
|
368
|
+
.status(422)
|
|
369
|
+
.send(apiErr('invalid_credentials', translate(cfg.messages, 'errors.invalid_credentials')));
|
|
346
370
|
}
|
|
347
|
-
const verified = account
|
|
371
|
+
const verified = account
|
|
372
|
+
? await store.verifyCredentials(account.email, currentPassword)
|
|
373
|
+
: null;
|
|
348
374
|
if (!verified) {
|
|
349
|
-
return ctx.response
|
|
375
|
+
return ctx.response
|
|
376
|
+
.status(422)
|
|
377
|
+
.send(apiErr('invalid_credentials', translate(cfg.messages, 'errors.invalid_credentials')));
|
|
350
378
|
}
|
|
351
379
|
}
|
|
352
380
|
const issued = await store.requestEmailChange(userId, newEmail);
|
|
353
381
|
if (!issued) {
|
|
354
|
-
return ctx.response
|
|
382
|
+
return ctx.response
|
|
383
|
+
.status(409)
|
|
384
|
+
.send(apiErr('email_taken', translate(cfg.messages, 'errors.email_taken')));
|
|
355
385
|
}
|
|
356
386
|
await cfg.audit?.record({
|
|
357
387
|
type: 'email_change.requested',
|
|
@@ -360,7 +390,7 @@ export default class AccountApiController {
|
|
|
360
390
|
ip: ctx.request.ip?.() ?? null,
|
|
361
391
|
});
|
|
362
392
|
const origin = `${ctx.request.protocol()}://${ctx.request.host()}`;
|
|
363
|
-
const confirmUrl = `${origin}
|
|
393
|
+
const confirmUrl = `${origin}${accountPath('emailConfirm')}?token=${encodeURIComponent(issued.token)}`;
|
|
364
394
|
if (cfg.mail?.onEmailChangeConfirm) {
|
|
365
395
|
await cfg.mail.onEmailChangeConfirm({
|
|
366
396
|
email: newEmail,
|
|
@@ -370,7 +400,11 @@ export default class AccountApiController {
|
|
|
370
400
|
});
|
|
371
401
|
}
|
|
372
402
|
else if (cfg.mail?.onEmailVerification) {
|
|
373
|
-
await cfg.mail.onEmailVerification({
|
|
403
|
+
await cfg.mail.onEmailVerification({
|
|
404
|
+
email: newEmail,
|
|
405
|
+
verifyUrl: confirmUrl,
|
|
406
|
+
token: issued.token,
|
|
407
|
+
});
|
|
374
408
|
}
|
|
375
409
|
else {
|
|
376
410
|
await sendEmailChangeConfirmationEmail(ctx, { email: newEmail, confirmUrl });
|
|
@@ -393,7 +427,9 @@ export default class AccountApiController {
|
|
|
393
427
|
const store = cfg.accountStore;
|
|
394
428
|
const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
|
|
395
429
|
if (!supportsAccountSecurity(store)) {
|
|
396
|
-
return ctx.response
|
|
430
|
+
return ctx.response
|
|
431
|
+
.status(422)
|
|
432
|
+
.send(apiErr('capability_unsupported', translate(cfg.messages, 'account.security.not_supported')));
|
|
397
433
|
}
|
|
398
434
|
const cancelFn = store.cancelEmailChange;
|
|
399
435
|
if (typeof cancelFn === 'function') {
|
|
@@ -447,7 +483,9 @@ export default class AccountApiController {
|
|
|
447
483
|
const sessionId = ctx.request.param('id');
|
|
448
484
|
const adminSessions = new AdminSessionsService(service);
|
|
449
485
|
if (!adminSessions.canList) {
|
|
450
|
-
return ctx.response
|
|
486
|
+
return ctx.response
|
|
487
|
+
.status(422)
|
|
488
|
+
.send(apiErr('capability_unsupported', 'Session enumeration not supported by this adapter.'));
|
|
451
489
|
}
|
|
452
490
|
// Verifica que a sessão pertence à conta logada.
|
|
453
491
|
const sessions = await adminSessions.listSessions(userId);
|
|
@@ -475,7 +513,9 @@ export default class AccountApiController {
|
|
|
475
513
|
const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
|
|
476
514
|
const adminSessions = new AdminSessionsService(service);
|
|
477
515
|
if (!adminSessions.canList) {
|
|
478
|
-
return ctx.response
|
|
516
|
+
return ctx.response
|
|
517
|
+
.status(422)
|
|
518
|
+
.send(apiErr('capability_unsupported', 'Session enumeration not supported by this adapter.'));
|
|
479
519
|
}
|
|
480
520
|
// Sem sessão OIDC corrente identificável aqui (o console de conta usa sessão
|
|
481
521
|
// Adonis, não sessão OIDC). Revogamos TODAS as sessões OIDC da conta.
|
|
@@ -503,7 +543,9 @@ export default class AccountApiController {
|
|
|
503
543
|
const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
|
|
504
544
|
const adminSessions = new AdminSessionsService(service);
|
|
505
545
|
if (!adminSessions.canList) {
|
|
506
|
-
return ctx.response
|
|
546
|
+
return ctx.response
|
|
547
|
+
.status(422)
|
|
548
|
+
.send(apiErr('capability_unsupported', 'Session enumeration not supported by this adapter.'));
|
|
507
549
|
}
|
|
508
550
|
// Revogar todas as sessões OIDC + grants da conta.
|
|
509
551
|
const result = await adminSessions.revokeAll(userId);
|
|
@@ -550,7 +592,9 @@ export default class AccountApiController {
|
|
|
550
592
|
const clientId = ctx.request.param('clientId');
|
|
551
593
|
const adminSessions = new AdminSessionsService(service);
|
|
552
594
|
if (!adminSessions.canList) {
|
|
553
|
-
return ctx.response
|
|
595
|
+
return ctx.response
|
|
596
|
+
.status(422)
|
|
597
|
+
.send(apiErr('capability_unsupported', 'Session enumeration not supported by this adapter.'));
|
|
554
598
|
}
|
|
555
599
|
const result = await adminSessions.revokeClientGrants(userId, clientId);
|
|
556
600
|
await cfg.audit?.record({
|
|
@@ -622,12 +666,16 @@ export default class AccountApiController {
|
|
|
622
666
|
const cfg = service.config;
|
|
623
667
|
const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
|
|
624
668
|
if (!supportsPasskeys(cfg.accountStore)) {
|
|
625
|
-
return ctx.response
|
|
669
|
+
return ctx.response
|
|
670
|
+
.status(422)
|
|
671
|
+
.send(apiErr('capability_unsupported', translate(cfg.messages, 'errors.passkeys_unavailable')));
|
|
626
672
|
}
|
|
627
673
|
const sudoSettings = await resolveRuntimeSettings(ctx);
|
|
628
674
|
const sudoResult = await requireSudo(ctx, sudoSettings);
|
|
629
675
|
if (sudoResult !== true) {
|
|
630
|
-
return ctx.response
|
|
676
|
+
return ctx.response
|
|
677
|
+
.status(403)
|
|
678
|
+
.send(apiErr('sudo_required', 'Identity confirmation required.'));
|
|
631
679
|
}
|
|
632
680
|
const credentialId = ctx.request.param('id');
|
|
633
681
|
await cfg.accountStore.removePasskey?.(userId, credentialId);
|
|
@@ -676,13 +724,17 @@ export default class AccountApiController {
|
|
|
676
724
|
const service = await ctx.containerResolver.make('authkit.server');
|
|
677
725
|
const cfg = service.config;
|
|
678
726
|
if (!cfg.patStore) {
|
|
679
|
-
return ctx.response
|
|
727
|
+
return ctx.response
|
|
728
|
+
.status(422)
|
|
729
|
+
.send(apiErr('capability_unsupported', 'PAT store not configured.'));
|
|
680
730
|
}
|
|
681
731
|
const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
|
|
682
732
|
const sudoSettings = await resolveRuntimeSettings(ctx);
|
|
683
733
|
const sudoResult = await requireSudo(ctx, sudoSettings);
|
|
684
734
|
if (sudoResult !== true) {
|
|
685
|
-
return ctx.response
|
|
735
|
+
return ctx.response
|
|
736
|
+
.status(403)
|
|
737
|
+
.send(apiErr('sudo_required', 'Identity confirmation required.'));
|
|
686
738
|
}
|
|
687
739
|
const name = ctx.request.input('name', 'Token').trim() || 'Token';
|
|
688
740
|
const { token, pat } = await cfg.patStore.issue({ accountId: userId, name });
|
|
@@ -709,13 +761,17 @@ export default class AccountApiController {
|
|
|
709
761
|
const service = await ctx.containerResolver.make('authkit.server');
|
|
710
762
|
const cfg = service.config;
|
|
711
763
|
if (!cfg.patStore) {
|
|
712
|
-
return ctx.response
|
|
764
|
+
return ctx.response
|
|
765
|
+
.status(422)
|
|
766
|
+
.send(apiErr('capability_unsupported', 'PAT store not configured.'));
|
|
713
767
|
}
|
|
714
768
|
const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
|
|
715
769
|
const sudoSettings = await resolveRuntimeSettings(ctx);
|
|
716
770
|
const sudoResult = await requireSudo(ctx, sudoSettings);
|
|
717
771
|
if (sudoResult !== true) {
|
|
718
|
-
return ctx.response
|
|
772
|
+
return ctx.response
|
|
773
|
+
.status(403)
|
|
774
|
+
.send(apiErr('sudo_required', 'Identity confirmation required.'));
|
|
719
775
|
}
|
|
720
776
|
const patId = ctx.request.param('id');
|
|
721
777
|
const revoked = await cfg.patStore.revoke(userId, patId);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { ResolvedServerConfig } from
|
|
3
|
-
import type {
|
|
4
|
-
import type { DeletionActor } from
|
|
1
|
+
import type { AuthAccount } from '../accounts/account_store.js';
|
|
2
|
+
import type { ResolvedServerConfig } from '../define_config.js';
|
|
3
|
+
import type { OidcService } from '../provider/oidc_service.js';
|
|
4
|
+
import type { DeletionActor } from './account_deletion_service.js';
|
|
5
5
|
/**
|
|
6
6
|
* As 9 etapas do cascade de deleção de conta (LGPD/GDPR) extraídas em operações
|
|
7
7
|
* DISCRETAS e IDEMPOTENTES. Cada uma é chamável individualmente — re-executar uma
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { supportsAccountDeletion, supportsMfa, supportsOrganizations, supportsPasskeys, supportsProviderIdentity, } from
|
|
2
|
-
import { AdminSessionsService } from
|
|
3
|
-
import { deleteAvatar } from
|
|
1
|
+
import { supportsAccountDeletion, supportsMfa, supportsOrganizations, supportsPasskeys, supportsProviderIdentity, } from '../accounts/account_store.js';
|
|
2
|
+
import { AdminSessionsService } from './admin_sessions_service.js';
|
|
3
|
+
import { deleteAvatar } from './avatar_storage.js';
|
|
4
4
|
/**
|
|
5
5
|
* Carrega o snapshot da conta. Retorna null se a conta não existe (ou já foi
|
|
6
6
|
* deletada) — idempotente: re-executar após a deleção da linha devolve null.
|
|
@@ -22,7 +22,7 @@ export async function snapshotAccount(cfg, accountId) {
|
|
|
22
22
|
*/
|
|
23
23
|
export async function auditDeleted(cfg, snapshot, actor) {
|
|
24
24
|
await cfg.audit?.record({
|
|
25
|
-
type:
|
|
25
|
+
type: 'account.deleted',
|
|
26
26
|
accountId: snapshot.id,
|
|
27
27
|
email: snapshot.email,
|
|
28
28
|
actorId: actor.actorId,
|
|
@@ -99,7 +99,7 @@ export async function deleteAccountAvatar(cfg, accountId, avatarUrl) {
|
|
|
99
99
|
}
|
|
100
100
|
/** 8) Anonimiza o histórico de audit da conta (quando o sink suporta). */
|
|
101
101
|
export async function anonymizeAudit(cfg, accountId) {
|
|
102
|
-
if (cfg.audit && typeof cfg.audit.anonymizeAccount ===
|
|
102
|
+
if (cfg.audit && typeof cfg.audit.anonymizeAccount === 'function') {
|
|
103
103
|
const auditAnonymized = await cfg.audit.anonymizeAccount(accountId);
|
|
104
104
|
return { auditAnonymized };
|
|
105
105
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OidcService } from
|
|
1
|
+
import type { OidcService } from '../provider/oidc_service.js';
|
|
2
2
|
/** Quem disparou a deleção (auditoria). 'self' = o próprio usuário; senão admin. */
|
|
3
3
|
export interface DeletionActor {
|
|
4
4
|
/** Id de quem agiu: o próprio user (self-service) ou o admin. null para admin-api. */
|
|
@@ -11,7 +11,7 @@ export interface DeletionActor {
|
|
|
11
11
|
* - 'admin' → um admin pelo console HTML;
|
|
12
12
|
* - 'admin-api' → via Admin REST API / SDK.
|
|
13
13
|
*/
|
|
14
|
-
source:
|
|
14
|
+
source: 'self' | 'admin' | 'admin-api';
|
|
15
15
|
}
|
|
16
16
|
/** Contagens do que foi removido no cascade (para auditoria/diagnóstico). */
|
|
17
17
|
export interface DeletionResult {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { supportsAccountDeletion } from
|
|
2
|
-
import { anonymizeAudit, auditDeleted, deleteAccountAvatar, deleteAccountRow, disableMfa, removeFromOrgs, removePasskeys, revokePats, revokeSessions, snapshotAccount, unlinkProviders, } from
|
|
1
|
+
import { supportsAccountDeletion } from '../accounts/account_store.js';
|
|
2
|
+
import { anonymizeAudit, auditDeleted, deleteAccountAvatar, deleteAccountRow, disableMfa, removeFromOrgs, removePasskeys, revokePats, revokeSessions, snapshotAccount, unlinkProviders, } from './account_deletion_ops.js';
|
|
3
3
|
/**
|
|
4
4
|
* Orquestra a deleção COMPLETA de uma conta (LGPD/GDPR — "direito ao
|
|
5
5
|
* esquecimento"), compartilhada entre o self-service (console de conta) e o admin
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OidcService } from
|
|
1
|
+
import type { OidcService } from '../provider/oidc_service.js';
|
|
2
2
|
/** Payload de export de dados de uma conta (portabilidade — LGPD/GDPR). */
|
|
3
3
|
export interface AccountExport {
|
|
4
4
|
/** Versão do formato do export (para evolução futura). */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { supportsOrganizations, supportsPasskeys, supportsProviderIdentity, } from
|
|
2
|
-
import { AdminSessionsService } from
|
|
1
|
+
import { supportsOrganizations, supportsPasskeys, supportsProviderIdentity, } from '../accounts/account_store.js';
|
|
2
|
+
import { AdminSessionsService } from './admin_sessions_service.js';
|
|
3
3
|
/** Limite de eventos de audit incluídos no export (evita payloads gigantes). */
|
|
4
4
|
const AUDIT_EXPORT_LIMIT = 1000;
|
|
5
5
|
/**
|
|
@@ -98,7 +98,7 @@ export class AccountExportService {
|
|
|
98
98
|
}
|
|
99
99
|
// Audit do próprio usuário (quando o sink suporta consulta).
|
|
100
100
|
let auditLog = [];
|
|
101
|
-
if (cfg.audit && typeof cfg.audit.list ===
|
|
101
|
+
if (cfg.audit && typeof cfg.audit.list === 'function') {
|
|
102
102
|
try {
|
|
103
103
|
const page = await cfg.audit.list({
|
|
104
104
|
subject: accountId,
|
|
@@ -110,9 +110,7 @@ export class AccountExportService {
|
|
|
110
110
|
clientId: e.clientId ?? null,
|
|
111
111
|
ip: e.ip ?? null,
|
|
112
112
|
metadata: e.metadata ?? null,
|
|
113
|
-
createdAt: typeof e.createdAt ===
|
|
114
|
-
? e.createdAt
|
|
115
|
-
: (e.createdAt?.toISOString?.() ?? null),
|
|
113
|
+
createdAt: typeof e.createdAt === 'string' ? e.createdAt : (e.createdAt?.toISOString?.() ?? null),
|
|
116
114
|
}));
|
|
117
115
|
}
|
|
118
116
|
catch {
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Destino default da área da conta (pós-login do console sem `return_to`,
|
|
3
|
-
* confirmações de e-mail, fallback de redirects).
|
|
4
|
-
*
|
|
5
|
-
* Configurável via `accountHome` no defineConfig; default '/account/security'
|
|
6
|
-
* (a "minha conta" natural — perfil, senha, sessões). Nunca '/account/tokens':
|
|
7
|
-
* aterrissar usuário comum numa tela de Personal Access Tokens é hostil.
|
|
8
|
-
*/
|
|
9
1
|
export declare function accountHome(cfg: {
|
|
10
2
|
accountHome?: string;
|
|
11
3
|
}): string;
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
* Destino default da área da conta (pós-login do console sem `return_to`,
|
|
3
3
|
* confirmações de e-mail, fallback de redirects).
|
|
4
4
|
*
|
|
5
|
-
* Configurável via `accountHome` no defineConfig; default '
|
|
6
|
-
* (a "minha conta" natural — perfil, senha, sessões
|
|
7
|
-
*
|
|
5
|
+
* Configurável via `accountHome` no defineConfig; default `accountPath('security')`
|
|
6
|
+
* (a "minha conta" natural — perfil, senha, sessões; `/account/security`, ou
|
|
7
|
+
* `/conta/seguranca` com overrides). Nunca a tela de tokens: aterrissar usuário
|
|
8
|
+
* comum numa tela de Personal Access Tokens é hostil.
|
|
8
9
|
*/
|
|
10
|
+
import { accountPath } from './account_paths.js';
|
|
9
11
|
export function accountHome(cfg) {
|
|
10
|
-
return cfg.accountHome ?? '
|
|
12
|
+
return cfg.accountHome ?? accountPath('security');
|
|
11
13
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL de login do console de conta — singleton de processo.
|
|
3
|
+
*
|
|
4
|
+
* Definida em tempo de registro das rotas (`registerAuthHost`, via a opção
|
|
5
|
+
* `accountLoginUrl`) e lida em runtime por TODOS os pontos que redirecionam o
|
|
6
|
+
* visitante não-autenticado para "faça login": os guards (`accountGuard`/
|
|
7
|
+
* `adminGuard`), o middleware `AccountAuthMiddleware`, o helper público
|
|
8
|
+
* `consoleLoginUrl()`, os redirects de fallback dos controllers de conta e a
|
|
9
|
+
* view Edge `otp-unlock` (injetada como prop `loginUrl` pelo renderer).
|
|
10
|
+
*
|
|
11
|
+
* Existe porque a tela `account/login` é DESMONTÁVEL (`account: { login: false }`):
|
|
12
|
+
* um host OIDC passwordless não monta o login por senha da lib e aponta o
|
|
13
|
+
* redirect de não-autenticado para a própria rota de login dele (ex.: `/login`).
|
|
14
|
+
* Sem esta indireção, esses destinos ficariam presos no `/account/login` que
|
|
15
|
+
* deixou de existir.
|
|
16
|
+
*
|
|
17
|
+
* Default `/account/login` (back-compat total: hosts que não passam a opção
|
|
18
|
+
* seguem redirecionando para a tela montada de sempre).
|
|
19
|
+
*
|
|
20
|
+
* Por que módulo singleton e não binding do container? Mesma razão do
|
|
21
|
+
* `admin_prefix`: os guards são closures construídas em tempo de registro,
|
|
22
|
+
* ANTES da primeira request, quando o container ainda não existe. Um módulo ESM
|
|
23
|
+
* é inicializado uma vez por processo — ideal para configuração imutável de boot.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Define a URL de login do console de conta para este processo.
|
|
27
|
+
* Chamado UMA VEZ por `registerAuthHost` no boot da aplicação.
|
|
28
|
+
*
|
|
29
|
+
* Não normaliza o path: aceita qualquer caminho interno que o host queira
|
|
30
|
+
* (ex.: `'/login'`, `'/auth/entrar'`). Valor vazio/whitespace volta a derivar
|
|
31
|
+
* de `accountPath('login')`.
|
|
32
|
+
*
|
|
33
|
+
* @param url Caminho de destino do redirect de não-autenticado.
|
|
34
|
+
*/
|
|
35
|
+
export declare function setAccountLoginUrl(url: string): void;
|
|
36
|
+
/**
|
|
37
|
+
* Retorna a URL de login do console de conta. Sem override explícito, deriva de
|
|
38
|
+
* `accountPath('login')` (default `'/account/login'`; `'/conta/login'` com
|
|
39
|
+
* `accountRoutes: { prefix: '/conta' }`). Usada por todo redirect/link de
|
|
40
|
+
* "faça login" da lib.
|
|
41
|
+
*/
|
|
42
|
+
export declare function getAccountLoginUrl(): string;
|
|
43
|
+
/** Restaura o default — uso em testes (isola o singleton entre casos). */
|
|
44
|
+
export declare function resetAccountLoginUrl(): void;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL de login do console de conta — singleton de processo.
|
|
3
|
+
*
|
|
4
|
+
* Definida em tempo de registro das rotas (`registerAuthHost`, via a opção
|
|
5
|
+
* `accountLoginUrl`) e lida em runtime por TODOS os pontos que redirecionam o
|
|
6
|
+
* visitante não-autenticado para "faça login": os guards (`accountGuard`/
|
|
7
|
+
* `adminGuard`), o middleware `AccountAuthMiddleware`, o helper público
|
|
8
|
+
* `consoleLoginUrl()`, os redirects de fallback dos controllers de conta e a
|
|
9
|
+
* view Edge `otp-unlock` (injetada como prop `loginUrl` pelo renderer).
|
|
10
|
+
*
|
|
11
|
+
* Existe porque a tela `account/login` é DESMONTÁVEL (`account: { login: false }`):
|
|
12
|
+
* um host OIDC passwordless não monta o login por senha da lib e aponta o
|
|
13
|
+
* redirect de não-autenticado para a própria rota de login dele (ex.: `/login`).
|
|
14
|
+
* Sem esta indireção, esses destinos ficariam presos no `/account/login` que
|
|
15
|
+
* deixou de existir.
|
|
16
|
+
*
|
|
17
|
+
* Default `/account/login` (back-compat total: hosts que não passam a opção
|
|
18
|
+
* seguem redirecionando para a tela montada de sempre).
|
|
19
|
+
*
|
|
20
|
+
* Por que módulo singleton e não binding do container? Mesma razão do
|
|
21
|
+
* `admin_prefix`: os guards são closures construídas em tempo de registro,
|
|
22
|
+
* ANTES da primeira request, quando o container ainda não existe. Um módulo ESM
|
|
23
|
+
* é inicializado uma vez por processo — ideal para configuração imutável de boot.
|
|
24
|
+
*/
|
|
25
|
+
import { accountPath } from './account_paths.js';
|
|
26
|
+
/**
|
|
27
|
+
* `undefined` = não configurado explicitamente → deriva de `accountPath('login')`
|
|
28
|
+
* (que respeita o prefixo/segmento configurados via `accountRoutes`). Default
|
|
29
|
+
* efetivo `'/account/login'` — back-compat total.
|
|
30
|
+
*/
|
|
31
|
+
let _loginUrl;
|
|
32
|
+
/**
|
|
33
|
+
* Define a URL de login do console de conta para este processo.
|
|
34
|
+
* Chamado UMA VEZ por `registerAuthHost` no boot da aplicação.
|
|
35
|
+
*
|
|
36
|
+
* Não normaliza o path: aceita qualquer caminho interno que o host queira
|
|
37
|
+
* (ex.: `'/login'`, `'/auth/entrar'`). Valor vazio/whitespace volta a derivar
|
|
38
|
+
* de `accountPath('login')`.
|
|
39
|
+
*
|
|
40
|
+
* @param url Caminho de destino do redirect de não-autenticado.
|
|
41
|
+
*/
|
|
42
|
+
export function setAccountLoginUrl(url) {
|
|
43
|
+
const trimmed = (url ?? '').trim();
|
|
44
|
+
_loginUrl = trimmed || undefined;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Retorna a URL de login do console de conta. Sem override explícito, deriva de
|
|
48
|
+
* `accountPath('login')` (default `'/account/login'`; `'/conta/login'` com
|
|
49
|
+
* `accountRoutes: { prefix: '/conta' }`). Usada por todo redirect/link de
|
|
50
|
+
* "faça login" da lib.
|
|
51
|
+
*/
|
|
52
|
+
export function getAccountLoginUrl() {
|
|
53
|
+
return _loginUrl ?? accountPath('login');
|
|
54
|
+
}
|
|
55
|
+
/** Restaura o default — uso em testes (isola o singleton entre casos). */
|
|
56
|
+
export function resetAccountLoginUrl() {
|
|
57
|
+
_loginUrl = undefined;
|
|
58
|
+
}
|