@adonis-agora/authkit-server 0.70.0 → 0.71.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.
@@ -18,217 +18,14 @@
18
18
  * Use em TODO ponto que grava ou busca a identidade: cadastro (com e sem senha),
19
19
  * "esqueci a senha", troca de e-mail, criação por admin, convite de organização,
20
20
  * import de usuários, cadastro social e o passo de identificador do login.
21
+ *
22
+ * As contas GRAVADAS antes disto (com o endereço mutilado, ou com maiúsculas
23
+ * vindas de import/convite/provider social) ficam inalcançáveis pelo login, que
24
+ * busca a forma normalizada. Elas pedem MIGRAÇÃO do endereço gravado — é o que o
25
+ * comando `authkit:users:normalize-emails` faz (ver `commands/normalize_emails.ts`).
26
+ * A lib não tenta adivinhar grafias no caminho do login: cada tentativa extra é
27
+ * uma query a mais por e-mail desconhecido, que é justamente o caminho de ataque.
21
28
  */
22
29
  export function normalizeEmailIdentifier(raw) {
23
30
  return typeof raw === 'string' ? raw.trim().toLowerCase() : '';
24
31
  }
25
- // ─── Ponte de compatibilidade com a normalização LEGADA ─────────────────────
26
- /** Domínios do gmail (pontos e sub-endereço eram removidos do local part). */
27
- const GMAIL_DOMAINS = ['gmail.com', 'googlemail.com'];
28
- /** Domínios do iCloud (sub-endereço `+tag` removido). */
29
- const ICLOUD_DOMAINS = ['icloud.com', 'me.com'];
30
- /** Domínios do Outlook.com/Hotmail/Live (sub-endereço `+tag` removido). */
31
- const OUTLOOK_DOMAINS = [
32
- 'hotmail.at',
33
- 'hotmail.be',
34
- 'hotmail.ca',
35
- 'hotmail.cl',
36
- 'hotmail.co.il',
37
- 'hotmail.co.nz',
38
- 'hotmail.co.th',
39
- 'hotmail.co.uk',
40
- 'hotmail.com',
41
- 'hotmail.com.ar',
42
- 'hotmail.com.au',
43
- 'hotmail.com.br',
44
- 'hotmail.com.gr',
45
- 'hotmail.com.mx',
46
- 'hotmail.com.pe',
47
- 'hotmail.com.tr',
48
- 'hotmail.com.vn',
49
- 'hotmail.cz',
50
- 'hotmail.de',
51
- 'hotmail.dk',
52
- 'hotmail.es',
53
- 'hotmail.fr',
54
- 'hotmail.hu',
55
- 'hotmail.id',
56
- 'hotmail.ie',
57
- 'hotmail.in',
58
- 'hotmail.it',
59
- 'hotmail.jp',
60
- 'hotmail.kr',
61
- 'hotmail.lv',
62
- 'hotmail.my',
63
- 'hotmail.ph',
64
- 'hotmail.pt',
65
- 'hotmail.sa',
66
- 'hotmail.sg',
67
- 'hotmail.sk',
68
- 'live.be',
69
- 'live.co.uk',
70
- 'live.com',
71
- 'live.com.ar',
72
- 'live.com.mx',
73
- 'live.de',
74
- 'live.es',
75
- 'live.eu',
76
- 'live.fr',
77
- 'live.it',
78
- 'live.nl',
79
- 'msn.com',
80
- 'outlook.at',
81
- 'outlook.be',
82
- 'outlook.cl',
83
- 'outlook.co.il',
84
- 'outlook.co.nz',
85
- 'outlook.co.th',
86
- 'outlook.com',
87
- 'outlook.com.ar',
88
- 'outlook.com.au',
89
- 'outlook.com.br',
90
- 'outlook.com.gr',
91
- 'outlook.com.pe',
92
- 'outlook.com.tr',
93
- 'outlook.com.vn',
94
- 'outlook.cz',
95
- 'outlook.de',
96
- 'outlook.dk',
97
- 'outlook.es',
98
- 'outlook.fr',
99
- 'outlook.hu',
100
- 'outlook.id',
101
- 'outlook.ie',
102
- 'outlook.in',
103
- 'outlook.it',
104
- 'outlook.jp',
105
- 'outlook.kr',
106
- 'outlook.lv',
107
- 'outlook.my',
108
- 'outlook.ph',
109
- 'outlook.pt',
110
- 'outlook.sa',
111
- 'outlook.sg',
112
- 'outlook.sk',
113
- 'passport.com',
114
- ];
115
- /** Domínios do Yahoo (sub-endereço `-tag` removido). */
116
- const YAHOO_DOMAINS = [
117
- 'rocketmail.com',
118
- 'yahoo.ca',
119
- 'yahoo.co.uk',
120
- 'yahoo.com',
121
- 'yahoo.de',
122
- 'yahoo.fr',
123
- 'yahoo.in',
124
- 'yahoo.it',
125
- 'ymail.com',
126
- ];
127
- /** Domínios do Yandex (todos colapsavam em `yandex.ru`). */
128
- const YANDEX_DOMAINS = ['yandex.ru', 'yandex.ua', 'yandex.kz', 'yandex.com', 'yandex.by', 'ya.ru'];
129
- /** Remove pontos SOLTOS do local part (pontos consecutivos ficam — regra do validator.js). */
130
- function stripSingleDots(local) {
131
- return local.replace(/\.+/g, (match) => (match.length > 1 ? match : ''));
132
- }
133
- /**
134
- * Réplica da normalização LEGADA — `normalizeEmail()` do validator.js com os
135
- * defaults, que é o que o `.normalizeEmail()` do VineJS aplicava no cadastro até
136
- * a v0.68. Existe SÓ para reencontrar as contas que nasceram com o endereço
137
- * mutilado; nada novo deve ser gravado com ela.
138
- *
139
- * É uma PONTE TEMPORÁRIA: quando as contas antigas tiverem sido migradas para o
140
- * endereço real (ou o suficiente delas), esta função, a opção
141
- * `login.legacyEmailFallback` e {@link resolveEmailIdentifier} podem sair.
142
- *
143
- * Retorna `null` para entradas que o validator.js também recusaria (local part
144
- * vazio depois do colapso) ou que não são um endereço com `@`.
145
- */
146
- export function legacyNormalizeEmailIdentifier(raw) {
147
- const email = normalizeEmailIdentifier(raw);
148
- const at = email.lastIndexOf('@');
149
- if (at <= 0 || at === email.length - 1)
150
- return null;
151
- let local = email.slice(0, at);
152
- let domain = email.slice(at + 1);
153
- if (GMAIL_DOMAINS.includes(domain)) {
154
- local = local.split('+')[0];
155
- local = stripSingleDots(local);
156
- domain = 'gmail.com';
157
- }
158
- else if (ICLOUD_DOMAINS.includes(domain) || OUTLOOK_DOMAINS.includes(domain)) {
159
- local = local.split('+')[0];
160
- }
161
- else if (YAHOO_DOMAINS.includes(domain)) {
162
- const parts = local.split('-');
163
- local = parts.length > 1 ? parts.slice(0, -1).join('-') : parts[0];
164
- }
165
- else if (YANDEX_DOMAINS.includes(domain)) {
166
- domain = 'yandex.ru';
167
- }
168
- if (!local.length)
169
- return null;
170
- return `${local}@${domain}`;
171
- }
172
- /**
173
- * Resolve o e-mail digitado na conta correspondente.
174
- *
175
- * Passe o valor **CRU** (como veio do formulário, do provider ou do arquivo): a
176
- * normalização acontece aqui dentro, e a forma crua é uma das candidatas da
177
- * ponte. Passar um valor já normalizado apaga essa candidata e reduz o alcance
178
- * da ponte às contas gravadas com o endereço mutilado.
179
- *
180
- * 1. Busca pela forma normalizada NOVA (trim + lowercase) — o caminho de sempre,
181
- * uma única query por igualdade (indexada).
182
- * 2. Não achando, e com a ponte ligada (`login.legacyEmailFallback`, default
183
- * `true`), tenta as formas de compatibilidade:
184
- * - o endereço EXATAMENTE como digitado (só com `trim`), para as contas que
185
- * foram gravadas com maiúsculas antes desta normalização existir (import,
186
- * convite, criação por admin, provider social);
187
- * - a {@link legacyNormalizeEmailIdentifier normalização legada}, para as
188
- * contas que nasceram com o endereço mutilado.
189
- * O resultado só é aceito quando as formas de compatibilidade apontam para
190
- * EXATAMENTE UMA conta. Duas contas distintas (ex.: `Davi.C@Gmail.com` criada
191
- * pelo social E `davic@gmail.com` criada pelo cadastro legado) são um empate:
192
- * a lib não adivinha qual é a pessoa e trata como "não achei".
193
- *
194
- * LIMITE CONHECIDO: a ponte só alcança grafias que dá para derivar do que foi
195
- * digitado. Uma conta gravada `Davi@Acme.com` é alcançada por quem digita
196
- * `Davi@Acme.com` (a forma crua), mas NÃO por quem digita `davi@acme.com` — aí as
197
- * três formas coincidem e só uma busca case-insensitive no store resolveria, o
198
- * que exigiria varrer a tabela a cada login com e-mail desconhecido (justamente o
199
- * caminho de ataque). Essas contas pedem migração do endereço gravado, não ponte.
200
- *
201
- * À PROVA DE ENUMERAÇÃO: nunca lança, nunca sinaliza nada para fora — quem chama
202
- * segue com `account: null` exatamente como seguia antes. As buscas extras só
203
- * acontecem quando as formas de compatibilidade DIFEREM da normalizada (e-mail
204
- * digitado em minúsculas e sem ponto/tag → uma query só, como hoje).
205
- */
206
- export async function resolveEmailIdentifier(store, raw, options = {}) {
207
- const email = normalizeEmailIdentifier(raw);
208
- const miss = {
209
- email,
210
- lookupEmail: email,
211
- account: null,
212
- viaLegacyFallback: false,
213
- };
214
- if (!email)
215
- return miss;
216
- const direct = await store.findByEmail(email);
217
- if (direct)
218
- return { email, lookupEmail: email, account: direct, viaLegacyFallback: false };
219
- if (options.legacyFallback === false)
220
- return miss;
221
- const typed = typeof raw === 'string' ? raw.trim() : '';
222
- const legacy = legacyNormalizeEmailIdentifier(email);
223
- const candidates = [typed, legacy].filter((candidate) => !!candidate && candidate !== email);
224
- const byId = new Map();
225
- for (const candidate of new Set(candidates)) {
226
- const found = await store.findByEmail(candidate);
227
- if (found)
228
- byId.set(found.id, found);
229
- }
230
- if (byId.size !== 1)
231
- return miss;
232
- const account = [...byId.values()][0];
233
- return { email, lookupEmail: account.email, account, viaLegacyFallback: true };
234
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonis-agora/authkit-server",
3
- "version": "0.70.0",
3
+ "version": "0.71.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",