@dudousxd/adonis-authkit-server 0.4.0 → 0.6.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/README.md +23 -2
- package/build/host/views/account/apps.edge +58 -0
- package/build/host/views/account/security.edge +53 -0
- package/build/host/views/account/tokens.edge +1 -0
- package/build/host/views/admin/users.edge +62 -2
- package/build/host/views/login.edge +55 -0
- package/build/host/views/mfa-challenge.edge +12 -0
- package/build/index.d.ts +9 -3
- package/build/index.js +5 -2
- package/build/src/accounts/account_store.d.ts +80 -2
- package/build/src/accounts/account_store.js +12 -0
- package/build/src/accounts/lucid_account_store.js +8 -0
- package/build/src/accounts/lucid_store/core.d.ts +2 -2
- package/build/src/accounts/lucid_store/core.js +33 -0
- package/build/src/accounts/lucid_store/mfa.js +4 -1
- package/build/src/accounts/lucid_store/status_profile.d.ts +21 -0
- package/build/src/accounts/lucid_store/status_profile.js +66 -0
- package/build/src/audit/audit_sink.d.ts +1 -1
- package/build/src/define_config.d.ts +53 -0
- package/build/src/define_config.js +14 -1
- package/build/src/doctor/checks.js +32 -32
- package/build/src/events/dispatcher.d.ts +45 -0
- package/build/src/events/dispatcher.js +92 -0
- package/build/src/host/admin_sessions_service.d.ts +8 -0
- package/build/src/host/admin_sessions_service.js +19 -0
- package/build/src/host/controllers/account_apps_controller.d.ts +15 -0
- package/build/src/host/controllers/account_apps_controller.js +61 -0
- package/build/src/host/controllers/account_mfa_controller.js +6 -2
- package/build/src/host/controllers/account_security_controller.d.ts +9 -0
- package/build/src/host/controllers/account_security_controller.js +52 -2
- package/build/src/host/controllers/account_session_controller.js +3 -1
- package/build/src/host/controllers/admin/admin_users_controller.d.ts +13 -0
- package/build/src/host/controllers/admin/admin_users_controller.js +133 -0
- package/build/src/host/controllers/interaction_controller.d.ts +32 -0
- package/build/src/host/controllers/interaction_controller.js +175 -8
- package/build/src/host/default_mailer.d.ts +8 -0
- package/build/src/host/default_mailer.js +81 -19
- package/build/src/host/email_templates.d.ts +4 -0
- package/build/src/host/email_templates.js +5 -2
- package/build/src/host/i18n.d.ts +395 -11
- package/build/src/host/i18n.js +433 -12
- package/build/src/host/login_attempt.d.ts +1 -0
- package/build/src/host/login_attempt.js +11 -0
- package/build/src/host/register_auth_host.js +18 -1
- package/build/src/host/trusted_device.d.ts +61 -0
- package/build/src/host/trusted_device.js +65 -0
- package/build/src/host/validators.d.ts +35 -0
- package/build/src/host/validators.js +14 -0
- package/build/src/observability/metrics_controller.js +4 -4
- package/package.json +1 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Trusted devices" — pular o 2º fator (MFA) neste dispositivo por N dias.
|
|
3
|
+
*
|
|
4
|
+
* Mecanismo SEM novos requisitos de DB: um cookie httpOnly assinado/encriptado
|
|
5
|
+
* com a appKey do host (via `response.encryptedCookie` / `request.encryptedCookie`,
|
|
6
|
+
* que são appKey-backed). O cookie carrega `{ a: accountId, d: deviceId, iat, exp }`.
|
|
7
|
+
*
|
|
8
|
+
* Validação (ver {@link isTrustedDeviceValid}):
|
|
9
|
+
* - `exp` ainda no futuro;
|
|
10
|
+
* - `a` casa com a conta que acabou de passar pela senha;
|
|
11
|
+
* - `iat >= mfaEnabledAt` — re-enrolar o MFA invalida cookies antigos (revogação
|
|
12
|
+
* por re-enrollment, sem estado server-side).
|
|
13
|
+
*
|
|
14
|
+
* Step-up (acr_values pedindo o mfaAcr) SEMPRE ignora o cookie e força o MFA — a
|
|
15
|
+
* decisão fica no controller, antes de checar o cookie.
|
|
16
|
+
*
|
|
17
|
+
* Limitação conhecida (documentada): NÃO há uma lista de revogação por-dispositivo
|
|
18
|
+
* server-side; a revogação disponível é "revogar todos" via re-enrollment do MFA.
|
|
19
|
+
* Uma allowlist/denylist persistida fica como trabalho futuro.
|
|
20
|
+
*/
|
|
21
|
+
/** Nome do cookie de dispositivo confiável. */
|
|
22
|
+
export declare const TRUSTED_DEVICE_COOKIE = "authkit_trusted_device";
|
|
23
|
+
/** Payload guardado (encriptado) no cookie de dispositivo confiável. */
|
|
24
|
+
export interface TrustedDevicePayload {
|
|
25
|
+
/** accountId ao qual a confiança pertence. */
|
|
26
|
+
a: string;
|
|
27
|
+
/** id opaco do dispositivo (para futura revogação por-dispositivo). */
|
|
28
|
+
d: string;
|
|
29
|
+
/** issued-at (epoch ms). */
|
|
30
|
+
iat: number;
|
|
31
|
+
/** expiry (epoch ms). */
|
|
32
|
+
exp: number;
|
|
33
|
+
}
|
|
34
|
+
export interface TrustedDevicesConfigInput {
|
|
35
|
+
/** Liga o mecanismo de trusted devices. Default: true. */
|
|
36
|
+
enabled?: boolean;
|
|
37
|
+
/** Validade da confiança em dias. Default: 30. */
|
|
38
|
+
days?: number;
|
|
39
|
+
}
|
|
40
|
+
export interface ResolvedTrustedDevicesConfig {
|
|
41
|
+
enabled: boolean;
|
|
42
|
+
days: number;
|
|
43
|
+
}
|
|
44
|
+
export declare function resolveTrustedDevices(input?: TrustedDevicesConfigInput): ResolvedTrustedDevicesConfig;
|
|
45
|
+
/** Constrói o payload de um novo cookie de confiança para a conta. */
|
|
46
|
+
export declare function buildTrustedDevicePayload(accountId: string, cfg: ResolvedTrustedDevicesConfig, now?: number): TrustedDevicePayload;
|
|
47
|
+
/**
|
|
48
|
+
* `true` se o payload do cookie é uma confiança VÁLIDA para `accountId`:
|
|
49
|
+
* - estrutura íntegra;
|
|
50
|
+
* - pertence à conta certa;
|
|
51
|
+
* - não expirou;
|
|
52
|
+
* - foi emitido em/depois do último (re)enrollment de MFA (`mfaEnabledAt`).
|
|
53
|
+
*
|
|
54
|
+
* `mfaEnabledAt` em epoch ms (ou null quando o store não rastreia — nesse caso a
|
|
55
|
+
* checagem de re-enrollment é pulada, mantendo a validade por expiração apenas).
|
|
56
|
+
*/
|
|
57
|
+
export declare function isTrustedDeviceValid(payload: unknown, opts: {
|
|
58
|
+
accountId: string;
|
|
59
|
+
mfaEnabledAt?: number | null;
|
|
60
|
+
now?: number;
|
|
61
|
+
}): boolean;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* "Trusted devices" — pular o 2º fator (MFA) neste dispositivo por N dias.
|
|
4
|
+
*
|
|
5
|
+
* Mecanismo SEM novos requisitos de DB: um cookie httpOnly assinado/encriptado
|
|
6
|
+
* com a appKey do host (via `response.encryptedCookie` / `request.encryptedCookie`,
|
|
7
|
+
* que são appKey-backed). O cookie carrega `{ a: accountId, d: deviceId, iat, exp }`.
|
|
8
|
+
*
|
|
9
|
+
* Validação (ver {@link isTrustedDeviceValid}):
|
|
10
|
+
* - `exp` ainda no futuro;
|
|
11
|
+
* - `a` casa com a conta que acabou de passar pela senha;
|
|
12
|
+
* - `iat >= mfaEnabledAt` — re-enrolar o MFA invalida cookies antigos (revogação
|
|
13
|
+
* por re-enrollment, sem estado server-side).
|
|
14
|
+
*
|
|
15
|
+
* Step-up (acr_values pedindo o mfaAcr) SEMPRE ignora o cookie e força o MFA — a
|
|
16
|
+
* decisão fica no controller, antes de checar o cookie.
|
|
17
|
+
*
|
|
18
|
+
* Limitação conhecida (documentada): NÃO há uma lista de revogação por-dispositivo
|
|
19
|
+
* server-side; a revogação disponível é "revogar todos" via re-enrollment do MFA.
|
|
20
|
+
* Uma allowlist/denylist persistida fica como trabalho futuro.
|
|
21
|
+
*/
|
|
22
|
+
/** Nome do cookie de dispositivo confiável. */
|
|
23
|
+
export const TRUSTED_DEVICE_COOKIE = 'authkit_trusted_device';
|
|
24
|
+
export function resolveTrustedDevices(input) {
|
|
25
|
+
return {
|
|
26
|
+
enabled: input?.enabled ?? true,
|
|
27
|
+
days: input?.days && input.days > 0 ? input.days : 30,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** Constrói o payload de um novo cookie de confiança para a conta. */
|
|
31
|
+
export function buildTrustedDevicePayload(accountId, cfg, now = Date.now()) {
|
|
32
|
+
return {
|
|
33
|
+
a: accountId,
|
|
34
|
+
d: randomBytes(16).toString('hex'),
|
|
35
|
+
iat: now,
|
|
36
|
+
exp: now + cfg.days * 24 * 60 * 60 * 1000,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* `true` se o payload do cookie é uma confiança VÁLIDA para `accountId`:
|
|
41
|
+
* - estrutura íntegra;
|
|
42
|
+
* - pertence à conta certa;
|
|
43
|
+
* - não expirou;
|
|
44
|
+
* - foi emitido em/depois do último (re)enrollment de MFA (`mfaEnabledAt`).
|
|
45
|
+
*
|
|
46
|
+
* `mfaEnabledAt` em epoch ms (ou null quando o store não rastreia — nesse caso a
|
|
47
|
+
* checagem de re-enrollment é pulada, mantendo a validade por expiração apenas).
|
|
48
|
+
*/
|
|
49
|
+
export function isTrustedDeviceValid(payload, opts) {
|
|
50
|
+
const now = opts.now ?? Date.now();
|
|
51
|
+
if (!payload || typeof payload !== 'object')
|
|
52
|
+
return false;
|
|
53
|
+
const p = payload;
|
|
54
|
+
if (typeof p.a !== 'string' || typeof p.iat !== 'number' || typeof p.exp !== 'number') {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
if (p.a !== opts.accountId)
|
|
58
|
+
return false;
|
|
59
|
+
if (p.exp <= now)
|
|
60
|
+
return false;
|
|
61
|
+
// Re-enrollment do MFA revoga cookies emitidos antes dele.
|
|
62
|
+
if (typeof opts.mfaEnabledAt === 'number' && p.iat < opts.mfaEnabledAt)
|
|
63
|
+
return false;
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
@@ -69,3 +69,38 @@ export declare const changeEmailValidator: import("@vinejs/vine").VineValidator<
|
|
|
69
69
|
newEmail: string;
|
|
70
70
|
currentPassword: string;
|
|
71
71
|
}>, Record<string, any> | undefined>;
|
|
72
|
+
/**
|
|
73
|
+
* Edição de perfil no console de conta: nome e avatarUrl, ambos opcionais.
|
|
74
|
+
* Campos vazios são normalizados para string vazia (limpa o valor).
|
|
75
|
+
*/
|
|
76
|
+
export declare const updateProfileValidator: import("@vinejs/vine").VineValidator<import("@vinejs/vine").VineObject<{
|
|
77
|
+
name: import("@vinejs/vine/schema/base/literal").OptionalModifier<import("@vinejs/vine").VineString>;
|
|
78
|
+
avatarUrl: import("@vinejs/vine/schema/base/literal").OptionalModifier<import("@vinejs/vine").VineString>;
|
|
79
|
+
}, {
|
|
80
|
+
name?: string | null | undefined;
|
|
81
|
+
avatarUrl?: string | null | undefined;
|
|
82
|
+
}, {
|
|
83
|
+
name?: string | undefined;
|
|
84
|
+
avatarUrl?: string | undefined;
|
|
85
|
+
}, {
|
|
86
|
+
name?: string | undefined;
|
|
87
|
+
avatarUrl?: string | undefined;
|
|
88
|
+
}>, Record<string, any> | undefined>;
|
|
89
|
+
/** Criação de usuário no console admin (email obrigatório; nome/senha opcionais). */
|
|
90
|
+
export declare const adminCreateUserValidator: import("@vinejs/vine").VineValidator<import("@vinejs/vine").VineObject<{
|
|
91
|
+
email: import("@vinejs/vine").VineString;
|
|
92
|
+
name: import("@vinejs/vine/schema/base/literal").OptionalModifier<import("@vinejs/vine").VineString>;
|
|
93
|
+
password: import("@vinejs/vine/schema/base/literal").OptionalModifier<import("@vinejs/vine").VineString>;
|
|
94
|
+
}, {
|
|
95
|
+
password?: string | null | undefined;
|
|
96
|
+
name?: string | null | undefined;
|
|
97
|
+
email: string;
|
|
98
|
+
}, {
|
|
99
|
+
password?: string | undefined;
|
|
100
|
+
name?: string | undefined;
|
|
101
|
+
email: string;
|
|
102
|
+
}, {
|
|
103
|
+
password?: string | undefined;
|
|
104
|
+
name?: string | undefined;
|
|
105
|
+
email: string;
|
|
106
|
+
}>, Record<string, any> | undefined>;
|
|
@@ -25,3 +25,17 @@ export const changeEmailValidator = vine.compile(vine.object({
|
|
|
25
25
|
currentPassword: vine.string().minLength(1),
|
|
26
26
|
newEmail: vine.string().trim().email().normalizeEmail(),
|
|
27
27
|
}));
|
|
28
|
+
/**
|
|
29
|
+
* Edição de perfil no console de conta: nome e avatarUrl, ambos opcionais.
|
|
30
|
+
* Campos vazios são normalizados para string vazia (limpa o valor).
|
|
31
|
+
*/
|
|
32
|
+
export const updateProfileValidator = vine.compile(vine.object({
|
|
33
|
+
name: vine.string().trim().maxLength(255).optional(),
|
|
34
|
+
avatarUrl: vine.string().trim().url().maxLength(2048).optional(),
|
|
35
|
+
}));
|
|
36
|
+
/** Criação de usuário no console admin (email obrigatório; nome/senha opcionais). */
|
|
37
|
+
export const adminCreateUserValidator = vine.compile(vine.object({
|
|
38
|
+
email: vine.string().trim().email().normalizeEmail(),
|
|
39
|
+
name: vine.string().trim().maxLength(255).optional(),
|
|
40
|
+
password: vine.string().minLength(8).maxLength(255).optional(),
|
|
41
|
+
}));
|
|
@@ -5,11 +5,11 @@ function renderDashboardHtml(snapshot) {
|
|
|
5
5
|
const histograms = Object.entries(snapshot.histograms)
|
|
6
6
|
.map(([k, h]) => `<tr><td>${k}</td><td>${h.count}</td><td>${h.sum}</td><td>${h.min}</td><td>${h.max}</td></tr>`)
|
|
7
7
|
.join('');
|
|
8
|
-
return `<!doctype html><html lang="
|
|
8
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="refresh" content="5"><title>AuthKit — Metrics</title>
|
|
9
9
|
<style>body{font-family:system-ui,sans-serif;margin:2rem;color:#111}h1{font-size:1.2rem}table{border-collapse:collapse;margin:1rem 0;width:100%}th,td{border:1px solid #ddd;padding:.4rem .6rem;text-align:left;font-size:.9rem}th{background:#f5f5f5}</style>
|
|
10
|
-
</head><body><h1>AuthKit —
|
|
11
|
-
<h2>Counters</h2><table><thead><tr><th>
|
|
12
|
-
<h2>Histograms</h2><table><thead><tr><th>
|
|
10
|
+
</head><body><h1>AuthKit — Metrics</h1><p>Updated: ${snapshot.updatedAt ? new Date(snapshot.updatedAt).toISOString() : '—'}</p>
|
|
11
|
+
<h2>Counters</h2><table><thead><tr><th>Metric</th><th>Total</th></tr></thead><tbody>${counters || '<tr><td colspan="2">—</td></tr>'}</tbody></table>
|
|
12
|
+
<h2>Histograms</h2><table><thead><tr><th>Metric</th><th>Count</th><th>Sum</th><th>Min</th><th>Max</th></tr></thead><tbody>${histograms || '<tr><td colspan="5">—</td></tr>'}</tbody></table>
|
|
13
13
|
</body></html>`;
|
|
14
14
|
}
|
|
15
15
|
export default class MetricsController {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dudousxd/adonis-authkit-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "AdonisJS OIDC/OAuth2 provider (Identity Provider) toolkit: ejectable auth server with sessions, rate-limiting, MFA/TOTP, audit log, federated logout and OpenTelemetry metrics.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "dudousxd",
|