@adonis-agora/authkit-server 0.45.0 → 0.47.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/ui_preset.js +15 -1
- package/build/host/views/account/confirm.edge +118 -52
- package/build/host/views/account/mfa.edge +1 -1
- package/build/host/views/login.edge +2 -4
- package/build/host/views/mfa-challenge.edge +1 -1
- package/build/host/views/otp-unlock.edge +2 -2
- package/build/host/views/partials/styles.edge +1 -1
- package/build/index.d.ts +5 -1
- package/build/index.js +15 -1
- package/build/providers/authkit_server_provider.js +7 -0
- package/build/services/booted_app.d.ts +8 -0
- package/build/services/booted_app.js +27 -0
- package/build/services/main.d.ts +7 -0
- package/build/services/main.js +9 -1
- package/build/src/define_config.d.ts +51 -0
- package/build/src/define_config.js +8 -0
- package/build/src/host/account_login_url.d.ts +41 -0
- package/build/src/host/account_login_url.js +50 -0
- package/build/src/host/admin_sessions_service.d.ts +3 -1
- package/build/src/host/admin_sessions_service.js +35 -3
- package/build/src/host/assets/webauthn.js +2 -0
- 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_confirm_controller.d.ts +11 -14
- package/build/src/host/controllers/account_confirm_controller.js +42 -130
- package/build/src/host/controllers/account_orgs_controller.js +7 -4
- package/build/src/host/controllers/account_security_controller.js +3 -2
- package/build/src/host/controllers/account_session_controller.js +23 -5
- package/build/src/host/controllers/account_tokens_controller.js +11 -0
- package/build/src/host/controllers/pat_introspection_controller.js +10 -0
- package/build/src/host/controllers/webauthn_asset_controller.d.ts +22 -0
- package/build/src/host/controllers/webauthn_asset_controller.js +66 -0
- package/build/src/host/i18n.d.ts +14 -0
- package/build/src/host/i18n.js +16 -0
- package/build/src/host/impersonation_session.js +15 -0
- package/build/src/host/middleware/account_auth.js +3 -1
- package/build/src/host/rate_limit.d.ts +10 -0
- package/build/src/host/rate_limit.js +6 -0
- package/build/src/host/register_auth_host.d.ts +85 -2
- package/build/src/host/register_auth_host.js +188 -61
- package/build/src/host/renderers/edge_renderer.js +6 -1
- package/build/src/host/renderers/inertia_renderer.d.ts +55 -0
- package/build/src/host/renderers/inertia_renderer.js +55 -0
- package/build/src/host/sudo/index.d.ts +47 -0
- package/build/src/host/sudo/index.js +41 -0
- package/build/src/host/sudo/methods/magic_link.d.ts +43 -0
- package/build/src/host/sudo/methods/magic_link.js +174 -0
- package/build/src/host/sudo/methods/oidc_step_up.d.ts +68 -0
- package/build/src/host/sudo/methods/oidc_step_up.js +78 -0
- package/build/src/host/sudo/methods/passkey.d.ts +28 -0
- package/build/src/host/sudo/methods/passkey.js +139 -0
- package/build/src/host/sudo/methods/password.d.ts +19 -0
- package/build/src/host/sudo/methods/password.js +93 -0
- package/build/src/host/sudo/runtime.d.ts +141 -0
- package/build/src/host/sudo/runtime.js +327 -0
- package/build/src/host/sudo/types.d.ts +93 -0
- package/build/src/host/sudo/types.js +1 -0
- package/build/src/host/sudo_mode.d.ts +116 -6
- package/build/src/host/sudo_mode.js +133 -7
- package/package.json +6 -2
- package/build/stubs/ui/edge/views/consent.edge +0 -13
- package/build/stubs/ui/edge/views/login.edge +0 -19
- package/stubs/ui/edge/views/consent.edge +0 -13
- package/stubs/ui/edge/views/login.edge +0 -19
|
@@ -8,6 +8,20 @@ import { setAdminPrefix, normalizeAdminPrefix, setAdminApiPrefix, normalizeAdmin
|
|
|
8
8
|
import { resolveRuntimeSettings } from './runtime_settings.js';
|
|
9
9
|
import { resolveEffectiveSessionPolicy } from './runtime_toggles.js';
|
|
10
10
|
import { getAuthHostConfig } from './auth_host_config.js';
|
|
11
|
+
import { setAccountLoginUrl, getAccountLoginUrl } from './account_login_url.js';
|
|
12
|
+
import { completeSudo, fail, guardSudoRoutes, sudoContextFrom, setMountedSudoMethods, } from './sudo/runtime.js';
|
|
13
|
+
import { password as sudoPassword } from './sudo/methods/password.js';
|
|
14
|
+
import { passkey as sudoPasskey } from './sudo/methods/passkey.js';
|
|
15
|
+
/**
|
|
16
|
+
* Métodos de sudo montados quando o host não passa `sudoMethods` —
|
|
17
|
+
* comportamento histórico (senha + passkey).
|
|
18
|
+
*
|
|
19
|
+
* PONTO ÚNICO. A tela não tem mais uma cópia desta lista: sem
|
|
20
|
+
* `config.sudo.methods`, `configuredSudoMethods` cai no que FOI MONTADO, ou
|
|
21
|
+
* seja, no resultado do `??` abaixo. Duas listas de default é como os dois
|
|
22
|
+
* lados divergiam.
|
|
23
|
+
*/
|
|
24
|
+
const SUDO_METHOD_DEFAULTS = [sudoPassword(), sudoPasskey()];
|
|
11
25
|
/** Chave da sessão Adonis que registra o timestamp da última atividade (idle timeout). */
|
|
12
26
|
export const ACCOUNT_LAST_SEEN_KEY = 'authkit_last_seen';
|
|
13
27
|
/**
|
|
@@ -55,16 +69,26 @@ async function checkAndRefreshIdle(ctx) {
|
|
|
55
69
|
* @internal
|
|
56
70
|
*/
|
|
57
71
|
function buildLoginRedirect(ctx, extra) {
|
|
72
|
+
// Destino configurável (`accountLoginUrl`): default `/account/login`, mas um
|
|
73
|
+
// host que desmontou a tela de login (`account: { login: false }`) aponta para
|
|
74
|
+
// a própria rota de login dele (ex.: `/login`). Ver `account_login_url.ts`.
|
|
75
|
+
const loginUrl = getAccountLoginUrl();
|
|
58
76
|
const url = ctx.request?.url?.() ?? '';
|
|
59
77
|
const qs = ctx.request?.parsedUrl?.search ?? '';
|
|
60
78
|
const dest = qs ? `${url}${qs}` : url;
|
|
61
79
|
// Só inclui return_to quando há um caminho real (não vazio, não é o próprio login).
|
|
62
|
-
if (dest && dest !== '/' && !dest.startsWith(
|
|
80
|
+
if (dest && dest !== '/' && !dest.startsWith(loginUrl)) {
|
|
63
81
|
const encoded = encodeURIComponent(dest);
|
|
64
|
-
const
|
|
82
|
+
const sep = loginUrl.includes('?') ? '&' : '?';
|
|
83
|
+
const base = extra
|
|
84
|
+
? `${loginUrl}${sep}${extra}&return_to=${encoded}`
|
|
85
|
+
: `${loginUrl}${sep}return_to=${encoded}`;
|
|
65
86
|
return base;
|
|
66
87
|
}
|
|
67
|
-
|
|
88
|
+
if (!extra)
|
|
89
|
+
return loginUrl;
|
|
90
|
+
const sep = loginUrl.includes('?') ? '&' : '?';
|
|
91
|
+
return `${loginUrl}${sep}${extra}`;
|
|
68
92
|
}
|
|
69
93
|
/**
|
|
70
94
|
* Guard inline do console de conta. Usamos uma closure (forma confiável do
|
|
@@ -87,9 +111,11 @@ const accountGuard = async (ctx, next) => {
|
|
|
87
111
|
* Guard do console admin (B6). Como o `accountGuard`, é uma closure inline (forma
|
|
88
112
|
* confiável do `.use()` num grupo). Exige:
|
|
89
113
|
* 0. `config.admin.enabled` ligado (senão → 404; ver nota de flag-drift abaixo);
|
|
90
|
-
* 1. sessão de conta ativa (senão → /account/login);
|
|
114
|
+
* 1. sessão de conta ativa (senão → `accountLoginUrl`, default /account/login);
|
|
91
115
|
* 2. a conta logada com pelo menos UMA das `config.admin.roles` nas roles globais
|
|
92
|
-
* (senão → /account/
|
|
116
|
+
* (senão → `accountHome(cfg)`, default /account/security — NÃO revela a
|
|
117
|
+
* existência do /admin, e cai numa tela que o host controla via `accountHome`;
|
|
118
|
+
* se a tela default estiver desmontada, aponte `config.accountHome` para uma montada).
|
|
93
119
|
* As roles permitidas são resolvidas em runtime do `authkit.server` (config lazy).
|
|
94
120
|
*/
|
|
95
121
|
export const adminGuard = async (ctx, next) => {
|
|
@@ -138,6 +164,7 @@ const C = {
|
|
|
138
164
|
accountMfa: () => import('./controllers/account_mfa_controller.js'),
|
|
139
165
|
accountOrgs: () => import('./controllers/account_orgs_controller.js'),
|
|
140
166
|
accountConfirm: () => import('./controllers/account_confirm_controller.js'),
|
|
167
|
+
webauthnAsset: () => import('./controllers/webauthn_asset_controller.js'),
|
|
141
168
|
// Console React JSON API (session-authed, under {prefix}/api/*).
|
|
142
169
|
consoleShell: () => import('./admin_console/admin_shell_controller.js'),
|
|
143
170
|
consoleOverview: () => import('./admin_console/console_overview_controller.js'),
|
|
@@ -174,6 +201,29 @@ export function registerAuthHost(router, opts = {}) {
|
|
|
174
201
|
const social = opts.social ?? hostCfg?.social;
|
|
175
202
|
const adminOpt = opts.admin ?? (hostCfg?.adminEnabled ? true : undefined);
|
|
176
203
|
const adminApiOpt = opts.adminApi ?? (hostCfg?.adminApiEnabled ? true : undefined);
|
|
204
|
+
// Destino do redirect de "faça login" — persiste no singleton de processo para
|
|
205
|
+
// que os guards (closures de tempo de registro), o middleware, os controllers e
|
|
206
|
+
// as views leiam o mesmo valor. Só quando a opção foi passada (senão fica o
|
|
207
|
+
// default `/account/login` do singleton — back-compat).
|
|
208
|
+
if (opts.accountLoginUrl !== undefined) {
|
|
209
|
+
setAccountLoginUrl(opts.accountLoginUrl);
|
|
210
|
+
}
|
|
211
|
+
// Montagem por tela do console de conta. `undefined` → tudo montado;
|
|
212
|
+
// `false` → nada; objeto → cada flag ausente default `true`.
|
|
213
|
+
const accountOpt = opts.account;
|
|
214
|
+
const mountScreen = (key) => {
|
|
215
|
+
if (accountOpt === false)
|
|
216
|
+
return false;
|
|
217
|
+
if (accountOpt && typeof accountOpt === 'object')
|
|
218
|
+
return accountOpt[key] !== false;
|
|
219
|
+
return true;
|
|
220
|
+
};
|
|
221
|
+
const mountLogin = mountScreen('login');
|
|
222
|
+
const mountTokens = mountScreen('tokens');
|
|
223
|
+
const mountOrgs = mountScreen('orgs');
|
|
224
|
+
const mountSecurity = mountScreen('security');
|
|
225
|
+
const mountMfa = mountScreen('mfa');
|
|
226
|
+
const mountApps = mountScreen('apps');
|
|
177
227
|
// Throttles opt-in (anti-brute-force). `undefined` quando rate-limit desligado.
|
|
178
228
|
const resolvedRateLimit = opts.rateLimit !== undefined
|
|
179
229
|
? resolveRateLimit(opts.rateLimit)
|
|
@@ -188,6 +238,25 @@ export function registerAuthHost(router, opts = {}) {
|
|
|
188
238
|
if (throttles)
|
|
189
239
|
route.use([throttles.introspection]);
|
|
190
240
|
};
|
|
241
|
+
// Bucket PRÓPRIO das rotas de sudo. Antes elas levavam o `withLogin`, o que
|
|
242
|
+
// funcionava mas somava dois orçamentos que medem coisas diferentes: login é
|
|
243
|
+
// um anônimo adivinhando credenciais, sudo é um usuário JÁ autenticado
|
|
244
|
+
// reprovando a própria identidade. Ver `ResolvedRateLimitConfig.sudo`.
|
|
245
|
+
const withSudo = (route) => {
|
|
246
|
+
if (throttles)
|
|
247
|
+
route.use([throttles.sudo]);
|
|
248
|
+
};
|
|
249
|
+
// ─── Assets estáticos do host-kit (públicos, sem autenticação) ─────────────
|
|
250
|
+
// Bundle do @simplewebauthn/browser servido pelo próprio app, no lugar do
|
|
251
|
+
// import de CDN público que as views de login/MFA/confirm faziam.
|
|
252
|
+
//
|
|
253
|
+
// Path FIXO e no topo, de propósito:
|
|
254
|
+
// • não pode viver sob o prefixo do console admin (`admin` é opt-in) —
|
|
255
|
+
// login.edge e mfa-challenge.edge precisam do script em qualquer host;
|
|
256
|
+
// • sem guard, porque é carregado NA tela de login, antes de haver sessão;
|
|
257
|
+
// • registrado ANTES do wildcard `${mount}/*` para que nenhum mountPath
|
|
258
|
+
// agressivo (ex.: '/') consiga engolir o asset e quebrar o login.
|
|
259
|
+
router.get('/authkit/assets/webauthn.js', [C.webauthnAsset]).as('authkit.assets.webauthn');
|
|
191
260
|
// Provider OIDC (wildcard + root) — o que registerOidcRoutes fazia.
|
|
192
261
|
router.any(`${mount}/*`, [C.oidc]).as('authkit.oidc.wildcard');
|
|
193
262
|
router.any(mount, [C.oidc]).as('authkit.oidc.root');
|
|
@@ -225,68 +294,126 @@ export function registerAuthHost(router, opts = {}) {
|
|
|
225
294
|
// PAT introspection (server-to-server).
|
|
226
295
|
withIntrospection(router.post('/authkit/pat/introspect', [C.patIntrospection, 'handle']));
|
|
227
296
|
// Organizations — invitation accept (sem guard: controller lida com não-autenticado).
|
|
228
|
-
|
|
229
|
-
|
|
297
|
+
// Parte da tela `orgs`: desmontada junto (sem multi-tenancy, não há convite a aceitar).
|
|
298
|
+
if (mountOrgs) {
|
|
299
|
+
router.get('/account/orgs/invitations/:token/accept', [C.accountOrgs, 'showAcceptInvitation']);
|
|
300
|
+
router.post('/account/orgs/invitations/:token/accept', [C.accountOrgs, 'acceptInvitation']);
|
|
301
|
+
}
|
|
230
302
|
// Console de conta (login de sessão do IdP + gerência de PAT).
|
|
231
|
-
|
|
232
|
-
//
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
303
|
+
// Tela `login` desmontável: hosts passwordless delegam ao OIDC próprio e
|
|
304
|
+
// apontam `accountLoginUrl` para a rota de login deles.
|
|
305
|
+
if (mountLogin) {
|
|
306
|
+
router.get('/account/login', [C.accountSession, 'show']);
|
|
307
|
+
// L6: throttle por IP no login/logout do console de conta (anti-brute-force),
|
|
308
|
+
// alinhado com as demais rotas de credencial (interaction, forgot, reset).
|
|
309
|
+
withLogin(router.post('/account/login', [C.accountSession, 'login']));
|
|
310
|
+
withLogin(router.post('/account/logout', [C.accountSession, 'logout']));
|
|
311
|
+
}
|
|
236
312
|
// Confirmação de troca de e-mail (standalone, GET-only — consome o token do link;
|
|
237
|
-
// pode ser aberta em outro dispositivo, então NÃO exige sessão).
|
|
238
|
-
|
|
313
|
+
// pode ser aberta em outro dispositivo, então NÃO exige sessão). Parte da tela
|
|
314
|
+
// `security` (o terminal do fluxo de troca de e-mail).
|
|
315
|
+
if (mountSecurity) {
|
|
316
|
+
router.get('/account/email/confirm', [C.accountSecurity, 'confirmEmail']);
|
|
317
|
+
}
|
|
239
318
|
// Rotas de tokens protegidas por AccountAuthMiddleware (redireciona para /account/login se não autenticado).
|
|
240
319
|
router
|
|
241
320
|
.group(() => {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
C.accountSecurity,
|
|
258
|
-
'
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
321
|
+
// Personal Access Tokens (tela `tokens`).
|
|
322
|
+
if (mountTokens) {
|
|
323
|
+
router.get('/account/tokens', [C.accountTokens, 'index']);
|
|
324
|
+
router.post('/account/tokens', [C.accountTokens, 'store']);
|
|
325
|
+
router.post('/account/tokens/:id/revoke', [C.accountTokens, 'destroy']);
|
|
326
|
+
}
|
|
327
|
+
// Segurança da conta: trocar senha + solicitar troca de e-mail + perfil (tela `security`).
|
|
328
|
+
if (mountSecurity) {
|
|
329
|
+
router.get('/account/security', [C.accountSecurity, 'index']);
|
|
330
|
+
router.post('/account/security/password', [C.accountSecurity, 'changePassword']);
|
|
331
|
+
router.post('/account/security/email', [C.accountSecurity, 'changeEmail']);
|
|
332
|
+
router.post('/account/security/email/cancel', [C.accountSecurity, 'cancelEmailChange']);
|
|
333
|
+
router.post('/account/security/profile', [C.accountSecurity, 'updateProfile']);
|
|
334
|
+
// LGPD/GDPR: export de dados (portabilidade) + deleção self-service (danger zone).
|
|
335
|
+
// O export carrega o throttle de login (anti-abuso) quando o rate-limit existe.
|
|
336
|
+
withLogin(router.get('/account/security/export', [C.accountSecurity, 'exportData']));
|
|
337
|
+
router.post('/account/security/delete', [C.accountSecurity, 'deleteAccount']);
|
|
338
|
+
// Trusted devices: limpa o cookie de confiança DESTE navegador.
|
|
339
|
+
router.post('/account/security/trusted-devices/revoke', [
|
|
340
|
+
C.accountSecurity,
|
|
341
|
+
'revokeTrustedDevices',
|
|
342
|
+
]);
|
|
343
|
+
}
|
|
344
|
+
// Apps com acesso (consentimento): lista os grants da conta + revogação por client (tela `apps`).
|
|
345
|
+
if (mountApps) {
|
|
346
|
+
router.get('/account/apps', [C.accountApps, 'index']);
|
|
347
|
+
router.post('/account/apps/:clientId/revoke', [C.accountApps, 'revoke']);
|
|
348
|
+
}
|
|
349
|
+
// MFA — TOTP + passkeys (tela `mfa`).
|
|
350
|
+
if (mountMfa) {
|
|
351
|
+
// MFA / TOTP (enrollment, confirmação, disable).
|
|
352
|
+
router.get('/account/mfa', [C.accountMfa, 'index']);
|
|
353
|
+
router.post('/account/mfa/enroll', [C.accountMfa, 'enroll']);
|
|
354
|
+
router.post('/account/mfa/confirm', [C.accountMfa, 'confirm']);
|
|
355
|
+
router.post('/account/mfa/disable', [C.accountMfa, 'disable']);
|
|
356
|
+
// MFA / WebAuthn (passkeys): registro (begin/finish) + remoção.
|
|
357
|
+
router.post('/account/mfa/passkeys/options', [C.accountMfa, 'passkeyRegisterOptions']);
|
|
358
|
+
router.post('/account/mfa/passkeys/verify', [C.accountMfa, 'passkeyRegisterVerify']);
|
|
359
|
+
router.post('/account/mfa/passkeys/:id/remove', [C.accountMfa, 'passkeyRemove']);
|
|
360
|
+
}
|
|
361
|
+
// Sudo mode (confirm identity): o GET lista os métodos; cada método
|
|
362
|
+
// registra suas próprias rotas de verificação (SPI `SudoMethod`).
|
|
273
363
|
router.get('/account/confirm', [C.accountConfirm, 'show']);
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
//
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
364
|
+
// Rotas próprias dos métodos de sudo — DENTRO do grupo com `accountGuard`.
|
|
365
|
+
// O guard não é só "tem sessão": ele roda `checkAndRefreshIdle`, que apaga
|
|
366
|
+
// a sessão vencida por idle e refresca `authkit_last_seen`. Fora do grupo,
|
|
367
|
+
// uma sessão já vencida (ainda não colhida) podia postar a senha correta e
|
|
368
|
+
// receber `markSudo` — e as rotas de sudo não refrescavam o last-seen.
|
|
369
|
+
// Nenhum método built-in é alcançável por GET vindo de e-mail: o token de
|
|
370
|
+
// sudo por magic link vive na PRÓPRIA sessão, então o usuário precisa
|
|
371
|
+
// estar logado no mesmo navegador de qualquer forma. Um método que
|
|
372
|
+
// genuinamente não puder ficar sob o guard precisa de uma decisão
|
|
373
|
+
// explícita, não de mover todos para fora.
|
|
374
|
+
const helpers = {
|
|
375
|
+
contextFrom: sudoContextFrom,
|
|
376
|
+
completeSudo,
|
|
377
|
+
fail,
|
|
378
|
+
};
|
|
379
|
+
const sudoMethodsToMount = opts?.sudoMethods ?? SUDO_METHOD_DEFAULTS;
|
|
380
|
+
for (const method of sudoMethodsToMount) {
|
|
381
|
+
// `guardSudoRoutes` embrulha os handlers que o método registrar, para
|
|
382
|
+
// que `config.sudo.methods` os desabilite de fato mesmo que o método
|
|
383
|
+
// não tenha checado nada por dentro. Ver o docblock lá.
|
|
384
|
+
//
|
|
385
|
+
// `withSudo` vai junto: TODA rota de um método de sudo leva o throttle
|
|
386
|
+
// do bucket de SUDO (no-op sem rate-limit). Não é adorno — o POST que
|
|
387
|
+
// emite o magic link de sudo dispara um e-mail por chamada, e o
|
|
388
|
+
// `accountGuard` sozinho só exige uma sessão viva, que o abusador tem.
|
|
389
|
+
// Aplicar aqui, no wrapper, cobre também os métodos customizados, que
|
|
390
|
+
// não teriam como pedir throttle pelo `SudoRouteHelpers`.
|
|
391
|
+
//
|
|
392
|
+
// Bucket próprio, não o de login: mesmos limites, contagem separada —
|
|
393
|
+
// errar a senha na tela de confirmação não pode gastar o orçamento de
|
|
394
|
+
// login do IP, nem vice-versa.
|
|
395
|
+
method.register?.(guardSudoRoutes(router, method.id, helpers, withSudo), helpers);
|
|
396
|
+
}
|
|
397
|
+
// A lista montada é a fonte de verdade dos DOIS lados quando o host não
|
|
398
|
+
// configura `config.sudo.methods`: a tela oferece exatamente isto, e os
|
|
399
|
+
// handlers aceitam exatamente isto.
|
|
400
|
+
setMountedSudoMethods(sudoMethodsToMount);
|
|
401
|
+
// Organizations (multi-tenancy) — tela `orgs`. Montadas por default;
|
|
402
|
+
// controller retorna 404/403 sem tabelas (capability-probed).
|
|
403
|
+
if (mountOrgs) {
|
|
404
|
+
router.get('/account/orgs', [C.accountOrgs, 'index']);
|
|
405
|
+
router.post('/account/orgs', [C.accountOrgs, 'store']);
|
|
406
|
+
router.post('/account/orgs/deactivate', [C.accountOrgs, 'deactivate']);
|
|
407
|
+
router.post('/account/orgs/:id/activate', [C.accountOrgs, 'activate']);
|
|
408
|
+
router.post('/account/orgs/:id/leave', [C.accountOrgs, 'leave']);
|
|
409
|
+
router.post('/account/orgs/:id/invite', [C.accountOrgs, 'invite']);
|
|
410
|
+
router.post('/account/orgs/:id/members/:accountId/remove', [C.accountOrgs, 'removeMember']);
|
|
411
|
+
router.post('/account/orgs/:id/invitations/:invId/revoke', [C.accountOrgs, 'revokeInvitation']);
|
|
412
|
+
// JSON endpoints for React hooks (authkit-react).
|
|
413
|
+
router.get('/account/orgs/json', [C.accountOrgs, 'listJson']);
|
|
414
|
+
router.get('/account/orgs/invitations/json', [C.accountOrgs, 'listInvitationsJson']);
|
|
415
|
+
router.get('/account/orgs/:id/json', [C.accountOrgs, 'showJson']);
|
|
416
|
+
}
|
|
290
417
|
// ─── Account self-service JSON API (authkit-react TanStack hooks) ─────
|
|
291
418
|
// ⚠️ ORDER MATTERS: fixed-segment routes before parameterised ones.
|
|
292
419
|
// Registered INSIDE the accountGuard group → same session-auth protection.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_MESSAGES, translate } from '../i18n.js';
|
|
2
|
+
import { getAccountLoginUrl } from '../account_login_url.js';
|
|
2
3
|
/**
|
|
3
4
|
* Resolve o catálogo de mensagens ativo a partir do `authkit.server` (config
|
|
4
5
|
* resolvida com o locale do host). Defensivo: se o container/serviço não estiver
|
|
@@ -28,7 +29,11 @@ async function resolveMessagesFromCtx(ctx) {
|
|
|
28
29
|
export async function renderEdgeView(ctx, view, props) {
|
|
29
30
|
const messages = await resolveMessagesFromCtx(ctx);
|
|
30
31
|
const t = (key, params) => translate(messages, key, params);
|
|
31
|
-
|
|
32
|
+
// `loginUrl` como prop global: as views que linkam "faça login" (ex.: `otp-unlock`)
|
|
33
|
+
// usam o destino configurável (`accountLoginUrl`) em vez do `/account/login` fixo,
|
|
34
|
+
// que pode estar desmontado. Props explícitas ainda têm precedência (spread depois).
|
|
35
|
+
const loginUrl = getAccountLoginUrl();
|
|
36
|
+
return ctx.view.render(`authkit::${view}`, { loginUrl, ...props, t, messages });
|
|
32
37
|
}
|
|
33
38
|
/** Renderer do seam para hosts Edge. As views são donas-da-lib (disco `authkit::`). */
|
|
34
39
|
export function edgeRenderer() {
|
|
@@ -66,5 +66,60 @@ export type AuthkitScreen = 'login' | 'signup' | 'consent' | 'forgot' | 'reset'
|
|
|
66
66
|
* | `returnTo` | `string \| null` | Caminho interno de destino pós-login (já validado pelo servidor — só caminhos internos). Quando presente, o formulário deve incluir `<input type="hidden" name="return_to" value={returnTo} />`. O servidor revalida o valor no POST; hosts com tela custom precisam propagar esse hidden input. |
|
|
67
67
|
* | `error` | `string \| undefined` | Mensagem de erro de autenticação localizada (credenciais inválidas, conta bloqueada, etc.). |
|
|
68
68
|
* | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
|
|
69
|
+
*
|
|
70
|
+
* ### Props da tela `account/security`
|
|
71
|
+
*
|
|
72
|
+
* | Prop | Tipo | Descrição |
|
|
73
|
+
* |-------------------------|---------------------|-----------|
|
|
74
|
+
* | `csrfToken` | `string` | Token CSRF para os formulários da tela. |
|
|
75
|
+
* | `supported` | `boolean` | `false` quando o store não suporta o self-service de segurança (troca de senha/e-mail) — a tela deve degradar. |
|
|
76
|
+
* | `profileSupported` | `boolean` | `true` quando o store suporta editar nome/avatar (`updateProfile`). |
|
|
77
|
+
* | `avatarUploadSupported` | `boolean` | `true` quando algum backend (drive OU media) pode armazenar o upload de avatar. |
|
|
78
|
+
* | `email` | `string` | E-mail atual da conta (`''` se ausente). |
|
|
79
|
+
* | `name` | `string` | Nome atual da conta (`''` se ausente). |
|
|
80
|
+
* | `avatarUrl` | `string` | URL do avatar atual (`''` se ausente). |
|
|
81
|
+
* | `passwordChanged` | `string \| null` | Flash de sucesso da troca de senha (mensagem localizada) ou `null`. |
|
|
82
|
+
* | `emailChangeRequested` | `string \| null` | Flash: link de confirmação de troca de e-mail enviado (ou cancelamento) ou `null`. |
|
|
83
|
+
* | `emailChanged` | `string \| null` | Flash: troca de e-mail concluída ou `null`. |
|
|
84
|
+
* | `profileUpdated` | `string \| null` | Flash: perfil atualizado ou `null`. |
|
|
85
|
+
* | `error` | `string \| null` | Flash de erro de segurança (senha inválida, e-mail em uso, política violada) ou `null`. |
|
|
86
|
+
* | `trustedDevicesEnabled` | `boolean` | `true` quando o recurso de dispositivos confiáveis está ligado. |
|
|
87
|
+
* | `trustedDevicesRevoked` | `string \| null` | Flash: confiança deste navegador revogada ou `null`. |
|
|
88
|
+
* | `sessionsSupported` | `boolean` | `true` quando o adapter OIDC enumera as sessões ativas da conta. |
|
|
89
|
+
* | `sessions` | `Array<{ loginTs: string; browser: string; os: string; ip: string; location: string }>` | Sessões ativas da própria conta (vazio quando não suportado). `loginTs` é ISO ou `''`. |
|
|
90
|
+
* | `exportSupported` | `boolean` | Sempre `true` — export de dados (portabilidade/LGPD) disponível para a conta logada. |
|
|
91
|
+
* | `deletionSupported` | `boolean` | `true` quando o store suporta hard delete (danger zone). |
|
|
92
|
+
* | `deleteError` | `string \| null` | Flash de erro da confirmação de deleção ou `null`. |
|
|
93
|
+
* | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
|
|
94
|
+
*
|
|
95
|
+
* ### Props da tela `account/mfa`
|
|
96
|
+
*
|
|
97
|
+
* | Prop | Tipo | Descrição |
|
|
98
|
+
* |---------------------|---------------------|-----------|
|
|
99
|
+
* | `csrfToken` | `string` | Token CSRF para os formulários de enroll/confirm/disable e passkeys. |
|
|
100
|
+
* | `enabled` | `boolean` | `true` quando o TOTP já está confirmado (habilitado) para a conta. |
|
|
101
|
+
* | `recoveryCodes` | `string[] \| null` | Códigos de recuperação recém-gerados (exibidos UMA vez após enroll/confirm) ou `null`. |
|
|
102
|
+
* | `passkeysSupported` | `boolean` | `true` quando o store persiste credenciais WebAuthn (passkeys). |
|
|
103
|
+
* | `passkeys` | `Array<{ id: string; label?: string; createdAt: string }>` | Passkeys cadastradas (vazio quando não suportado). `id` é base64url; `createdAt` é ISO. |
|
|
104
|
+
* | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
|
|
105
|
+
*
|
|
106
|
+
* ### Props da tela `account/confirm` (sudo — confirmar identidade)
|
|
107
|
+
*
|
|
108
|
+
* | Prop | Tipo | Descrição |
|
|
109
|
+
* |---------------|---------------------|-----------|
|
|
110
|
+
* | `csrfToken` | `string` | Token CSRF para o POST de cada método. |
|
|
111
|
+
* | `returnTo` | `string \| null` | Caminho interno de destino após confirmar (validado pelo servidor) ou `null`. |
|
|
112
|
+
* | `error` | `string \| null` | Flash de erro da última tentativa de confirmação ou `null`. |
|
|
113
|
+
* | `notice` | `string \| null` | Flash informativo (ex.: "link de confirmação enviado") ou `null`. |
|
|
114
|
+
* | `methods` | `Array<{ id: string; labelKey: string; kind: 'form' \| 'action' \| 'redirect' \| 'webauthn'; endpoint: string; fields?: Array<{ name: string; type: 'password' \| 'text'; labelKey: string }> }>` | Métodos de sudo disponíveis para a conta. A tela renderiza por `kind`; `endpoint` é o POST de verificação (`webauthn` pede options em `${endpoint}/options`). |
|
|
115
|
+
* | `preferredId` | `string \| null` | `id` do último método usado (destaque na UI) ou `null`. |
|
|
116
|
+
* | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
|
|
117
|
+
*
|
|
118
|
+
* ### Props da tela `account/email-confirmed` (terminal do link de troca de e-mail)
|
|
119
|
+
*
|
|
120
|
+
* | Prop | Tipo | Descrição |
|
|
121
|
+
* |------------|----------------|-----------|
|
|
122
|
+
* | `ok` | `boolean` | `true` quando o token era válido e o novo e-mail foi aplicado; `false` para token inválido/expirado ou store sem suporte. A tela mostra sucesso ou falha conforme o valor. |
|
|
123
|
+
* | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
|
|
69
124
|
*/
|
|
70
125
|
export declare function inertiaRenderer(opts: InertiaRendererOptions): (ctx: HttpContext, view: string, props: Record<string, unknown>) => Promise<any>;
|
|
@@ -37,6 +37,61 @@ async function resolveMessagesFromCtx(ctx) {
|
|
|
37
37
|
* | `returnTo` | `string \| null` | Caminho interno de destino pós-login (já validado pelo servidor — só caminhos internos). Quando presente, o formulário deve incluir `<input type="hidden" name="return_to" value={returnTo} />`. O servidor revalida o valor no POST; hosts com tela custom precisam propagar esse hidden input. |
|
|
38
38
|
* | `error` | `string \| undefined` | Mensagem de erro de autenticação localizada (credenciais inválidas, conta bloqueada, etc.). |
|
|
39
39
|
* | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
|
|
40
|
+
*
|
|
41
|
+
* ### Props da tela `account/security`
|
|
42
|
+
*
|
|
43
|
+
* | Prop | Tipo | Descrição |
|
|
44
|
+
* |-------------------------|---------------------|-----------|
|
|
45
|
+
* | `csrfToken` | `string` | Token CSRF para os formulários da tela. |
|
|
46
|
+
* | `supported` | `boolean` | `false` quando o store não suporta o self-service de segurança (troca de senha/e-mail) — a tela deve degradar. |
|
|
47
|
+
* | `profileSupported` | `boolean` | `true` quando o store suporta editar nome/avatar (`updateProfile`). |
|
|
48
|
+
* | `avatarUploadSupported` | `boolean` | `true` quando algum backend (drive OU media) pode armazenar o upload de avatar. |
|
|
49
|
+
* | `email` | `string` | E-mail atual da conta (`''` se ausente). |
|
|
50
|
+
* | `name` | `string` | Nome atual da conta (`''` se ausente). |
|
|
51
|
+
* | `avatarUrl` | `string` | URL do avatar atual (`''` se ausente). |
|
|
52
|
+
* | `passwordChanged` | `string \| null` | Flash de sucesso da troca de senha (mensagem localizada) ou `null`. |
|
|
53
|
+
* | `emailChangeRequested` | `string \| null` | Flash: link de confirmação de troca de e-mail enviado (ou cancelamento) ou `null`. |
|
|
54
|
+
* | `emailChanged` | `string \| null` | Flash: troca de e-mail concluída ou `null`. |
|
|
55
|
+
* | `profileUpdated` | `string \| null` | Flash: perfil atualizado ou `null`. |
|
|
56
|
+
* | `error` | `string \| null` | Flash de erro de segurança (senha inválida, e-mail em uso, política violada) ou `null`. |
|
|
57
|
+
* | `trustedDevicesEnabled` | `boolean` | `true` quando o recurso de dispositivos confiáveis está ligado. |
|
|
58
|
+
* | `trustedDevicesRevoked` | `string \| null` | Flash: confiança deste navegador revogada ou `null`. |
|
|
59
|
+
* | `sessionsSupported` | `boolean` | `true` quando o adapter OIDC enumera as sessões ativas da conta. |
|
|
60
|
+
* | `sessions` | `Array<{ loginTs: string; browser: string; os: string; ip: string; location: string }>` | Sessões ativas da própria conta (vazio quando não suportado). `loginTs` é ISO ou `''`. |
|
|
61
|
+
* | `exportSupported` | `boolean` | Sempre `true` — export de dados (portabilidade/LGPD) disponível para a conta logada. |
|
|
62
|
+
* | `deletionSupported` | `boolean` | `true` quando o store suporta hard delete (danger zone). |
|
|
63
|
+
* | `deleteError` | `string \| null` | Flash de erro da confirmação de deleção ou `null`. |
|
|
64
|
+
* | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
|
|
65
|
+
*
|
|
66
|
+
* ### Props da tela `account/mfa`
|
|
67
|
+
*
|
|
68
|
+
* | Prop | Tipo | Descrição |
|
|
69
|
+
* |---------------------|---------------------|-----------|
|
|
70
|
+
* | `csrfToken` | `string` | Token CSRF para os formulários de enroll/confirm/disable e passkeys. |
|
|
71
|
+
* | `enabled` | `boolean` | `true` quando o TOTP já está confirmado (habilitado) para a conta. |
|
|
72
|
+
* | `recoveryCodes` | `string[] \| null` | Códigos de recuperação recém-gerados (exibidos UMA vez após enroll/confirm) ou `null`. |
|
|
73
|
+
* | `passkeysSupported` | `boolean` | `true` quando o store persiste credenciais WebAuthn (passkeys). |
|
|
74
|
+
* | `passkeys` | `Array<{ id: string; label?: string; createdAt: string }>` | Passkeys cadastradas (vazio quando não suportado). `id` é base64url; `createdAt` é ISO. |
|
|
75
|
+
* | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
|
|
76
|
+
*
|
|
77
|
+
* ### Props da tela `account/confirm` (sudo — confirmar identidade)
|
|
78
|
+
*
|
|
79
|
+
* | Prop | Tipo | Descrição |
|
|
80
|
+
* |---------------|---------------------|-----------|
|
|
81
|
+
* | `csrfToken` | `string` | Token CSRF para o POST de cada método. |
|
|
82
|
+
* | `returnTo` | `string \| null` | Caminho interno de destino após confirmar (validado pelo servidor) ou `null`. |
|
|
83
|
+
* | `error` | `string \| null` | Flash de erro da última tentativa de confirmação ou `null`. |
|
|
84
|
+
* | `notice` | `string \| null` | Flash informativo (ex.: "link de confirmação enviado") ou `null`. |
|
|
85
|
+
* | `methods` | `Array<{ id: string; labelKey: string; kind: 'form' \| 'action' \| 'redirect' \| 'webauthn'; endpoint: string; fields?: Array<{ name: string; type: 'password' \| 'text'; labelKey: string }> }>` | Métodos de sudo disponíveis para a conta. A tela renderiza por `kind`; `endpoint` é o POST de verificação (`webauthn` pede options em `${endpoint}/options`). |
|
|
86
|
+
* | `preferredId` | `string \| null` | `id` do último método usado (destaque na UI) ou `null`. |
|
|
87
|
+
* | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
|
|
88
|
+
*
|
|
89
|
+
* ### Props da tela `account/email-confirmed` (terminal do link de troca de e-mail)
|
|
90
|
+
*
|
|
91
|
+
* | Prop | Tipo | Descrição |
|
|
92
|
+
* |------------|----------------|-----------|
|
|
93
|
+
* | `ok` | `boolean` | `true` quando o token era válido e o novo e-mail foi aplicado; `false` para token inválido/expirado ou store sem suporte. A tela mostra sucesso ou falha conforme o valor. |
|
|
94
|
+
* | `messages` | `AuthMessages` | Catálogo de mensagens i18n. |
|
|
40
95
|
*/
|
|
41
96
|
export function inertiaRenderer(opts) {
|
|
42
97
|
const allowed = opts.views ? new Set(opts.views) : null;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { password } from './methods/password.js';
|
|
2
|
+
import { passkey } from './methods/passkey.js';
|
|
3
|
+
import { oidcStepUp } from './methods/oidc_step_up.js';
|
|
4
|
+
import { magicLink } from './methods/magic_link.js';
|
|
5
|
+
/**
|
|
6
|
+
* Métodos de confirmação de identidade (sudo mode), no mesmo padrão de factory
|
|
7
|
+
* usado em `stores.*` e `retrievers.*` das libs irmãs.
|
|
8
|
+
*
|
|
9
|
+
* A lista vai em DOIS lugares, e eles precisam casar:
|
|
10
|
+
*
|
|
11
|
+
* - `config/authkit.ts` → `sudo.methods` decide o que a TELA oferece e o que os
|
|
12
|
+
* handlers ACEITAM;
|
|
13
|
+
* - `registerAuthHost(router, { sudoMethods })` decide o que tem ROTA montada.
|
|
14
|
+
*
|
|
15
|
+
* São dois porque a montagem de rotas acontece antes de o config (lazy)
|
|
16
|
+
* resolver — mesma razão de `social`/`admin`/`rateLimit`. Divergiram, a tela
|
|
17
|
+
* loga um aviso de flag-drift e o endpoint faltante dá 404.
|
|
18
|
+
*
|
|
19
|
+
* SEM `config.sudo.methods` não há como divergir: a tela cai na própria lista
|
|
20
|
+
* montada por `registerAuthHost`, a mesma que os handlers aceitam.
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* defineConfig({
|
|
24
|
+
* sudo: {
|
|
25
|
+
* methods: [
|
|
26
|
+
* sudoMethods.oidcStepUp({ url: '/auth/step-up' }),
|
|
27
|
+
* sudoMethods.magicLink(),
|
|
28
|
+
* sudoMethods.passkey(),
|
|
29
|
+
* sudoMethods.password(),
|
|
30
|
+
* ],
|
|
31
|
+
* },
|
|
32
|
+
* })
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare const sudoMethods: {
|
|
36
|
+
password: typeof password;
|
|
37
|
+
passkey: typeof passkey;
|
|
38
|
+
oidcStepUp: typeof oidcStepUp;
|
|
39
|
+
magicLink: typeof magicLink;
|
|
40
|
+
};
|
|
41
|
+
export type { SudoMethod, SudoContext, SudoMethodDescriptor, SudoRouteHelpers } from './types.js';
|
|
42
|
+
/**
|
|
43
|
+
* Montagem do `SudoContext` a partir do `HttpContext`. Reexportado aqui por
|
|
44
|
+
* simetria com `sudoMethods` e os tipos: quem escreve um método (ou a rota de
|
|
45
|
+
* callback do `oidcStepUp`) precisa dos três.
|
|
46
|
+
*/
|
|
47
|
+
export { sudoContextFrom } from './runtime.js';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { password } from './methods/password.js';
|
|
2
|
+
import { passkey } from './methods/passkey.js';
|
|
3
|
+
import { oidcStepUp } from './methods/oidc_step_up.js';
|
|
4
|
+
import { magicLink } from './methods/magic_link.js';
|
|
5
|
+
/**
|
|
6
|
+
* Métodos de confirmação de identidade (sudo mode), no mesmo padrão de factory
|
|
7
|
+
* usado em `stores.*` e `retrievers.*` das libs irmãs.
|
|
8
|
+
*
|
|
9
|
+
* A lista vai em DOIS lugares, e eles precisam casar:
|
|
10
|
+
*
|
|
11
|
+
* - `config/authkit.ts` → `sudo.methods` decide o que a TELA oferece e o que os
|
|
12
|
+
* handlers ACEITAM;
|
|
13
|
+
* - `registerAuthHost(router, { sudoMethods })` decide o que tem ROTA montada.
|
|
14
|
+
*
|
|
15
|
+
* São dois porque a montagem de rotas acontece antes de o config (lazy)
|
|
16
|
+
* resolver — mesma razão de `social`/`admin`/`rateLimit`. Divergiram, a tela
|
|
17
|
+
* loga um aviso de flag-drift e o endpoint faltante dá 404.
|
|
18
|
+
*
|
|
19
|
+
* SEM `config.sudo.methods` não há como divergir: a tela cai na própria lista
|
|
20
|
+
* montada por `registerAuthHost`, a mesma que os handlers aceitam.
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* defineConfig({
|
|
24
|
+
* sudo: {
|
|
25
|
+
* methods: [
|
|
26
|
+
* sudoMethods.oidcStepUp({ url: '/auth/step-up' }),
|
|
27
|
+
* sudoMethods.magicLink(),
|
|
28
|
+
* sudoMethods.passkey(),
|
|
29
|
+
* sudoMethods.password(),
|
|
30
|
+
* ],
|
|
31
|
+
* },
|
|
32
|
+
* })
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export const sudoMethods = { password, passkey, oidcStepUp, magicLink };
|
|
36
|
+
/**
|
|
37
|
+
* Montagem do `SudoContext` a partir do `HttpContext`. Reexportado aqui por
|
|
38
|
+
* simetria com `sudoMethods` e os tipos: quem escreve um método (ou a rota de
|
|
39
|
+
* callback do `oidcStepUp`) precisa dos três.
|
|
40
|
+
*/
|
|
41
|
+
export { sudoContextFrom } from './runtime.js';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { SudoContext, SudoMethod } from '../types.js';
|
|
2
|
+
/** Token de sudo pendente, guardado na sessão que o pediu. */
|
|
3
|
+
export declare const SUDO_LINK_SESSION_KEY = "authkit_sudo_link";
|
|
4
|
+
/** Mesma janela dos magic links de login. */
|
|
5
|
+
export declare const SUDO_LINK_TTL_MS: number;
|
|
6
|
+
/**
|
|
7
|
+
* Emite um token de sudo e guarda o HASH na sessão que pediu.
|
|
8
|
+
*
|
|
9
|
+
* O TOKEN É PRÓPRIO, DE ESCOPO SUDO — nunca o token de login
|
|
10
|
+
* (`issueMagicLinkToken`/`consumeMagicLinkToken` do `AccountStore`). Aquele é
|
|
11
|
+
* credencial de AUTENTICAÇÃO: reusá-lo faria de um link de sudo vazado uma
|
|
12
|
+
* sessão completa.
|
|
13
|
+
*
|
|
14
|
+
* | propriedade | valor | razão |
|
|
15
|
+
* |---|---|---|
|
|
16
|
+
* | geração | `randomBytes(32)` hex | entropia de credencial |
|
|
17
|
+
* | armazenamento | HASH na sessão que pediu | não guarda o segredo em claro |
|
|
18
|
+
* | escopo | só marca sudo | nunca autentica |
|
|
19
|
+
* | validade | 5 min | mesma janela dos magic links de login |
|
|
20
|
+
* | uso | único (apagado no consumo) | replay |
|
|
21
|
+
* | navegador | só o mesmo (vive na sessão) | step-up é reprova de QUEM ESTÁ ALI |
|
|
22
|
+
* | conta | vinculado ao `accountId` emissor | sessão sobrevive à troca de conta |
|
|
23
|
+
*
|
|
24
|
+
* O "só mesmo navegador" é propriedade desejada aqui, diferente do magic link
|
|
25
|
+
* de login, onde é limitação conhecida.
|
|
26
|
+
*
|
|
27
|
+
* Exportada (em vez de membro `__` do método) para ser testável sem furar a
|
|
28
|
+
* API pública do `SudoMethod`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function issueSudoLinkToken(c: SudoContext): string;
|
|
31
|
+
/**
|
|
32
|
+
* Consome o token: single-use, vinculado à conta emissora, expira em 5 min,
|
|
33
|
+
* comparação em tempo constante.
|
|
34
|
+
*/
|
|
35
|
+
export declare function verifySudoLinkToken(c: SudoContext, token: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Confirmação por link enviado ao e-mail da conta.
|
|
38
|
+
*
|
|
39
|
+
* Depende do hook `mail.onSudoLink`, DISTINTO de `mail.onMagicLink` justamente
|
|
40
|
+
* para que o host não confunda um link que autentica com um que só concede
|
|
41
|
+
* sudo a quem já está logado. Sem o hook, o método fica indisponível.
|
|
42
|
+
*/
|
|
43
|
+
export declare function magicLink(): SudoMethod;
|