@adonis-agora/authkit-server 0.34.1 → 0.36.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/host/views/signup.edge +13 -5
- package/build/index.d.ts +1 -0
- package/build/index.js +5 -0
- package/build/providers/authkit_server_provider.js +21 -13
- package/build/src/define_config.d.ts +40 -1
- package/build/src/define_config.js +12 -1
- package/build/src/host/account_api/account_api_controller.js +2 -0
- package/build/src/host/adonis_auth_sync.d.ts +27 -0
- package/build/src/host/adonis_auth_sync.js +47 -0
- package/build/src/host/adonis_auth_user_provider.d.ts +38 -0
- package/build/src/host/adonis_auth_user_provider.js +74 -0
- package/build/src/host/app_key.d.ts +18 -0
- package/build/src/host/app_key.js +28 -0
- package/build/src/host/controllers/account_security_controller.js +2 -0
- package/build/src/host/controllers/account_session_controller.js +8 -0
- package/build/src/host/controllers/registration_controller.d.ts +1 -0
- package/build/src/host/controllers/registration_controller.js +59 -2
- package/build/src/host/i18n.d.ts +2 -0
- package/build/src/host/i18n.js +2 -0
- package/build/src/host/validators.d.ts +14 -0
- package/build/src/host/validators.js +5 -0
- package/package.json +17 -12
|
@@ -36,11 +36,19 @@
|
|
|
36
36
|
class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none transition focus:border-gray-900 focus:ring-2 focus:ring-gray-900" />
|
|
37
37
|
</div>
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<
|
|
42
|
-
class="
|
|
43
|
-
|
|
39
|
+
{{-- Cadastro passwordless: sem campo de senha. O login vem por magic link. --}}
|
|
40
|
+
@if(!passwordlessSignup)
|
|
41
|
+
<div class="mt-4">
|
|
42
|
+
<label for="password" class="mb-1 block text-sm font-medium text-gray-700">{{ t('signup.password_label') }}</label>
|
|
43
|
+
<input id="password" name="password" type="password" required minlength="8"
|
|
44
|
+
class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none transition focus:border-gray-900 focus:ring-2 focus:ring-gray-900" />
|
|
45
|
+
</div>
|
|
46
|
+
@end
|
|
47
|
+
|
|
48
|
+
{{-- Passwordless: confirmação de link enviado (anti-enumeração). --}}
|
|
49
|
+
@if(magicLinkSent)
|
|
50
|
+
<p class="mt-4 rounded-lg bg-green-50 px-3 py-2 text-sm text-green-700">{{ t('signup.magic_link_sent') }}</p>
|
|
51
|
+
@end
|
|
44
52
|
|
|
45
53
|
{{-- Bot protection: container do widget (config-trusted; HTML do host renderizado raw). --}}
|
|
46
54
|
@if(botProtection && botProtection.html)
|
package/build/index.d.ts
CHANGED
|
@@ -113,3 +113,4 @@ export { getAccountId, hasAccountSession, consoleLoginUrl, } from './src/host/co
|
|
|
113
113
|
export { ACCOUNT_SESSION_KEY } from './src/host/middleware/account_auth.js';
|
|
114
114
|
export { SUDO_SESSION_KEY, SUDO_MODE_DEFAULTS, requireSudo, isSudoActive, markSudo, resolveEffectiveSudoMode, } from './src/host/sudo_mode.js';
|
|
115
115
|
export type { SudoModeSetting, ResolvedSudoModeSetting, } from './src/host/sudo_mode.js';
|
|
116
|
+
export { authkitUserProvider } from './src/host/adonis_auth_user_provider.js';
|
package/build/index.js
CHANGED
|
@@ -74,3 +74,8 @@ export { getAccountId, hasAccountSession, consoleLoginUrl, } from './src/host/co
|
|
|
74
74
|
export { ACCOUNT_SESSION_KEY } from './src/host/middleware/account_auth.js';
|
|
75
75
|
// Sudo mode — helpers for host controllers that require step-up authentication.
|
|
76
76
|
export { SUDO_SESSION_KEY, SUDO_MODE_DEFAULTS, requireSudo, isSudoActive, markSudo, resolveEffectiveSudoMode, } from './src/host/sudo_mode.js';
|
|
77
|
+
// @adonisjs/auth integration (opt-in) — user provider for config/auth.ts's
|
|
78
|
+
// sessionGuard(), backed by authkit's own accountStore. Pair with
|
|
79
|
+
// `adonisAuth: { guard: '...' }` in config/authkit.ts so the account console's
|
|
80
|
+
// login/logout also sync ctx.auth. See src/host/adonis_auth_user_provider.ts.
|
|
81
|
+
export { authkitUserProvider } from './src/host/adonis_auth_user_provider.js';
|
|
@@ -12,12 +12,27 @@ import { defaultEncryptForStore } from '../src/define_config.js';
|
|
|
12
12
|
import { resolveKeystoreVault, KeystoreManager } from '../src/keys/keystore_manager.js';
|
|
13
13
|
import { KeystoreCodec } from '../src/keys/keystore_codec.js';
|
|
14
14
|
import { loadEncryptionService } from '../src/keys/keystore_crypto.js';
|
|
15
|
+
import { resolveAppKey } from '../src/host/app_key.js';
|
|
15
16
|
export default class AuthkitServerProvider {
|
|
16
17
|
app;
|
|
17
18
|
constructor(app) {
|
|
18
19
|
this.app = app;
|
|
19
20
|
}
|
|
20
21
|
async boot() {
|
|
22
|
+
// EAGER + LOUD (fail-fast): se o host configurou `config/authkit.ts`, a
|
|
23
|
+
// `appKey` é indispensável — o oidc-provider a usa para assinar TODOS os
|
|
24
|
+
// cookies (keygrip). Antes, essa validação só rodava dentro do factory
|
|
25
|
+
// lazy do `authkit.server` (registrado abaixo): sem nada que resolvesse o
|
|
26
|
+
// binding no boot, o app subia normalmente e o erro só aparecia (a) na
|
|
27
|
+
// 1ª request que tocasse uma rota do authkit, como um 500 sem contexto
|
|
28
|
+
// fora do modo dev, OU (b) nunca, porque `#startKeystoreReloadPoll`/
|
|
29
|
+
// `#startKeyRotationScheduler` engolem qualquer erro do `container.make`
|
|
30
|
+
// via `.catch(() => null)`. Validar aqui, fora do try/catch fail-safe
|
|
31
|
+
// abaixo, propaga o erro para `app.boot()` e derruba o boot do processo
|
|
32
|
+
// com a mensagem certa — em vez de um 500 misterioso depois.
|
|
33
|
+
if (this.app.config.get('authkit')) {
|
|
34
|
+
resolveAppKey(this.app);
|
|
35
|
+
}
|
|
21
36
|
// Config locks: trava as settings definidas explicitamente no defineConfig
|
|
22
37
|
// (config vence em runtime; a UI/Admin API não pode alterá-las). Fail-safe:
|
|
23
38
|
// qualquer erro → sem locks (comportamento legado).
|
|
@@ -76,19 +91,12 @@ export default class AuthkitServerProvider {
|
|
|
76
91
|
if (!config) {
|
|
77
92
|
throw new RuntimeException('Config inválido em "config/authkit.ts". Use o método defineConfig de @adonis-agora/authkit-server.');
|
|
78
93
|
}
|
|
79
|
-
// `app.appKey` pode ser uma string crua ou um `Secret` do AdonisJS
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
const
|
|
85
|
-
const appKey = rawAppKey && typeof rawAppKey.release === 'function'
|
|
86
|
-
? rawAppKey.release()
|
|
87
|
-
: rawAppKey;
|
|
88
|
-
if (!appKey || typeof appKey !== 'string') {
|
|
89
|
-
throw new RuntimeException('APP_KEY ausente: defina `export const appKey = new Secret(env.get(\'APP_KEY\'))` em config/app.ts. ' +
|
|
90
|
-
'O @adonis-agora/authkit-server precisa dele para assinar os cookies do oidc-provider.');
|
|
91
|
-
}
|
|
94
|
+
// `app.appKey` pode ser uma string crua ou um `Secret` do AdonisJS — ver
|
|
95
|
+
// `resolveAppKey` para o porquê e para a mensagem de erro (agora também
|
|
96
|
+
// validada EAGER no `boot()`, acima; mantida aqui para o caso raro de
|
|
97
|
+
// alguém resolver `authkit.server` diretamente sem passar pelo boot do
|
|
98
|
+
// provider, ex.: testes unitários do binding).
|
|
99
|
+
const appKey = resolveAppKey(this.app);
|
|
92
100
|
const metrics = await this.app.container.make('authkit.metrics');
|
|
93
101
|
const jwksInput = config.jwksConfig;
|
|
94
102
|
let jwksLoader;
|
|
@@ -322,10 +322,19 @@ export interface PasswordlessConfigInput {
|
|
|
322
322
|
magicLink?: boolean;
|
|
323
323
|
/** Liga o "entrar com passkey" antes da senha. Default: false. */
|
|
324
324
|
passkeyFirst?: boolean;
|
|
325
|
+
/**
|
|
326
|
+
* Liga o cadastro público passwordless: o signup pede só e-mail + nome (sem
|
|
327
|
+
* senha), cria a conta com uma senha aleatória inutilizável e envia um magic
|
|
328
|
+
* link que finaliza o login (o mesmo fluxo do login por magic link). Default:
|
|
329
|
+
* false. Exige que o accountStore implemente {@link MagicLinkCapability}; sem
|
|
330
|
+
* ela o cadastro passwordless fica indisponível e o fluxo de senha segue valendo.
|
|
331
|
+
*/
|
|
332
|
+
signup?: boolean;
|
|
325
333
|
}
|
|
326
334
|
export interface ResolvedPasswordlessConfig {
|
|
327
335
|
magicLink: boolean;
|
|
328
336
|
passkeyFirst: boolean;
|
|
337
|
+
signup: boolean;
|
|
329
338
|
}
|
|
330
339
|
export declare function resolvePasswordless(input?: PasswordlessConfigInput): ResolvedPasswordlessConfig;
|
|
331
340
|
/**
|
|
@@ -521,7 +530,12 @@ export interface AuthServerConfigInput {
|
|
|
521
530
|
* Hosts que preferem mandar o usuário direto pro app podem apontar pra rota deles.
|
|
522
531
|
*/
|
|
523
532
|
accountHome?: string;
|
|
524
|
-
/**
|
|
533
|
+
/**
|
|
534
|
+
* Renderer de páginas do host (Inertia ou Edge). Default (quando omitido):
|
|
535
|
+
* {@link edgeRenderer}. Sem isso, TODA request a `/account/*` e
|
|
536
|
+
* `/auth/interaction/*` estourava um 500 sem explicação — `render` era
|
|
537
|
+
* `undefined` e o controller chamava `render(ctx, ...)` diretamente.
|
|
538
|
+
*/
|
|
525
539
|
render?: AuthHostRenderer;
|
|
526
540
|
/** Configuração de branding por cliente. */
|
|
527
541
|
branding?: BrandingConfig;
|
|
@@ -662,6 +676,27 @@ export interface AuthServerConfigInput {
|
|
|
662
676
|
accountLifecycle?: {
|
|
663
677
|
durable?: boolean;
|
|
664
678
|
};
|
|
679
|
+
/**
|
|
680
|
+
* Integração OPT-IN com `@adonisjs/auth`. authkit-server autentica a área de
|
|
681
|
+
* conta (`/account/*`) com sua PRÓPRIA sessão (`ACCOUNT_SESSION_KEY`, via
|
|
682
|
+
* `@adonisjs/session` diretamente) — nunca chamou `@adonisjs/auth` em lugar
|
|
683
|
+
* nenhum, então `ctx.auth.user`/`middleware.auth()`/Bouncer's
|
|
684
|
+
* `() => ctx.auth.user` ficavam sempre `undefined` num host que os use.
|
|
685
|
+
*
|
|
686
|
+
* Quando `adonisAuth.guard` é setado, `AccountSessionController#login`/`logout`
|
|
687
|
+
* TAMBÉM chamam `ctx.auth.use(guard).login(account)`/`.logout()` do guard
|
|
688
|
+
* indicado — em paralelo ao cookie bespoke do authkit, que continua sendo a
|
|
689
|
+
* fonte de verdade das rotas do próprio authkit. Requer um user provider
|
|
690
|
+
* plugado nesse guard em `config/auth.ts` — veja {@link authkitUserProvider}.
|
|
691
|
+
*
|
|
692
|
+
* Default (ausente): comportamento de sempre, sem tocar em `ctx.auth`.
|
|
693
|
+
* Degrada limpo mesmo sem `@adonisjs/auth` instalado (peer opcional): o
|
|
694
|
+
* código só acessa `ctx.auth` quando esta opção está setada E a propriedade
|
|
695
|
+
* existe no ctx (host com `@adonisjs/auth` de fato inicializado).
|
|
696
|
+
*/
|
|
697
|
+
adonisAuth?: {
|
|
698
|
+
guard: string;
|
|
699
|
+
};
|
|
665
700
|
}
|
|
666
701
|
export interface ResolvedServerConfig {
|
|
667
702
|
issuer: string;
|
|
@@ -755,6 +790,10 @@ export interface ResolvedServerConfig {
|
|
|
755
790
|
};
|
|
756
791
|
/** Keys de `auth_settings` travadas por terem sido definidas no defineConfig. */
|
|
757
792
|
lockedSettingKeys: string[];
|
|
793
|
+
/** Integração opt-in com `@adonisjs/auth` (ausente = não integrado; comportamento de sempre). */
|
|
794
|
+
adonisAuth?: {
|
|
795
|
+
guard: string;
|
|
796
|
+
};
|
|
758
797
|
}
|
|
759
798
|
export declare function toSeconds(value: string | number | undefined, fallback: number): number;
|
|
760
799
|
/**
|
|
@@ -9,6 +9,7 @@ import { resolveMessages, } from "./host/i18n.js";
|
|
|
9
9
|
import { resolveTrustedDevices, } from "./host/trusted_device.js";
|
|
10
10
|
import { resolveBotProtection, } from "./host/bot_protection.js";
|
|
11
11
|
import { deriveLockedSettingKeys } from "./host/config_locks.js";
|
|
12
|
+
import { edgeRenderer } from "./host/renderers/edge_renderer.js";
|
|
12
13
|
export { adapters };
|
|
13
14
|
const RATE_LIMIT_DEFAULTS = {
|
|
14
15
|
login: { points: 10, duration: "1 min" },
|
|
@@ -91,6 +92,7 @@ export function resolvePasswordless(input) {
|
|
|
91
92
|
return {
|
|
92
93
|
magicLink: input?.magicLink ?? false,
|
|
93
94
|
passkeyFirst: input?.passkeyFirst ?? false,
|
|
95
|
+
signup: input?.signup ?? false,
|
|
94
96
|
};
|
|
95
97
|
}
|
|
96
98
|
export function resolveLogin(input) {
|
|
@@ -296,7 +298,14 @@ export function defineConfig(config) {
|
|
|
296
298
|
accountStore: config.accountStore,
|
|
297
299
|
patStore: config.patStore,
|
|
298
300
|
mountPath: config.mountPath ?? "/oidc",
|
|
299
|
-
|
|
301
|
+
// Default de runtime: sem isso, `render` ficava `undefined` e TODA
|
|
302
|
+
// request a `/account/*`/`/auth/interaction/*` estourava um 500 sem
|
|
303
|
+
// contexto (os controllers fazem `cfg.render!(...)`). `edgeRenderer()`
|
|
304
|
+
// só usa `ctx.view` quando de fato invocado por uma request — não
|
|
305
|
+
// importa `edge.js` eagerly, então é seguro construir mesmo em hosts
|
|
306
|
+
// sem o peer opcional instalado (só quebraria se a request realmente
|
|
307
|
+
// chegasse sem Edge configurado, exatamente como hoje).
|
|
308
|
+
render: config.render ?? edgeRenderer(),
|
|
300
309
|
branding: config.branding,
|
|
301
310
|
social: config.social,
|
|
302
311
|
patIntrospectionSecret: config.patIntrospectionSecret,
|
|
@@ -346,6 +355,8 @@ export function defineConfig(config) {
|
|
|
346
355
|
// Keys de auth_settings travadas porque foram definidas no defineConfig:
|
|
347
356
|
// config vence e a UI/Admin API não pode alterá-las (ver host/config_locks.ts).
|
|
348
357
|
lockedSettingKeys: deriveLockedSettingKeys(config),
|
|
358
|
+
// Opt-in: ausente = authkit nunca toca `ctx.auth` (comportamento de sempre).
|
|
359
|
+
adonisAuth: config.adonisAuth,
|
|
349
360
|
};
|
|
350
361
|
});
|
|
351
362
|
}
|
|
@@ -35,6 +35,7 @@ import { AdminSessionsService } from '../admin_sessions_service.js';
|
|
|
35
35
|
import { enrichSessionsWithContext } from '../session_context.js';
|
|
36
36
|
import { PasswordPolicyError } from '../../password/password_manager.js';
|
|
37
37
|
import { resolveRuntimeSettings } from '../runtime_settings.js';
|
|
38
|
+
import { syncAdonisAuthLogout } from '../adonis_auth_sync.js';
|
|
38
39
|
import { resolveEffectiveEmailChange, resolveEffectivePasswordHistory, } from '../runtime_toggles.js';
|
|
39
40
|
import { dispatchSecurityNotice } from '../security_notice_service.js';
|
|
40
41
|
import { requireSudo, isSudoActive, SUDO_MODE_DEFAULTS, resolveEffectiveSudoMode } from '../sudo_mode.js';
|
|
@@ -515,6 +516,7 @@ export default class AccountApiController {
|
|
|
515
516
|
// Encerrar a sessão Adonis do console (logout global do self-service).
|
|
516
517
|
ctx.session.forget(ACCOUNT_SESSION_KEY);
|
|
517
518
|
await ctx.session.regenerate();
|
|
519
|
+
await syncAdonisAuthLogout(ctx, cfg);
|
|
518
520
|
return { ok: true, signedOut: true, ...result };
|
|
519
521
|
}
|
|
520
522
|
// ─── GET /account/api/apps ──────────────────────────────────────────────
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
2
|
+
import type { ResolvedServerConfig } from '../define_config.js';
|
|
3
|
+
import type { AuthAccount } from '../accounts/account_store.js';
|
|
4
|
+
/**
|
|
5
|
+
* Sincroniza `ctx.auth` (`@adonisjs/auth`) com um login bem-sucedido no console
|
|
6
|
+
* de conta do authkit. Ver `adonisAuth` em `define_config.ts` pro porquê: sem
|
|
7
|
+
* isso, `ctx.auth.user` nunca é populado por nenhum fluxo do authkit-server —
|
|
8
|
+
* mesmo num host que roda `@adonisjs/auth` normalmente.
|
|
9
|
+
*
|
|
10
|
+
* NO-OP quando:
|
|
11
|
+
* - `adonisAuth.guard` não está configurado (opt-in; default é não tocar em
|
|
12
|
+
* `ctx.auth` — comportamento de sempre).
|
|
13
|
+
* - `ctx.auth` não existe nesta request (host sem `InitializeAuthMiddleware`
|
|
14
|
+
* de `@adonisjs/auth` registrado — o peer é opcional).
|
|
15
|
+
*
|
|
16
|
+
* BEST-EFFORT: uma falha aqui (ex.: guard mal configurado em `config/auth.ts`)
|
|
17
|
+
* NUNCA quebra o login do authkit — o cookie bespoke (`ACCOUNT_SESSION_KEY`)
|
|
18
|
+
* já é a fonte de verdade das rotas do próprio authkit e continua funcionando
|
|
19
|
+
* mesmo se a sincronização com `@adonisjs/auth` falhar. Loga um warning para
|
|
20
|
+
* não mascarar um guard mal configurado silenciosamente.
|
|
21
|
+
*/
|
|
22
|
+
export declare function syncAdonisAuthLogin(ctx: HttpContext, cfg: Pick<ResolvedServerConfig, 'adonisAuth'>, account: AuthAccount): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Sincroniza `ctx.auth` com um logout do console de conta. Ver
|
|
25
|
+
* {@link syncAdonisAuthLogin} para as mesmas regras de no-op/best-effort.
|
|
26
|
+
*/
|
|
27
|
+
export declare function syncAdonisAuthLogout(ctx: HttpContext, cfg: Pick<ResolvedServerConfig, 'adonisAuth'>): Promise<void>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sincroniza `ctx.auth` (`@adonisjs/auth`) com um login bem-sucedido no console
|
|
3
|
+
* de conta do authkit. Ver `adonisAuth` em `define_config.ts` pro porquê: sem
|
|
4
|
+
* isso, `ctx.auth.user` nunca é populado por nenhum fluxo do authkit-server —
|
|
5
|
+
* mesmo num host que roda `@adonisjs/auth` normalmente.
|
|
6
|
+
*
|
|
7
|
+
* NO-OP quando:
|
|
8
|
+
* - `adonisAuth.guard` não está configurado (opt-in; default é não tocar em
|
|
9
|
+
* `ctx.auth` — comportamento de sempre).
|
|
10
|
+
* - `ctx.auth` não existe nesta request (host sem `InitializeAuthMiddleware`
|
|
11
|
+
* de `@adonisjs/auth` registrado — o peer é opcional).
|
|
12
|
+
*
|
|
13
|
+
* BEST-EFFORT: uma falha aqui (ex.: guard mal configurado em `config/auth.ts`)
|
|
14
|
+
* NUNCA quebra o login do authkit — o cookie bespoke (`ACCOUNT_SESSION_KEY`)
|
|
15
|
+
* já é a fonte de verdade das rotas do próprio authkit e continua funcionando
|
|
16
|
+
* mesmo se a sincronização com `@adonisjs/auth` falhar. Loga um warning para
|
|
17
|
+
* não mascarar um guard mal configurado silenciosamente.
|
|
18
|
+
*/
|
|
19
|
+
export async function syncAdonisAuthLogin(ctx, cfg, account) {
|
|
20
|
+
const guard = cfg.adonisAuth?.guard;
|
|
21
|
+
const auth = ctx.auth;
|
|
22
|
+
if (!guard || !auth)
|
|
23
|
+
return;
|
|
24
|
+
try {
|
|
25
|
+
await auth.use(guard).login(account);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
ctx.logger?.warn({ err: error, guard }, 'authkit: falha ao sincronizar login com @adonisjs/auth (adonisAuth.guard) — ' +
|
|
29
|
+
'ctx.auth.user pode não refletir a conta logada; o cookie do authkit segue normal');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Sincroniza `ctx.auth` com um logout do console de conta. Ver
|
|
34
|
+
* {@link syncAdonisAuthLogin} para as mesmas regras de no-op/best-effort.
|
|
35
|
+
*/
|
|
36
|
+
export async function syncAdonisAuthLogout(ctx, cfg) {
|
|
37
|
+
const guard = cfg.adonisAuth?.guard;
|
|
38
|
+
const auth = ctx.auth;
|
|
39
|
+
if (!guard || !auth)
|
|
40
|
+
return;
|
|
41
|
+
try {
|
|
42
|
+
await auth.use(guard).logout();
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
ctx.logger?.warn({ err: error, guard }, 'authkit: falha ao sincronizar logout com @adonisjs/auth (adonisAuth.guard)');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ConfigProvider } from '@adonisjs/core/types';
|
|
2
|
+
import type { SessionUserProviderContract } from '@adonisjs/auth/types/session';
|
|
3
|
+
import type { AuthAccount } from '../accounts/account_store.js';
|
|
4
|
+
/**
|
|
5
|
+
* User provider de `@adonisjs/auth` (session guard) apoiado no `accountStore`
|
|
6
|
+
* do próprio authkit — a MESMA fonte de identidade que o login do console de
|
|
7
|
+
* conta autentica. Plugado em `config/auth.ts`:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { sessionGuard } from '@adonisjs/auth/session'
|
|
11
|
+
* import { authkitUserProvider } from '@adonis-agora/authkit-server'
|
|
12
|
+
*
|
|
13
|
+
* export default defineConfig({
|
|
14
|
+
* default: 'web',
|
|
15
|
+
* guards: {
|
|
16
|
+
* web: sessionGuard({
|
|
17
|
+
* useRememberMeTokens: false,
|
|
18
|
+
* provider: authkitUserProvider(),
|
|
19
|
+
* }),
|
|
20
|
+
* },
|
|
21
|
+
* })
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* Junto com `adonisAuth: { guard: 'web' }` em `config/authkit.ts`, o login do
|
|
25
|
+
* console de conta (`AccountSessionController#login`) passa a chamar
|
|
26
|
+
* `ctx.auth.use('web').login(account)` no sucesso — populando `ctx.auth.user`
|
|
27
|
+
* com um {@link AuthAccount} de verdade para o resto do app (Bouncer,
|
|
28
|
+
* `middleware.auth()`, etc.), sem duplicar a sessão bespoke do authkit
|
|
29
|
+
* (`ACCOUNT_SESSION_KEY`), que continua sendo a fonte de verdade das próprias
|
|
30
|
+
* rotas do authkit.
|
|
31
|
+
*
|
|
32
|
+
* Devolve um `ConfigProvider` (mesmo padrão do `sessionUserProvider()` nativo
|
|
33
|
+
* de `@adonisjs/auth`) — a resolução (incluindo o import dinâmico de
|
|
34
|
+
* `@adonisjs/auth` para o símbolo `PROVIDER_REAL_USER` exigido pelo contrato)
|
|
35
|
+
* só acontece quando o HOST resolve a config do guard no boot, nunca no
|
|
36
|
+
* carregamento deste módulo.
|
|
37
|
+
*/
|
|
38
|
+
export declare function authkitUserProvider(): ConfigProvider<SessionUserProviderContract<AuthAccount>>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { configProvider } from '@adonisjs/core';
|
|
2
|
+
import { RuntimeException } from '@adonisjs/core/exceptions';
|
|
3
|
+
/**
|
|
4
|
+
* User provider de `@adonisjs/auth` (session guard) apoiado no `accountStore`
|
|
5
|
+
* do próprio authkit — a MESMA fonte de identidade que o login do console de
|
|
6
|
+
* conta autentica. Plugado em `config/auth.ts`:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { sessionGuard } from '@adonisjs/auth/session'
|
|
10
|
+
* import { authkitUserProvider } from '@adonis-agora/authkit-server'
|
|
11
|
+
*
|
|
12
|
+
* export default defineConfig({
|
|
13
|
+
* default: 'web',
|
|
14
|
+
* guards: {
|
|
15
|
+
* web: sessionGuard({
|
|
16
|
+
* useRememberMeTokens: false,
|
|
17
|
+
* provider: authkitUserProvider(),
|
|
18
|
+
* }),
|
|
19
|
+
* },
|
|
20
|
+
* })
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* Junto com `adonisAuth: { guard: 'web' }` em `config/authkit.ts`, o login do
|
|
24
|
+
* console de conta (`AccountSessionController#login`) passa a chamar
|
|
25
|
+
* `ctx.auth.use('web').login(account)` no sucesso — populando `ctx.auth.user`
|
|
26
|
+
* com um {@link AuthAccount} de verdade para o resto do app (Bouncer,
|
|
27
|
+
* `middleware.auth()`, etc.), sem duplicar a sessão bespoke do authkit
|
|
28
|
+
* (`ACCOUNT_SESSION_KEY`), que continua sendo a fonte de verdade das próprias
|
|
29
|
+
* rotas do authkit.
|
|
30
|
+
*
|
|
31
|
+
* Devolve um `ConfigProvider` (mesmo padrão do `sessionUserProvider()` nativo
|
|
32
|
+
* de `@adonisjs/auth`) — a resolução (incluindo o import dinâmico de
|
|
33
|
+
* `@adonisjs/auth` para o símbolo `PROVIDER_REAL_USER` exigido pelo contrato)
|
|
34
|
+
* só acontece quando o HOST resolve a config do guard no boot, nunca no
|
|
35
|
+
* carregamento deste módulo.
|
|
36
|
+
*/
|
|
37
|
+
export function authkitUserProvider() {
|
|
38
|
+
return configProvider.create(async (app) => {
|
|
39
|
+
let PROVIDER_REAL_USER;
|
|
40
|
+
try {
|
|
41
|
+
const auth = (await import('@adonisjs/auth'));
|
|
42
|
+
PROVIDER_REAL_USER = auth.symbols.PROVIDER_REAL_USER;
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
throw new RuntimeException('authkitUserProvider() precisa de "@adonisjs/auth" instalado (é um peer opcional do ' +
|
|
46
|
+
'@adonis-agora/authkit-server, só necessário se você plugar este provider em config/auth.ts). ' +
|
|
47
|
+
'Rode `npm i @adonisjs/auth` (ou pnpm/yarn) e configure um sessionGuard em config/auth.ts.', { cause: error });
|
|
48
|
+
}
|
|
49
|
+
// `PROVIDER_REAL_USER` chega em runtime como um `symbol` (não um "unique
|
|
50
|
+
// symbol" literal) — o cast final é seguro pq o valor É literalmente o
|
|
51
|
+
// símbolo exportado por `@adonisjs/auth`, só que o TS não consegue provar
|
|
52
|
+
// isso estruturalmente a partir de um import dinâmico.
|
|
53
|
+
const provider = {
|
|
54
|
+
[PROVIDER_REAL_USER]: undefined,
|
|
55
|
+
async createUserForGuard(user) {
|
|
56
|
+
return {
|
|
57
|
+
getId: () => user.id,
|
|
58
|
+
getOriginal: () => user,
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
async findById(identifier) {
|
|
62
|
+
const store = await app.container.make('authkit.accountStore');
|
|
63
|
+
const account = await store.findById(String(identifier));
|
|
64
|
+
if (!account)
|
|
65
|
+
return null;
|
|
66
|
+
return {
|
|
67
|
+
getId: () => account.id,
|
|
68
|
+
getOriginal: () => account,
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
return provider;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ApplicationService } from '@adonisjs/core/types';
|
|
2
|
+
/**
|
|
3
|
+
* Mensagem única (compartilhada entre o check EAGER do `boot()` e o resolver
|
|
4
|
+
* LAZY do `register()`) para o erro de `appKey` ausente — um só lugar para
|
|
5
|
+
* editar o texto, e garante que os dois caminhos falem exatamente a mesma coisa.
|
|
6
|
+
*/
|
|
7
|
+
export declare const MISSING_APP_KEY_MESSAGE: string;
|
|
8
|
+
/**
|
|
9
|
+
* Lê e valida `app.appKey` (usado pelo oidc-provider para assinar cookies via
|
|
10
|
+
* keygrip). `app.appKey` pode ser uma string crua ou um `Secret` do AdonisJS
|
|
11
|
+
* (`config/app.ts` expõe `export const appKey = new Secret(env.get('APP_KEY'))`).
|
|
12
|
+
*
|
|
13
|
+
* Lança {@link RuntimeException} com uma mensagem que NOMEIA o arquivo e o campo
|
|
14
|
+
* exatos a corrigir — nunca deixa o app seguir com uma appKey ausente/inválida
|
|
15
|
+
* (o oidc-provider quebraria mais adiante com um erro genérico do keygrip,
|
|
16
|
+
* sem apontar a causa raiz).
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveAppKey(app: ApplicationService): string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { RuntimeException } from '@adonisjs/core/exceptions';
|
|
2
|
+
/**
|
|
3
|
+
* Mensagem única (compartilhada entre o check EAGER do `boot()` e o resolver
|
|
4
|
+
* LAZY do `register()`) para o erro de `appKey` ausente — um só lugar para
|
|
5
|
+
* editar o texto, e garante que os dois caminhos falem exatamente a mesma coisa.
|
|
6
|
+
*/
|
|
7
|
+
export const MISSING_APP_KEY_MESSAGE = 'APP_KEY ausente: defina `export const appKey = new Secret(env.get(\'APP_KEY\'))` em config/app.ts. ' +
|
|
8
|
+
'O @adonis-agora/authkit-server precisa dele para assinar os cookies do oidc-provider (NÃO usa config/encryption.ts).';
|
|
9
|
+
/**
|
|
10
|
+
* Lê e valida `app.appKey` (usado pelo oidc-provider para assinar cookies via
|
|
11
|
+
* keygrip). `app.appKey` pode ser uma string crua ou um `Secret` do AdonisJS
|
|
12
|
+
* (`config/app.ts` expõe `export const appKey = new Secret(env.get('APP_KEY'))`).
|
|
13
|
+
*
|
|
14
|
+
* Lança {@link RuntimeException} com uma mensagem que NOMEIA o arquivo e o campo
|
|
15
|
+
* exatos a corrigir — nunca deixa o app seguir com uma appKey ausente/inválida
|
|
16
|
+
* (o oidc-provider quebraria mais adiante com um erro genérico do keygrip,
|
|
17
|
+
* sem apontar a causa raiz).
|
|
18
|
+
*/
|
|
19
|
+
export function resolveAppKey(app) {
|
|
20
|
+
const rawAppKey = app.config.get('app.appKey');
|
|
21
|
+
const appKey = rawAppKey && typeof rawAppKey.release === 'function'
|
|
22
|
+
? rawAppKey.release()
|
|
23
|
+
: rawAppKey;
|
|
24
|
+
if (!appKey || typeof appKey !== 'string') {
|
|
25
|
+
throw new RuntimeException(MISSING_APP_KEY_MESSAGE);
|
|
26
|
+
}
|
|
27
|
+
return appKey;
|
|
28
|
+
}
|
|
@@ -15,6 +15,7 @@ import { resolveRuntimeSettings } from "../runtime_settings.js";
|
|
|
15
15
|
import { resolveEffectiveEmailChange, resolveEffectivePasswordHistory, } from "../runtime_toggles.js";
|
|
16
16
|
import { dispatchSecurityNotice } from "../security_notice_service.js";
|
|
17
17
|
import { requireSudo } from "../sudo_mode.js";
|
|
18
|
+
import { syncAdonisAuthLogout } from "../adonis_auth_sync.js";
|
|
18
19
|
/** Resolve os password history settings em runtime (fail-safe). */
|
|
19
20
|
async function resolvePasswordHistorySettings(ctx) {
|
|
20
21
|
try {
|
|
@@ -193,6 +194,7 @@ export default class AccountSecurityController {
|
|
|
193
194
|
}
|
|
194
195
|
// Encerra a sessão e leva ao login com a mensagem de sucesso.
|
|
195
196
|
ctx.session.forget(ACCOUNT_SESSION_KEY);
|
|
197
|
+
await syncAdonisAuthLogout(ctx, cfg);
|
|
196
198
|
ctx.session.flash("accountDeleted", translate(cfg.messages, "account.delete.deleted"));
|
|
197
199
|
return ctx.response.redirect("/account/login");
|
|
198
200
|
}
|
|
@@ -6,6 +6,7 @@ import { notifyLoginSuccess } from '../login_notify.js';
|
|
|
6
6
|
import { markSudo } from '../sudo_mode.js';
|
|
7
7
|
import { accountHome } from '../account_home.js';
|
|
8
8
|
import { resolveRuntimeSettings } from '../runtime_settings.js';
|
|
9
|
+
import { syncAdonisAuthLogin, syncAdonisAuthLogout } from '../adonis_auth_sync.js';
|
|
9
10
|
/**
|
|
10
11
|
* Valida um valor de `return_to` recebido da query-string ou de um campo hidden.
|
|
11
12
|
*
|
|
@@ -95,11 +96,16 @@ export default class AccountSessionController {
|
|
|
95
96
|
ctx.session.put(ACCOUNT_SESSION_KEY, acc.id);
|
|
96
97
|
// Login com senha = confirmação de identidade → marca sudo (graça a partir do login).
|
|
97
98
|
markSudo(ctx);
|
|
99
|
+
// Opt-in: sincroniza ctx.auth.use(cfg.adonisAuth.guard) com a mesma conta —
|
|
100
|
+
// no-op sem `adonisAuth` configurado ou sem @adonisjs/auth inicializado.
|
|
101
|
+
await syncAdonisAuthLogin(ctx, cfg, acc);
|
|
98
102
|
await notifyLoginSuccess(ctx, cfg, { accountId: acc.id, email, ip });
|
|
99
103
|
// Redireciona pro destino original (validado), ou cai no accountHome configurado.
|
|
100
104
|
return ctx.response.redirect(returnTo ?? accountHome(cfg));
|
|
101
105
|
}
|
|
102
106
|
async logout(ctx) {
|
|
107
|
+
const service = await ctx.containerResolver.make('authkit.server');
|
|
108
|
+
const cfg = service.config;
|
|
103
109
|
// M6: não basta `forget(ACCOUNT_SESSION_KEY)` — sobravam na sessão
|
|
104
110
|
// `authkit_sudo_at` (sudo), `authkit_last_seen` e qualquer outro estado
|
|
105
111
|
// sensível, com o session id INALTERADO. Regenerar a sessão troca o id E
|
|
@@ -108,6 +114,8 @@ export default class AccountSessionController {
|
|
|
108
114
|
// (belt-and-braces) caso o store de sessão não suporte regenerate.
|
|
109
115
|
ctx.session.forget(ACCOUNT_SESSION_KEY);
|
|
110
116
|
await ctx.session.regenerate();
|
|
117
|
+
// Opt-in: espelha o logout no guard de @adonisjs/auth (ver adonisAuth em define_config.ts).
|
|
118
|
+
await syncAdonisAuthLogout(ctx, cfg);
|
|
111
119
|
return ctx.response.redirect('/account/login');
|
|
112
120
|
}
|
|
113
121
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import '../augmentations.js';
|
|
2
2
|
import type { HttpContext } from '@adonisjs/core/http';
|
|
3
3
|
export default class AuthRegistrationController {
|
|
4
|
+
#private;
|
|
4
5
|
/** GET /auth/interaction/:uid/signup — tela de cadastro (dentro do fluxo OIDC). */
|
|
5
6
|
showSignup(ctx: HttpContext): Promise<any>;
|
|
6
7
|
/** POST /auth/interaction/:uid/signup — cria o usuário e finaliza o login. */
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import '../augmentations.js';
|
|
2
2
|
import { brandFor } from '../branding.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { randomBytes } from 'node:crypto';
|
|
4
|
+
import { signupValidator, passwordlessSignupValidator, forgotPasswordValidator, resetPasswordValidator, } from '../validators.js';
|
|
5
|
+
import { sendPasswordResetEmail, sendEmailVerificationEmail, sendMagicLinkEmail, } from '../default_mailer.js';
|
|
5
6
|
import { translate } from '../i18n.js';
|
|
6
7
|
import { PasswordPolicyError } from '../../password/password_manager.js';
|
|
7
8
|
import { guardBotProtection, resolveEffectiveBotProtection } from '../bot_protection.js';
|
|
@@ -54,6 +55,7 @@ export default class AuthRegistrationController {
|
|
|
54
55
|
uid: details.uid,
|
|
55
56
|
csrfToken: ctx.request.csrfToken,
|
|
56
57
|
brand,
|
|
58
|
+
passwordlessSignup: cfg.passwordless?.signup ?? false,
|
|
57
59
|
botProtection: effectiveBot?.on.includes('signup') ? effectiveBot.widget : undefined,
|
|
58
60
|
});
|
|
59
61
|
}
|
|
@@ -102,6 +104,12 @@ export default class AuthRegistrationController {
|
|
|
102
104
|
botProtection: effectiveBotSignup?.widget,
|
|
103
105
|
});
|
|
104
106
|
}
|
|
107
|
+
// Cadastro passwordless (config): só e-mail + nome. Cria conta com senha random
|
|
108
|
+
// inutilizável e envia um magic link — mesmo fluxo do login por magic link.
|
|
109
|
+
if (cfg.passwordless?.signup &&
|
|
110
|
+
typeof cfg.accountStore.issueMagicLinkToken === 'function') {
|
|
111
|
+
return this.#passwordlessSignup(ctx, { cfg, brand, details });
|
|
112
|
+
}
|
|
105
113
|
const data = await ctx.request.validateUsing(signupValidator);
|
|
106
114
|
const accountStore = cfg.accountStore;
|
|
107
115
|
const existing = await accountStore.findByEmail(data.email);
|
|
@@ -183,6 +191,55 @@ export default class AuthRegistrationController {
|
|
|
183
191
|
ctx.logger.error({ err: error, email: data.email }, 'authkit: falha ao enviar verificação de e-mail');
|
|
184
192
|
}
|
|
185
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* Cadastro passwordless: valida e-mail + nome, cria a conta (senha random
|
|
196
|
+
* inutilizável) se ainda não existe, emite um magic link e o envia. Sempre
|
|
197
|
+
* responde "link enviado" (anti-enumeração), exista a conta ou não. Consumir o
|
|
198
|
+
* link finaliza o login pelo fluxo de magic link já existente (GET /magic).
|
|
199
|
+
*/
|
|
200
|
+
async #passwordlessSignup(ctx, deps) {
|
|
201
|
+
const { cfg, brand, details } = deps;
|
|
202
|
+
const render = cfg.render;
|
|
203
|
+
const accountStore = cfg.accountStore;
|
|
204
|
+
const uid = ctx.request.param('uid');
|
|
205
|
+
const data = await ctx.request.validateUsing(passwordlessSignupValidator);
|
|
206
|
+
// Cria a conta se ainda não existe. Senha random inutilizável: o login é 100%
|
|
207
|
+
// passwordless (mesmo precedente das contas criadas por identidade social).
|
|
208
|
+
const existing = await accountStore.findByEmail(data.email);
|
|
209
|
+
if (!existing) {
|
|
210
|
+
const created = await accountStore.create({
|
|
211
|
+
email: data.email,
|
|
212
|
+
fullName: data.fullName,
|
|
213
|
+
password: randomBytes(24).toString('hex'),
|
|
214
|
+
});
|
|
215
|
+
await cfg.audit?.record({
|
|
216
|
+
type: 'signup',
|
|
217
|
+
accountId: created.id,
|
|
218
|
+
email: data.email,
|
|
219
|
+
ip: ctx.request.ip?.() ?? null,
|
|
220
|
+
clientId: details.params.client_id ?? null,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
// Emite + envia o magic link (mesma construção do login por magic link).
|
|
224
|
+
const issued = await accountStore.issueMagicLinkToken(data.email);
|
|
225
|
+
if (issued) {
|
|
226
|
+
const origin = `${ctx.request.protocol()}://${ctx.request.host()}`;
|
|
227
|
+
const magicUrl = `${origin}/auth/interaction/${uid}/magic?token=${encodeURIComponent(issued.token)}`;
|
|
228
|
+
if (cfg.mail?.onMagicLink) {
|
|
229
|
+
await cfg.mail.onMagicLink({ email: data.email, magicUrl, token: issued.token });
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
await sendMagicLinkEmail(ctx, { email: data.email, magicUrl });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// Resposta uniforme: "enviamos um link" (não vaza existência da conta).
|
|
236
|
+
return render(ctx, 'signup', {
|
|
237
|
+
uid,
|
|
238
|
+
csrfToken: ctx.request.csrfToken,
|
|
239
|
+
brand,
|
|
240
|
+
magicLinkSent: true,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
186
243
|
/** GET /auth/forgot-password — tela standalone. */
|
|
187
244
|
async showForgot(ctx) {
|
|
188
245
|
const service = await ctx.containerResolver.make('authkit.server');
|
package/build/src/host/i18n.d.ts
CHANGED
|
@@ -48,6 +48,7 @@ export declare const DEFAULT_MESSAGES: {
|
|
|
48
48
|
"account.login.idle_timeout": string;
|
|
49
49
|
"login.magic_link_button": string;
|
|
50
50
|
"login.magic_link_sent": string;
|
|
51
|
+
"signup.magic_link_sent": string;
|
|
51
52
|
"login.passkey_button": string;
|
|
52
53
|
"signup.page_title": string;
|
|
53
54
|
"signup.title": string;
|
|
@@ -734,6 +735,7 @@ export declare const PT_BR_MESSAGES: {
|
|
|
734
735
|
"account.login.idle_timeout": string;
|
|
735
736
|
"login.magic_link_button": string;
|
|
736
737
|
"login.magic_link_sent": string;
|
|
738
|
+
"signup.magic_link_sent": string;
|
|
737
739
|
"login.passkey_button": string;
|
|
738
740
|
"signup.page_title": string;
|
|
739
741
|
"signup.title": string;
|
package/build/src/host/i18n.js
CHANGED
|
@@ -39,6 +39,7 @@ export const DEFAULT_MESSAGES = {
|
|
|
39
39
|
// Passwordless (login).
|
|
40
40
|
"login.magic_link_button": "Email me a login link",
|
|
41
41
|
"login.magic_link_sent": "If the account exists, we sent you a login link.",
|
|
42
|
+
"signup.magic_link_sent": "Check your email — we sent you a link to finish creating your account.",
|
|
42
43
|
"login.passkey_button": "Sign in with a passkey",
|
|
43
44
|
// Tela de cadastro (signup).
|
|
44
45
|
"signup.page_title": "Create account",
|
|
@@ -803,6 +804,7 @@ export const PT_BR_MESSAGES = {
|
|
|
803
804
|
// Passwordless (login).
|
|
804
805
|
"login.magic_link_button": "Me envie um link de login",
|
|
805
806
|
"login.magic_link_sent": "Se a conta existir, enviamos um link de login.",
|
|
807
|
+
"signup.magic_link_sent": "Enviamos um link para o seu e-mail. Abra-o para concluir o cadastro.",
|
|
806
808
|
"login.passkey_button": "Entrar com passkey",
|
|
807
809
|
// Tela de cadastro (signup).
|
|
808
810
|
"signup.page_title": "Criar conta",
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
/** Cadastro passwordless: só e-mail + nome (sem senha). O login vem por magic link. */
|
|
2
|
+
export declare const passwordlessSignupValidator: import("@vinejs/vine").VineValidator<import("@vinejs/vine").VineObject<{
|
|
3
|
+
email: import("@vinejs/vine").VineString;
|
|
4
|
+
fullName: import("@vinejs/vine").VineString;
|
|
5
|
+
}, {
|
|
6
|
+
email: string;
|
|
7
|
+
fullName: string;
|
|
8
|
+
}, {
|
|
9
|
+
email: string;
|
|
10
|
+
fullName: string;
|
|
11
|
+
}, {
|
|
12
|
+
email: string;
|
|
13
|
+
fullName: string;
|
|
14
|
+
}>, Record<string, any> | undefined>;
|
|
1
15
|
export declare const signupValidator: import("@vinejs/vine").VineValidator<import("@vinejs/vine").VineObject<{
|
|
2
16
|
email: import("@vinejs/vine").VineString;
|
|
3
17
|
fullName: import("@vinejs/vine").VineString;
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import vine from '@vinejs/vine';
|
|
2
|
+
/** Cadastro passwordless: só e-mail + nome (sem senha). O login vem por magic link. */
|
|
3
|
+
export const passwordlessSignupValidator = vine.compile(vine.object({
|
|
4
|
+
email: vine.string().trim().email().normalizeEmail(),
|
|
5
|
+
fullName: vine.string().trim().minLength(2).maxLength(255),
|
|
6
|
+
}));
|
|
2
7
|
export const signupValidator = vine.compile(vine.object({
|
|
3
8
|
email: vine.string().trim().email().normalizeEmail(),
|
|
4
9
|
fullName: vine.string().trim().minLength(2).maxLength(255),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonis-agora/authkit-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.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",
|
|
@@ -56,15 +56,16 @@
|
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"@adonis-agora/durable": "^0.2.0",
|
|
58
58
|
"@adonis-agora/telescope": "^0.1.0",
|
|
59
|
-
"@adonisjs/ally": "6.3.0",
|
|
60
|
-
"@adonisjs/
|
|
61
|
-
"@adonisjs/
|
|
62
|
-
"@adonisjs/
|
|
63
|
-
"@adonisjs/
|
|
64
|
-
"@adonisjs/
|
|
65
|
-
"@adonisjs/
|
|
66
|
-
"@adonisjs/
|
|
67
|
-
"
|
|
59
|
+
"@adonisjs/ally": "^6.3.0",
|
|
60
|
+
"@adonisjs/auth": "^10.1.0",
|
|
61
|
+
"@adonisjs/core": "^7.3.3",
|
|
62
|
+
"@adonisjs/drive": "^4.0.0",
|
|
63
|
+
"@adonisjs/lock": "^2.1.0",
|
|
64
|
+
"@adonisjs/lucid": "^22.4.2",
|
|
65
|
+
"@adonisjs/redis": "^9.2.0",
|
|
66
|
+
"@adonisjs/session": "^8.1.0",
|
|
67
|
+
"@adonisjs/shield": "^9.0.0",
|
|
68
|
+
"edge.js": "^6.5.1"
|
|
68
69
|
},
|
|
69
70
|
"peerDependenciesMeta": {
|
|
70
71
|
"@adonis-agora/durable": {
|
|
@@ -79,6 +80,9 @@
|
|
|
79
80
|
"@adonisjs/ally": {
|
|
80
81
|
"optional": true
|
|
81
82
|
},
|
|
83
|
+
"@adonisjs/auth": {
|
|
84
|
+
"optional": true
|
|
85
|
+
},
|
|
82
86
|
"@adonisjs/drive": {
|
|
83
87
|
"optional": true
|
|
84
88
|
},
|
|
@@ -104,7 +108,8 @@
|
|
|
104
108
|
"@adonis-agora/durable": "^0.2.0",
|
|
105
109
|
"@adonis-agora/telescope": "^0.1.0",
|
|
106
110
|
"@adonisjs/ally": "6.3.0",
|
|
107
|
-
"@adonisjs/
|
|
111
|
+
"@adonisjs/auth": "10.1.0",
|
|
112
|
+
"@adonisjs/core": "7.3.5",
|
|
108
113
|
"@adonisjs/drive": "4.0.0",
|
|
109
114
|
"@adonisjs/lucid": "22.4.2",
|
|
110
115
|
"@adonisjs/session": "8.1.0",
|
|
@@ -135,7 +140,7 @@
|
|
|
135
140
|
"react-error-boundary": "6.1.2",
|
|
136
141
|
"nuqs": "2.8.9",
|
|
137
142
|
"recharts": "3.8.1",
|
|
138
|
-
"@adonis-agora/authkit-react": "0.
|
|
143
|
+
"@adonis-agora/authkit-react": "0.14.0"
|
|
139
144
|
},
|
|
140
145
|
"scripts": {
|
|
141
146
|
"build": "node scripts/build_host_css.mjs && node scripts/build_ui.mjs && tsc && node -e \"require('node:fs').cpSync('stubs','build/stubs',{recursive:true,filter:(s)=>!s.endsWith('.ts')})\" && node -e \"const fs=require('node:fs');if(fs.existsSync('assets'))fs.cpSync('assets','build/assets',{recursive:true})\" && node -e \"require('node:fs').cpSync('src/host/views','build/host/views',{recursive:true})\" && node -e \"const fs=require('node:fs');fs.mkdirSync('build/host/ui',{recursive:true});fs.readdirSync('src/host/ui').filter(f=>f.endsWith('.html')).forEach(f=>fs.copyFileSync('src/host/ui/'+f,'build/host/ui/'+f))\" && node -e \"require('node:fs').copyFileSync('commands/commands.json','build/commands/commands.json')\" && node -e \"const fs=require('node:fs');fs.mkdirSync('build/password',{recursive:true});fs.copyFileSync('src/password/common_passwords.txt','build/password/common_passwords.txt')\"",
|