@adonis-agora/authkit-server 0.70.0 → 0.72.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/commands.json +18 -0
- package/build/commands/import_users.js +1 -6
- package/build/commands/normalize_emails.d.ts +21 -0
- package/build/commands/normalize_emails.js +106 -0
- package/build/host/views/mfa-challenge.edge +8 -3
- package/build/index.d.ts +3 -4
- package/build/index.js +2 -2
- package/build/src/accounts/account_store.d.ts +33 -1
- package/build/src/accounts/account_store.js +4 -0
- package/build/src/accounts/lucid_store/core.d.ts +2 -2
- package/build/src/accounts/lucid_store/core.js +16 -0
- package/build/src/accounts/lucid_store/mfa.js +9 -1
- package/build/src/commands/import_users.d.ts +0 -7
- package/build/src/commands/import_users.js +5 -12
- package/build/src/commands/normalize_emails.d.ts +78 -0
- package/build/src/commands/normalize_emails.js +129 -0
- package/build/src/define_config.d.ts +0 -16
- package/build/src/define_config.js +0 -1
- package/build/src/host/admin_api/admin_users_service.js +5 -7
- package/build/src/host/controllers/account_session_controller.js +6 -11
- package/build/src/host/controllers/interaction_controller.d.ts +33 -5
- package/build/src/host/controllers/interaction_controller.js +154 -124
- package/build/src/host/controllers/registration_controller.js +11 -27
- package/build/src/host/controllers/social_controller.js +5 -7
- package/build/src/host/email_identifier.d.ts +7 -70
- package/build/src/host/email_identifier.js +7 -210
- package/package.json +1 -1
|
@@ -537,26 +537,10 @@ export interface LoginConfigInput {
|
|
|
537
537
|
* idêntico ao de antes, e-mail idêntico). Ver `host/otp_login.ts`.
|
|
538
538
|
*/
|
|
539
539
|
otp?: OtpLoginConfigInput;
|
|
540
|
-
/**
|
|
541
|
-
* PONTE TEMPORÁRIA: no passo de identificador, quando o e-mail normalizado
|
|
542
|
-
* (`trim` + `toLowerCase`) não acha conta nenhuma, tenta também o endereço
|
|
543
|
-
* exatamente como foi digitado e a normalização LEGADA (os defaults do
|
|
544
|
-
* validator.js que o cadastro aplicava até a v0.68 e que, no gmail, removiam
|
|
545
|
-
* pontos e sub-endereço `+tag`). Só aceita quando essas formas apontam para
|
|
546
|
-
* EXATAMENTE UMA conta — empate é tratado como "não achei".
|
|
547
|
-
*
|
|
548
|
-
* Existe para que as contas que nasceram com o endereço mutilado
|
|
549
|
-
* (`davi.carvalho96@gmail.com` gravado como `davicarvalho96@gmail.com`)
|
|
550
|
-
* continuem tendo caminho de volta. Default: `true` — desligar tranca essas
|
|
551
|
-
* contas do lado de fora. Depois de migrar os endereços gravados, desligue e,
|
|
552
|
-
* mais adiante, a ponte inteira sai da lib (ver `host/email_identifier.ts`).
|
|
553
|
-
*/
|
|
554
|
-
legacyEmailFallback?: boolean;
|
|
555
540
|
}
|
|
556
541
|
export interface ResolvedLoginConfig {
|
|
557
542
|
requireVerifiedEmail: boolean;
|
|
558
543
|
otp: ResolvedOtpLoginConfig;
|
|
559
|
-
legacyEmailFallback: boolean;
|
|
560
544
|
}
|
|
561
545
|
export declare function resolveLogin(input?: LoginConfigInput): ResolvedLoginConfig;
|
|
562
546
|
/**
|
|
@@ -137,7 +137,6 @@ export function resolveLogin(input) {
|
|
|
137
137
|
return {
|
|
138
138
|
requireVerifiedEmail: input?.requireVerifiedEmail ?? false,
|
|
139
139
|
otp: resolveOtpLoginConfig(input?.otp),
|
|
140
|
-
legacyEmailFallback: input?.legacyEmailFallback ?? true,
|
|
141
140
|
};
|
|
142
141
|
}
|
|
143
142
|
export function resolveRegistration(input) {
|
|
@@ -4,7 +4,7 @@ import { ADMIN_LIST_DEFAULT_SIZE, LIST_FIRST_PAGE } from '../../pagination.js';
|
|
|
4
4
|
import { PasswordPolicyError } from '../../password/password_manager.js';
|
|
5
5
|
import { AccountDeletionService } from '../account_deletion_service.js';
|
|
6
6
|
import { sendPasswordResetEmail } from '../default_mailer.js';
|
|
7
|
-
import { normalizeEmailIdentifier
|
|
7
|
+
import { normalizeEmailIdentifier } from '../email_identifier.js';
|
|
8
8
|
import { authkitOrigin } from '../origin.js';
|
|
9
9
|
import { resolveEffectiveRolesCatalog } from '../runtime_toggles.js';
|
|
10
10
|
/**
|
|
@@ -29,12 +29,10 @@ export class AdminUsersService {
|
|
|
29
29
|
// serviço também é chamado direto (console/API/host) — normaliza aqui para
|
|
30
30
|
// que nenhum caminho grave um endereço que o login não encontra.
|
|
31
31
|
const email = normalizeEmailIdentifier(input.email);
|
|
32
|
-
// Duplicado
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
const existing =
|
|
36
|
-
legacyFallback: this.cfg.login?.legacyEmailFallback ?? true,
|
|
37
|
-
})).account;
|
|
32
|
+
// Duplicado pela MESMA forma que o login busca — a normalizada. Contas
|
|
33
|
+
// gravadas com outra grafia (import/convite antigos) só entram nesta conta
|
|
34
|
+
// depois do `authkit:users:normalize-emails`.
|
|
35
|
+
const existing = await store.findByEmail(email);
|
|
38
36
|
if (existing)
|
|
39
37
|
return { ok: false, reason: 'email_taken' };
|
|
40
38
|
const hasPassword = !!input.password;
|
|
@@ -3,7 +3,7 @@ import { accountHome } from '../account_home.js';
|
|
|
3
3
|
import { getAccountLoginUrl } from '../account_login_url.js';
|
|
4
4
|
import { ACCOUNT_SESSION_KEY } from '../account_session_key.js';
|
|
5
5
|
import { syncAdonisAuthLogin, syncAdonisAuthLogout } from '../adonis_auth_sync.js';
|
|
6
|
-
import {
|
|
6
|
+
import { normalizeEmailIdentifier } from '../email_identifier.js';
|
|
7
7
|
import { translate } from '../i18n.js';
|
|
8
8
|
import { endBridgedIdpSession } from '../idp_session_bridge.js';
|
|
9
9
|
import { attemptPasswordLogin } from '../login_attempt.js';
|
|
@@ -68,18 +68,13 @@ export default class AccountSessionController {
|
|
|
68
68
|
// Verificação + lockout + auditoria de falha centralizados (sem clientId no console).
|
|
69
69
|
// M1: passa `settings` p/ o lockout (e verified-email/expiração) runtime valerem aqui também.
|
|
70
70
|
const settings = await resolveRuntimeSettings(ctx);
|
|
71
|
-
// L6:
|
|
72
|
-
//
|
|
73
|
-
// o qual a conta está gravada — de modo que o lookup, o lockout (keyed por
|
|
71
|
+
// L6: normaliza (trim + lowercase, a MESMA função do cadastro e do passo de
|
|
72
|
+
// identificador do login OIDC) para que o lookup, o lockout (keyed por
|
|
74
73
|
// e-mail) e a auditoria usem a forma canônica, independente do casing/espaços
|
|
75
|
-
// digitados. A
|
|
76
|
-
|
|
77
|
-
// mesma (credencial inválida), ache conta ou não.
|
|
78
|
-
const { lookupEmail } = await resolveEmailIdentifier(cfg.accountStore, rawEmail, {
|
|
79
|
-
legacyFallback: cfg.login?.legacyEmailFallback ?? true,
|
|
80
|
-
});
|
|
74
|
+
// digitados. A tela de erro é a mesma (credencial inválida), ache conta ou não.
|
|
75
|
+
const email = normalizeEmailIdentifier(rawEmail);
|
|
81
76
|
const result = await attemptPasswordLogin(cfg, {
|
|
82
|
-
email
|
|
77
|
+
email,
|
|
83
78
|
password,
|
|
84
79
|
ip,
|
|
85
80
|
logger: ctx.logger,
|
|
@@ -12,11 +12,11 @@ export default class AuthInteractionController {
|
|
|
12
12
|
* `trim` + `toLowerCase`) — antes disto o valor cru ia para a sessão e a busca
|
|
13
13
|
* no store era por igualdade exata, então nem `Davi@x.com` achava `davi@x.com`.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
15
|
+
* À PROVA DE ENUMERAÇÃO: o passo NÃO toca no account store — só normaliza e
|
|
16
|
+
* guarda. A resposta é o MESMO redirect incondicional, exista a conta ou não,
|
|
17
|
+
* e não há busca alguma aqui para diferenciar os dois casos nem no tempo de
|
|
18
|
+
* resposta. Quem decide se a conta existe é o passo 2, que renderiza a mesma
|
|
19
|
+
* tela nos dois casos.
|
|
20
20
|
*/
|
|
21
21
|
identifier(ctx: HttpContext): Promise<void>;
|
|
22
22
|
/**
|
|
@@ -41,6 +41,34 @@ export default class AuthInteractionController {
|
|
|
41
41
|
* Retorna `undefined` quando não há step-up — completeLogin usa o default.
|
|
42
42
|
*/
|
|
43
43
|
private stepUpExtra;
|
|
44
|
+
/**
|
|
45
|
+
* Gate do segundo fator, compartilhado pelos caminhos de login: senha, link
|
|
46
|
+
* mágico, código por e-mail e troca forçada de senha. Antes disto só o login
|
|
47
|
+
* por senha passava pelo MFA — link e código completavam a interaction direto,
|
|
48
|
+
* então uma conta com TOTP ou passkey entrava apresentando só o e-mail e o
|
|
49
|
+
* segundo fator virava enfeite.
|
|
50
|
+
*
|
|
51
|
+
* A pergunta que o gate responde é "existe um fator que esta pessoa consegue
|
|
52
|
+
* apresentar AGORA?", e não `mfa.enabled` — que liga ao registrar uma passkey
|
|
53
|
+
* e não desliga ao remover a última. Fator utilizável = TOTP confirmado
|
|
54
|
+
* (`mfa.totp`) OU ao menos uma passkey registrada. Pela regra antiga, quem
|
|
55
|
+
* registrava uma passkey e depois a removia ficava preso num desafio TOTP que
|
|
56
|
+
* não tinha como responder.
|
|
57
|
+
*
|
|
58
|
+
* - sem fator e sem step-up → `none`, o login segue;
|
|
59
|
+
* - step-up (acr) exigindo MFA numa conta sem fator → `challenge` com a
|
|
60
|
+
* instrução de enrolar (não há o que desafiar);
|
|
61
|
+
* - fator presente, trusted-device válido e sem step-up → `trusted`; quem
|
|
62
|
+
* chamou decide como finalizar, porque o amr muda conforme o caminho;
|
|
63
|
+
* - caso geral → `challenge`, com o accountId em `MFA_PENDING_KEY`.
|
|
64
|
+
*/
|
|
65
|
+
private secondFactorGate;
|
|
66
|
+
/**
|
|
67
|
+
* true se a conta tem um app autenticador CONFIRMADO. Stores que não reportam
|
|
68
|
+
* `totp` (a chave é opcional em {@link MfaCapability}) caem no `enabled`, o
|
|
69
|
+
* comportamento antigo.
|
|
70
|
+
*/
|
|
71
|
+
private hasTotp;
|
|
44
72
|
/** true se o store suporta passkeys E a conta tem ao menos uma registrada. */
|
|
45
73
|
private hasPasskeys;
|
|
46
74
|
/**
|