@adonis-agora/authkit-server 0.47.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/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 +20 -11
- package/build/services/main.d.ts +1 -1
- package/build/services/main.js +2 -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 +6 -3
- package/build/src/host/account_login_url.js +16 -8
- 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.js +1 -3
- 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.js +1 -1
- 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 +47 -27
- package/build/src/host/controllers/account_security_controller.d.ts +2 -2
- package/build/src/host/controllers/account_security_controller.js +114 -115
- package/build/src/host/controllers/account_session_controller.js +5 -5
- package/build/src/host/controllers/account_tokens_controller.js +3 -2
- package/build/src/host/controllers/interaction_controller.js +59 -22
- 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/otp_lockout.js +1 -3
- package/build/src/host/rate_limit.js +1 -1
- package/build/src/host/register_auth_host.d.ts +30 -0
- package/build/src/host/register_auth_host.js +120 -74
- package/build/src/host/renderers/edge_renderer.js +15 -2
- package/build/src/host/renderers/inertia_renderer.d.ts +9 -1
- package/build/src/host/renderers/inertia_renderer.js +9 -1
- 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
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
9
9
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
10
10
|
<link href="https://fonts.googleapis.com/css2?family=Sora:wght@400;500;600;700&family=JetBrains+Mono:ital,wght@0,400;0,500;0,600;1,400&display=swap" rel="stylesheet">
|
|
11
|
-
<script type="module" crossorigin src="/__AUTHKIT_BASE__/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/__AUTHKIT_BASE__/assets/index-BQoTDiLV.js"></script>
|
|
12
12
|
<link rel="stylesheet" crossorigin href="/__AUTHKIT_BASE__/assets/index-DTSmD4RU.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { generateKeyPair, exportJWK } from 'jose';
|
|
2
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { exportJWK, generateKeyPair } from 'jose';
|
|
3
3
|
/** Gera um JWKS privado (1 chave) pronto para `oidc-provider`. */
|
|
4
4
|
export async function generateJwks(alg) {
|
|
5
5
|
const { privateKey } = await generateKeyPair(alg, { extractable: true });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { generateKeyPair, exportJWK } from 'jose';
|
|
2
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { exportJWK, generateKeyPair } from 'jose';
|
|
3
3
|
/** Gera uma chave de assinatura privada como JWK (com use/alg/kid + carimbo de criação). */
|
|
4
4
|
export async function generateSigningJwk(alg) {
|
|
5
5
|
const { privateKey } = await generateKeyPair(alg, { extractable: true });
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import type { KeystoreStoreConfig } from '@adonis-agora/authkit-core';
|
|
2
|
+
import type { SigningAlg } from './jwks_manager.js';
|
|
1
3
|
import { type PersistedKeystore, type RotationPlan } from './keystore.js';
|
|
2
4
|
import type { KeystoreCodec } from './keystore_codec.js';
|
|
3
5
|
import { type KeystoreVault } from './keystore_vault.js';
|
|
4
|
-
import type { SigningAlg } from './jwks_manager.js';
|
|
5
|
-
import type { KeystoreStoreConfig } from '@adonis-agora/authkit-core';
|
|
6
6
|
/**
|
|
7
7
|
* Único caminho de I/O do keystore managed: compõe um {@link KeystoreVault} (onde
|
|
8
8
|
* o blob mora) com um {@link KeystoreCodec} (serialização + encryption). As helpers
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { generateSigningJwk, planRotation } from './keystore.js';
|
|
2
|
-
import { FileKeystoreVault,
|
|
1
|
+
import { generateSigningJwk, planRotation, } from './keystore.js';
|
|
2
|
+
import { DriveKeystoreVault, FileKeystoreVault, HashicorpVaultKeystoreVault, LazyExternalVault, LucidKeystoreVault, RedisKeystoreVault, } from './keystore_vault.js';
|
|
3
3
|
/**
|
|
4
4
|
* Único caminho de I/O do keystore managed: compõe um {@link KeystoreVault} (onde
|
|
5
5
|
* o blob mora) com um {@link KeystoreCodec} (serialização + encryption). As helpers
|
|
@@ -19,7 +19,7 @@ export class FileKeystoreVault {
|
|
|
19
19
|
}
|
|
20
20
|
async write(blob) {
|
|
21
21
|
mkdirSync(dirname(this.path), { recursive: true });
|
|
22
|
-
writeFileSync(this.path, blob.endsWith('\n') ? blob : blob
|
|
22
|
+
writeFileSync(this.path, blob.endsWith('\n') ? blob : `${blob}\n`, { mode: 0o600 });
|
|
23
23
|
}
|
|
24
24
|
async head() {
|
|
25
25
|
return existsSync(this.path) ? String(statSync(this.path).mtimeMs) : null;
|
|
@@ -148,8 +148,12 @@ export class HashicorpVaultKeystoreVault {
|
|
|
148
148
|
h['x-vault-token'] = this.#token;
|
|
149
149
|
return h;
|
|
150
150
|
}
|
|
151
|
-
#dataUrl() {
|
|
152
|
-
|
|
151
|
+
#dataUrl() {
|
|
152
|
+
return `${this.#endpoint}/v1/${this.#mount}/data/${this.#path}`;
|
|
153
|
+
}
|
|
154
|
+
#metaUrl() {
|
|
155
|
+
return `${this.#endpoint}/v1/${this.#mount}/metadata/${this.#path}`;
|
|
156
|
+
}
|
|
153
157
|
async read() {
|
|
154
158
|
const res = await this.#fetch(this.#dataUrl(), { method: 'GET', headers: this.#headers() });
|
|
155
159
|
if (res.status === 404)
|
|
@@ -256,7 +260,7 @@ export class DriveKeystoreVault {
|
|
|
256
260
|
const disk = await this.disk();
|
|
257
261
|
try {
|
|
258
262
|
const meta = await disk.getMetaData(this.key);
|
|
259
|
-
return meta?.etag ?? (meta?.lastModified ? String(new Date(meta.lastModified).getTime()) : null);
|
|
263
|
+
return (meta?.etag ?? (meta?.lastModified ? String(new Date(meta.lastModified).getTime()) : null));
|
|
260
264
|
}
|
|
261
265
|
catch {
|
|
262
266
|
return null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { BaseModel } from '@adonisjs/lucid/orm';
|
|
2
1
|
import type { NormalizeConstructor } from '@adonisjs/core/types/helpers';
|
|
2
|
+
import { BaseModel } from '@adonisjs/lucid/orm';
|
|
3
3
|
import { DateTime } from 'luxon';
|
|
4
4
|
import type { AuditEventType } from '../audit/audit_sink.js';
|
|
5
5
|
/** Instância composta pelo mixin {@link withAuditLog}. */
|
|
@@ -4,8 +4,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { column, beforeSave } from '@adonisjs/lucid/orm';
|
|
8
7
|
import { Scrypt } from '@adonisjs/core/hash/drivers/scrypt';
|
|
8
|
+
import { beforeSave, column } from '@adonisjs/lucid/orm';
|
|
9
9
|
import { jsonColumn } from './json_column.js';
|
|
10
10
|
const hasher = new Scrypt({});
|
|
11
11
|
export function withAuthUser() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { BaseModel } from '@adonisjs/lucid/orm';
|
|
2
1
|
import type { NormalizeConstructor } from '@adonisjs/core/types/helpers';
|
|
2
|
+
import { BaseModel } from '@adonisjs/lucid/orm';
|
|
3
3
|
import { DateTime } from 'luxon';
|
|
4
4
|
/**
|
|
5
5
|
* Instância composta pelo mixin {@link withCredentials}.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { BaseModel } from '@adonisjs/lucid/orm';
|
|
2
1
|
import type { NormalizeConstructor } from '@adonisjs/core/types/helpers';
|
|
2
|
+
import { BaseModel } from '@adonisjs/lucid/orm';
|
|
3
3
|
import { DateTime } from 'luxon';
|
|
4
4
|
/**
|
|
5
5
|
* @deprecated O estado de MFA deixou de viver no model do host. Estas propriedades
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { BaseModel } from '@adonisjs/lucid/orm';
|
|
2
1
|
import type { NormalizeConstructor } from '@adonisjs/core/types/helpers';
|
|
2
|
+
import { BaseModel } from '@adonisjs/lucid/orm';
|
|
3
3
|
import { DateTime } from 'luxon';
|
|
4
4
|
/** Instância composta pelo mixin {@link withPersonalAccessToken}. */
|
|
5
5
|
export interface PersonalAccessTokenRow {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { BaseModel } from '@adonisjs/lucid/orm';
|
|
2
1
|
import type { NormalizeConstructor } from '@adonisjs/core/types/helpers';
|
|
2
|
+
import { BaseModel } from '@adonisjs/lucid/orm';
|
|
3
3
|
import { DateTime } from 'luxon';
|
|
4
4
|
/**
|
|
5
5
|
* Instância composta pelo mixin {@link withProviderIdentity}. Liga uma conta
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { BaseModel } from '@adonisjs/lucid/orm';
|
|
2
1
|
import type { NormalizeConstructor } from '@adonisjs/core/types/helpers';
|
|
2
|
+
import { BaseModel } from '@adonisjs/lucid/orm';
|
|
3
3
|
import { DateTime } from 'luxon';
|
|
4
4
|
/**
|
|
5
5
|
* Instância composta pelo mixin {@link withWebauthnCredential}. Representa uma
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* `onDiagnostic('authkit', …)`) registra quando presente. Nada aqui pode lançar
|
|
11
11
|
* para dentro do caminho de auth.
|
|
12
12
|
*/
|
|
13
|
-
const EMIT_SLOT = Symbol.for(
|
|
13
|
+
const EMIT_SLOT = Symbol.for('@agora/diagnostics:emit');
|
|
14
14
|
/**
|
|
15
15
|
* Republica `event` no barramento de diagnostics como `agora:authkit:<event>`
|
|
16
16
|
* (o canal/diagnostics suffix É o nome do evento — sem tabela de mapeamento).
|
|
@@ -19,7 +19,7 @@ const EMIT_SLOT = Symbol.for("@agora/diagnostics:emit");
|
|
|
19
19
|
export function emitDiagnostic(event, payload) {
|
|
20
20
|
try {
|
|
21
21
|
const emit = globalThis[EMIT_SLOT];
|
|
22
|
-
emit?.(
|
|
22
|
+
emit?.('authkit', event, payload);
|
|
23
23
|
}
|
|
24
24
|
catch {
|
|
25
25
|
// best-effort: a ponte de diagnostics nunca quebra o caminho de auth.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NoopRecorder } from '@adonis-agora/authkit-core';
|
|
1
|
+
import { NoopRecorder, } from '@adonis-agora/authkit-core';
|
|
2
2
|
import { OtelRecorder } from './otel_recorder.js';
|
|
3
3
|
export async function createMetricsRecorder(observability, meterName) {
|
|
4
4
|
if (!observability.metrics)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DataProvider } from
|
|
1
|
+
import type { DataProvider } from '@adonis-agora/telescope';
|
|
2
2
|
/**
|
|
3
3
|
* A stat (`{ value, delta?, spark? }`) over a window for a set of event types.
|
|
4
4
|
* `query.metric` chooses the grouping; `query.windowMs` the window (default 24h).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** The Telescope entry `type` the generic diagnostics watcher records under. */
|
|
2
|
-
const DIAGNOSTIC_TYPE =
|
|
2
|
+
const DIAGNOSTIC_TYPE = 'diagnostic';
|
|
3
3
|
/** The tag the diagnostics watcher stamps for authkit-emitted events. */
|
|
4
|
-
const AUTHKIT_TAG =
|
|
4
|
+
const AUTHKIT_TAG = 'lib:authkit';
|
|
5
5
|
/** Default rolling window for the entry-backed providers (24h). */
|
|
6
6
|
const DEFAULT_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
7
7
|
/** Fetch captured `agora:authkit:*` audit entries from Telescope (newest-first). */
|
|
@@ -14,7 +14,7 @@ async function fetchEntries(ctx, limit = 5_000) {
|
|
|
14
14
|
}
|
|
15
15
|
/** The `AuditEventType` an entry carries (under `content.event`), or `''`. */
|
|
16
16
|
function eventOf(e) {
|
|
17
|
-
return e.content?.event ??
|
|
17
|
+
return e.content?.event ?? '';
|
|
18
18
|
}
|
|
19
19
|
/** Epoch millis the entry was recorded at, or `0` when absent/unparsable. */
|
|
20
20
|
function timeOf(e) {
|
|
@@ -39,32 +39,19 @@ function splitWindows(entries, windowMs, now) {
|
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
// The audit event groupings the dashboard rolls up.
|
|
42
|
-
const LOGIN_SUCCESS = [
|
|
43
|
-
const LOGIN_FAILURE = [
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
];
|
|
47
|
-
const
|
|
48
|
-
"mfa.enabled",
|
|
49
|
-
"passkey.registered",
|
|
50
|
-
];
|
|
51
|
-
const LOCKOUTS = ["account.locked", "otp.locked"];
|
|
52
|
-
const PAT_EVENTS = [
|
|
53
|
-
"pat.issued",
|
|
54
|
-
"pat.revoked",
|
|
55
|
-
"pat.used",
|
|
56
|
-
];
|
|
57
|
-
const IMPERSONATION_EVENTS = [
|
|
58
|
-
"impersonation",
|
|
59
|
-
"impersonation.started",
|
|
60
|
-
];
|
|
42
|
+
const LOGIN_SUCCESS = ['login.success'];
|
|
43
|
+
const LOGIN_FAILURE = ['login.failure', 'bot_protection.rejected'];
|
|
44
|
+
const MFA_ENROLLMENTS = ['mfa.enabled', 'passkey.registered'];
|
|
45
|
+
const LOCKOUTS = ['account.locked', 'otp.locked'];
|
|
46
|
+
const PAT_EVENTS = ['pat.issued', 'pat.revoked', 'pat.used'];
|
|
47
|
+
const IMPERSONATION_EVENTS = ['impersonation', 'impersonation.started'];
|
|
61
48
|
/**
|
|
62
49
|
* A stat (`{ value, delta?, spark? }`) over a window for a set of event types.
|
|
63
50
|
* `query.metric` chooses the grouping; `query.windowMs` the window (default 24h).
|
|
64
51
|
*/
|
|
65
52
|
export function authkitEventCountProvider() {
|
|
66
53
|
return {
|
|
67
|
-
name:
|
|
54
|
+
name: 'authkit.eventCount',
|
|
68
55
|
async resolve(query, ctx) {
|
|
69
56
|
const entries = await fetchEntries(ctx);
|
|
70
57
|
const windowMs = Number(query?.windowMs ?? DEFAULT_WINDOW_MS);
|
|
@@ -91,17 +78,16 @@ export function authkitEventCountProvider() {
|
|
|
91
78
|
/** Map a panel `metric` to the set of `AuditEventType`s it aggregates. */
|
|
92
79
|
function typesForMetric(metric) {
|
|
93
80
|
switch (metric) {
|
|
94
|
-
case
|
|
81
|
+
case 'loginFailure':
|
|
95
82
|
return LOGIN_FAILURE;
|
|
96
|
-
case
|
|
83
|
+
case 'mfaEnrollments':
|
|
97
84
|
return MFA_ENROLLMENTS;
|
|
98
|
-
case
|
|
85
|
+
case 'lockouts':
|
|
99
86
|
return LOCKOUTS;
|
|
100
|
-
case
|
|
87
|
+
case 'pat':
|
|
101
88
|
return PAT_EVENTS;
|
|
102
|
-
case
|
|
89
|
+
case 'impersonation':
|
|
103
90
|
return IMPERSONATION_EVENTS;
|
|
104
|
-
case "loginSuccess":
|
|
105
91
|
default:
|
|
106
92
|
return LOGIN_SUCCESS;
|
|
107
93
|
}
|
|
@@ -112,7 +98,7 @@ function typesForMetric(metric) {
|
|
|
112
98
|
*/
|
|
113
99
|
export function authkitLoginSuccessRateProvider() {
|
|
114
100
|
return {
|
|
115
|
-
name:
|
|
101
|
+
name: 'authkit.loginSuccessRate',
|
|
116
102
|
async resolve(query, ctx) {
|
|
117
103
|
const entries = await fetchEntries(ctx);
|
|
118
104
|
const windowMs = Number(query?.windowMs ?? DEFAULT_WINDOW_MS);
|
|
@@ -130,7 +116,7 @@ export function authkitLoginSuccessRateProvider() {
|
|
|
130
116
|
*/
|
|
131
117
|
export function authkitLoginsOverTimeProvider() {
|
|
132
118
|
return {
|
|
133
|
-
name:
|
|
119
|
+
name: 'authkit.loginsOverTime',
|
|
134
120
|
async resolve(query, ctx) {
|
|
135
121
|
const entries = await fetchEntries(ctx);
|
|
136
122
|
const n = Math.max(1, Number(query?.buckets ?? 24));
|
|
@@ -170,16 +156,16 @@ export function authkitLoginsOverTimeProvider() {
|
|
|
170
156
|
}
|
|
171
157
|
/** Donut/bar `breakdown` segments — a count per top-level audit event family. */
|
|
172
158
|
const BREAKDOWN_GROUPS = [
|
|
173
|
-
{ label:
|
|
174
|
-
{ label:
|
|
175
|
-
{ label:
|
|
176
|
-
{ label:
|
|
177
|
-
{ label:
|
|
178
|
-
{ label:
|
|
159
|
+
{ label: 'Login OK', types: LOGIN_SUCCESS, color: '#34d399' },
|
|
160
|
+
{ label: 'Login fail', types: LOGIN_FAILURE, color: '#f87171' },
|
|
161
|
+
{ label: 'MFA', types: MFA_ENROLLMENTS, color: '#38bdf8' },
|
|
162
|
+
{ label: 'Lockouts', types: LOCKOUTS, color: '#fbbf24' },
|
|
163
|
+
{ label: 'PAT', types: PAT_EVENTS, color: '#a78bfa' },
|
|
164
|
+
{ label: 'Impersonation', types: IMPERSONATION_EVENTS, color: '#fb923c' },
|
|
179
165
|
];
|
|
180
166
|
export function authkitEventBreakdownProvider() {
|
|
181
167
|
return {
|
|
182
|
-
name:
|
|
168
|
+
name: 'authkit.eventBreakdown',
|
|
183
169
|
async resolve(query, ctx) {
|
|
184
170
|
const entries = await fetchEntries(ctx);
|
|
185
171
|
const windowMs = Number(query?.windowMs ?? DEFAULT_WINDOW_MS);
|
|
@@ -199,7 +185,7 @@ export function authkitEventBreakdownProvider() {
|
|
|
199
185
|
*/
|
|
200
186
|
export function authkitTokenActivityProvider() {
|
|
201
187
|
return {
|
|
202
|
-
name:
|
|
188
|
+
name: 'authkit.tokenActivity',
|
|
203
189
|
async resolve(query, ctx) {
|
|
204
190
|
const entries = await fetchEntries(ctx);
|
|
205
191
|
const limit = Math.min(200, Math.max(10, Number(query?.limit ?? 50)));
|
|
@@ -211,11 +197,11 @@ export function authkitTokenActivityProvider() {
|
|
|
211
197
|
const p = e.content?.payload;
|
|
212
198
|
return {
|
|
213
199
|
at: e.createdAt
|
|
214
|
-
? `${new Date(e.createdAt).toISOString().replace(
|
|
215
|
-
:
|
|
200
|
+
? `${new Date(e.createdAt).toISOString().replace('T', ' ').slice(0, 16)}Z`
|
|
201
|
+
: '',
|
|
216
202
|
event: eventOf(e),
|
|
217
|
-
subject: p?.accountId ??
|
|
218
|
-
actor: p?.actorId ??
|
|
203
|
+
subject: p?.accountId ?? '',
|
|
204
|
+
actor: p?.actorId ?? '',
|
|
219
205
|
// `ip` is intentionally NOT surfaced: the diagnostics bridge emits a
|
|
220
206
|
// PII-free projection of each audit event (no `email`/`ip`/`metadata`)
|
|
221
207
|
// so a deleted account's PII never lands in Telescope's store. See
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { authkitDataProviders } from
|
|
1
|
+
import { authkitDataProviders } from './data_providers.js';
|
|
2
2
|
/**
|
|
3
3
|
* The navigable entry type the extension contributes: it scopes Telescope's
|
|
4
4
|
* entry-type nav to authkit's audit events (recorded as `diagnostic` entries
|
|
5
5
|
* tagged `lib:authkit`).
|
|
6
6
|
*/
|
|
7
7
|
function authkitEntryType() {
|
|
8
|
-
return { id:
|
|
8
|
+
return { id: 'authkit', label: 'Auth', dot: 'bg-emerald-400' };
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
11
|
* The authkit "Security" dashboard — a golden-signals layout for auth: login
|
|
@@ -16,123 +16,123 @@ function authkitEntryType() {
|
|
|
16
16
|
function authkitSecurityDashboard(opts) {
|
|
17
17
|
const windowMs = opts.windowMs ?? 24 * 60 * 60 * 1000;
|
|
18
18
|
return {
|
|
19
|
-
id:
|
|
20
|
-
label:
|
|
21
|
-
navGroup:
|
|
19
|
+
id: 'authkit.security',
|
|
20
|
+
label: 'Security',
|
|
21
|
+
navGroup: 'Auth',
|
|
22
22
|
panels: [],
|
|
23
23
|
sections: [
|
|
24
24
|
{
|
|
25
|
-
title:
|
|
25
|
+
title: 'Logins',
|
|
26
26
|
cols: 4,
|
|
27
27
|
panels: [
|
|
28
28
|
{
|
|
29
|
-
kind:
|
|
30
|
-
title:
|
|
31
|
-
data: { provider:
|
|
29
|
+
kind: 'gauge',
|
|
30
|
+
title: 'Login success rate',
|
|
31
|
+
data: { provider: 'authkit.loginSuccessRate', query: { windowMs } },
|
|
32
32
|
max: 1,
|
|
33
|
-
format:
|
|
34
|
-
thresholds: { warn: 0.9, bad: 0.75, direction:
|
|
33
|
+
format: 'percent',
|
|
34
|
+
thresholds: { warn: 0.9, bad: 0.75, direction: 'down-bad' },
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
|
-
kind:
|
|
38
|
-
title:
|
|
37
|
+
kind: 'stat',
|
|
38
|
+
title: 'Successful logins',
|
|
39
39
|
data: {
|
|
40
|
-
provider:
|
|
41
|
-
query: { metric:
|
|
40
|
+
provider: 'authkit.eventCount',
|
|
41
|
+
query: { metric: 'loginSuccess', windowMs },
|
|
42
42
|
},
|
|
43
43
|
spark: true,
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
|
-
kind:
|
|
47
|
-
title:
|
|
46
|
+
kind: 'stat',
|
|
47
|
+
title: 'Failed logins',
|
|
48
48
|
data: {
|
|
49
|
-
provider:
|
|
50
|
-
query: { metric:
|
|
49
|
+
provider: 'authkit.eventCount',
|
|
50
|
+
query: { metric: 'loginFailure', windowMs },
|
|
51
51
|
},
|
|
52
52
|
spark: true,
|
|
53
|
-
thresholds: { warn: 25, bad: 100, direction:
|
|
53
|
+
thresholds: { warn: 25, bad: 100, direction: 'up-bad' },
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
|
-
kind:
|
|
57
|
-
title:
|
|
56
|
+
kind: 'stat',
|
|
57
|
+
title: 'Account lockouts',
|
|
58
58
|
data: {
|
|
59
|
-
provider:
|
|
60
|
-
query: { metric:
|
|
59
|
+
provider: 'authkit.eventCount',
|
|
60
|
+
query: { metric: 'lockouts', windowMs },
|
|
61
61
|
},
|
|
62
62
|
spark: true,
|
|
63
|
-
thresholds: { warn: 5, bad: 25, direction:
|
|
63
|
+
thresholds: { warn: 5, bad: 25, direction: 'up-bad' },
|
|
64
64
|
},
|
|
65
65
|
],
|
|
66
66
|
},
|
|
67
67
|
{
|
|
68
|
-
title:
|
|
68
|
+
title: 'MFA & tokens',
|
|
69
69
|
cols: 3,
|
|
70
70
|
panels: [
|
|
71
71
|
{
|
|
72
|
-
kind:
|
|
73
|
-
title:
|
|
72
|
+
kind: 'stat',
|
|
73
|
+
title: 'MFA / passkey enrollments',
|
|
74
74
|
data: {
|
|
75
|
-
provider:
|
|
76
|
-
query: { metric:
|
|
75
|
+
provider: 'authkit.eventCount',
|
|
76
|
+
query: { metric: 'mfaEnrollments', windowMs },
|
|
77
77
|
},
|
|
78
78
|
spark: true,
|
|
79
79
|
},
|
|
80
80
|
{
|
|
81
|
-
kind:
|
|
82
|
-
title:
|
|
81
|
+
kind: 'stat',
|
|
82
|
+
title: 'Impersonation events',
|
|
83
83
|
data: {
|
|
84
|
-
provider:
|
|
85
|
-
query: { metric:
|
|
84
|
+
provider: 'authkit.eventCount',
|
|
85
|
+
query: { metric: 'impersonation', windowMs },
|
|
86
86
|
},
|
|
87
87
|
spark: true,
|
|
88
|
-
thresholds: { warn: 1, bad: 10, direction:
|
|
88
|
+
thresholds: { warn: 1, bad: 10, direction: 'up-bad' },
|
|
89
89
|
},
|
|
90
90
|
{
|
|
91
|
-
kind:
|
|
92
|
-
title:
|
|
91
|
+
kind: 'stat',
|
|
92
|
+
title: 'PAT activity',
|
|
93
93
|
data: {
|
|
94
|
-
provider:
|
|
95
|
-
query: { metric:
|
|
94
|
+
provider: 'authkit.eventCount',
|
|
95
|
+
query: { metric: 'pat', windowMs },
|
|
96
96
|
},
|
|
97
97
|
spark: true,
|
|
98
98
|
},
|
|
99
99
|
],
|
|
100
100
|
},
|
|
101
101
|
{
|
|
102
|
-
title:
|
|
102
|
+
title: 'Trends',
|
|
103
103
|
cols: 2,
|
|
104
104
|
panels: [
|
|
105
105
|
{
|
|
106
|
-
kind:
|
|
107
|
-
title:
|
|
108
|
-
data: { provider:
|
|
109
|
-
series: [
|
|
110
|
-
style:
|
|
106
|
+
kind: 'timeseries',
|
|
107
|
+
title: 'Logins over time',
|
|
108
|
+
data: { provider: 'authkit.loginsOverTime' },
|
|
109
|
+
series: ['success', 'failure'],
|
|
110
|
+
style: 'stacked',
|
|
111
111
|
},
|
|
112
112
|
{
|
|
113
|
-
kind:
|
|
114
|
-
title:
|
|
115
|
-
data: { provider:
|
|
116
|
-
style:
|
|
113
|
+
kind: 'breakdown',
|
|
114
|
+
title: 'Events by family',
|
|
115
|
+
data: { provider: 'authkit.eventBreakdown', query: { windowMs } },
|
|
116
|
+
style: 'donut',
|
|
117
117
|
},
|
|
118
118
|
],
|
|
119
119
|
},
|
|
120
120
|
{
|
|
121
|
-
title:
|
|
121
|
+
title: 'PAT & impersonation activity',
|
|
122
122
|
cols: 2,
|
|
123
123
|
panels: [
|
|
124
124
|
{
|
|
125
|
-
kind:
|
|
126
|
-
title:
|
|
127
|
-
data: { provider:
|
|
125
|
+
kind: 'table',
|
|
126
|
+
title: 'Recent token & impersonation events',
|
|
127
|
+
data: { provider: 'authkit.tokenActivity' },
|
|
128
128
|
// No "IP" column: the diagnostics bridge emits a PII-free projection
|
|
129
129
|
// (no `email`/`ip`/`metadata`), so a deleted account's PII never lands
|
|
130
130
|
// in Telescope's store. See `redactAuditEventForDiagnostics`.
|
|
131
131
|
columns: [
|
|
132
|
-
{ key:
|
|
133
|
-
{ key:
|
|
134
|
-
{ key:
|
|
135
|
-
{ key:
|
|
132
|
+
{ key: 'at', label: 'When' },
|
|
133
|
+
{ key: 'event', label: 'Event' },
|
|
134
|
+
{ key: 'subject', label: 'Subject' },
|
|
135
|
+
{ key: 'actor', label: 'Actor' },
|
|
136
136
|
],
|
|
137
137
|
},
|
|
138
138
|
],
|
|
@@ -161,7 +161,7 @@ function authkitSecurityDashboard(opts) {
|
|
|
161
161
|
*/
|
|
162
162
|
export function defineAuthkitTelescopeExtension(opts = {}) {
|
|
163
163
|
return {
|
|
164
|
-
name:
|
|
164
|
+
name: 'authkit',
|
|
165
165
|
entryTypes: () => [authkitEntryType()],
|
|
166
166
|
dashboards: () => [authkitSecurityDashboard(opts)],
|
|
167
167
|
dataProviders: () => authkitDataProviders(),
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* pulls in telescope. Import it ONLY from `config/telescope.ts`, where both
|
|
5
5
|
* `@adonis-agora/telescope` and `@adonis-agora/authkit-server` are installed.
|
|
6
6
|
*/
|
|
7
|
-
export { defineAuthkitTelescopeExtension, type AuthkitTelescopeOptions, } from
|
|
8
|
-
export { authkitDataProviders, authkitEventCountProvider, authkitLoginSuccessRateProvider, authkitLoginsOverTimeProvider, authkitEventBreakdownProvider, authkitTokenActivityProvider, } from
|
|
7
|
+
export { defineAuthkitTelescopeExtension, type AuthkitTelescopeOptions, } from './extension.js';
|
|
8
|
+
export { authkitDataProviders, authkitEventCountProvider, authkitLoginSuccessRateProvider, authkitLoginsOverTimeProvider, authkitEventBreakdownProvider, authkitTokenActivityProvider, } from './data_providers.js';
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* pulls in telescope. Import it ONLY from `config/telescope.ts`, where both
|
|
5
5
|
* `@adonis-agora/telescope` and `@adonis-agora/authkit-server` are installed.
|
|
6
6
|
*/
|
|
7
|
-
export { defineAuthkitTelescopeExtension, } from
|
|
8
|
-
export { authkitDataProviders, authkitEventCountProvider, authkitLoginSuccessRateProvider, authkitLoginsOverTimeProvider, authkitEventBreakdownProvider, authkitTokenActivityProvider, } from
|
|
7
|
+
export { defineAuthkitTelescopeExtension, } from './extension.js';
|
|
8
|
+
export { authkitDataProviders, authkitEventCountProvider, authkitLoginSuccessRateProvider, authkitLoginsOverTimeProvider, authkitEventBreakdownProvider, authkitTokenActivityProvider, } from './data_providers.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type MetricsRecorder } from
|
|
1
|
+
import { type MetricsRecorder } from '@adonis-agora/authkit-core';
|
|
2
2
|
/**
|
|
3
3
|
* Liga eventos do oidc-provider aos counters do recorder. Usa só eventos reais
|
|
4
4
|
* do v9 (verificados em node_modules/oidc-provider/lib):
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AUTHKIT_METRICS
|
|
2
|
-
import { emitDiagnostic } from
|
|
1
|
+
import { AUTHKIT_METRICS } from '@adonis-agora/authkit-core';
|
|
2
|
+
import { emitDiagnostic } from './diagnostics_bridge.js';
|
|
3
3
|
/**
|
|
4
4
|
* Liga eventos do oidc-provider aos counters do recorder. Usa só eventos reais
|
|
5
5
|
* do v9 (verificados em node_modules/oidc-provider/lib):
|
|
@@ -11,20 +11,20 @@ import { emitDiagnostic } from "./diagnostics_bridge.js";
|
|
|
11
11
|
* - `grant.revoked` (helpers/revoke.js) -> grantRevoked
|
|
12
12
|
*/
|
|
13
13
|
export function wireProviderEvents(provider, recorder) {
|
|
14
|
-
provider.on(
|
|
15
|
-
provider.on(
|
|
16
|
-
provider.on(
|
|
14
|
+
provider.on('grant.success', () => recorder.increment(AUTHKIT_METRICS.loginSuccess));
|
|
15
|
+
provider.on('server_error', () => recorder.increment(AUTHKIT_METRICS.loginFailure));
|
|
16
|
+
provider.on('access_token.saved', () => {
|
|
17
17
|
recorder.increment(AUTHKIT_METRICS.tokenIssued);
|
|
18
18
|
// Emit estrutural irmão (best-effort, no-op sem o slot de diagnostics).
|
|
19
|
-
emitDiagnostic(
|
|
19
|
+
emitDiagnostic('token.issued', { kind: 'access_token' });
|
|
20
20
|
});
|
|
21
|
-
provider.on(
|
|
21
|
+
provider.on('access_token.issued', () => {
|
|
22
22
|
recorder.increment(AUTHKIT_METRICS.tokenIssued);
|
|
23
|
-
emitDiagnostic(
|
|
23
|
+
emitDiagnostic('token.issued', { kind: 'access_token' });
|
|
24
24
|
});
|
|
25
|
-
provider.on(
|
|
25
|
+
provider.on('refresh_token.saved', () => {
|
|
26
26
|
recorder.increment(AUTHKIT_METRICS.refreshRotated);
|
|
27
|
-
emitDiagnostic(
|
|
27
|
+
emitDiagnostic('token.refreshed', { kind: 'refresh_token' });
|
|
28
28
|
});
|
|
29
|
-
provider.on(
|
|
29
|
+
provider.on('grant.revoked', () => recorder.increment(AUTHKIT_METRICS.grantRevoked));
|
|
30
30
|
}
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* Licença do arquivo de dados: CC0 / Public Domain.
|
|
13
13
|
*/
|
|
14
14
|
import { readFileSync } from 'node:fs';
|
|
15
|
+
import { dirname, join } from 'node:path';
|
|
15
16
|
import { fileURLToPath } from 'node:url';
|
|
16
|
-
import { join, dirname } from 'node:path';
|
|
17
17
|
/**
|
|
18
18
|
* Set em memória das senhas comuns (minúsculas). Inicializado na primeira
|
|
19
19
|
* chamada a `isCommonPassword`. O módulo garante que o carregamento ocorre
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type PasswordPolicyViolation, type ResolvedPasswordConfig } from './policy.js';
|
|
2
|
-
import { type
|
|
2
|
+
import { type FetchLike, type PwnedLogger } from './pwned.js';
|
|
3
3
|
/**
|
|
4
4
|
* Verificador de hashes legados (de OUTROS sistemas). Chamado quando a
|
|
5
5
|
* verificação nativa do hasher do Adonis falha OU não reconhece o formato do
|