@dudousxd/adonis-authkit-server 0.1.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.
Files changed (174) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +137 -0
  3. package/build/assets/grafana/authkit-dashboard.json +118 -0
  4. package/build/commands/commands.json +30 -0
  5. package/build/commands/configure.d.ts +2 -0
  6. package/build/commands/configure.js +42 -0
  7. package/build/commands/eject.d.ts +11 -0
  8. package/build/commands/eject.js +96 -0
  9. package/build/commands/main.d.ts +12 -0
  10. package/build/commands/main.js +38 -0
  11. package/build/commands/ui_preset.d.ts +4 -0
  12. package/build/commands/ui_preset.js +32 -0
  13. package/build/database/migrations/make_authkit_oidc_table.d.ts +6 -0
  14. package/build/database/migrations/make_authkit_oidc_table.js +19 -0
  15. package/build/host/views/account/login.edge +29 -0
  16. package/build/host/views/account/mfa.edge +151 -0
  17. package/build/host/views/account/tokens.edge +70 -0
  18. package/build/host/views/admin/audit.edge +72 -0
  19. package/build/host/views/admin/clients.edge +51 -0
  20. package/build/host/views/admin/dashboard.edge +58 -0
  21. package/build/host/views/admin/users.edge +76 -0
  22. package/build/host/views/consent.edge +19 -0
  23. package/build/host/views/forgot.edge +30 -0
  24. package/build/host/views/login.edge +91 -0
  25. package/build/host/views/mfa-challenge.edge +88 -0
  26. package/build/host/views/reset.edge +29 -0
  27. package/build/host/views/signup.edge +44 -0
  28. package/build/host/views/verify-email.edge +16 -0
  29. package/build/index.d.ts +42 -0
  30. package/build/index.js +28 -0
  31. package/build/providers/authkit_server_provider.d.ts +19 -0
  32. package/build/providers/authkit_server_provider.js +81 -0
  33. package/build/src/accounts/account_store.d.ts +136 -0
  34. package/build/src/accounts/account_store.js +1 -0
  35. package/build/src/accounts/lucid_account_store.d.ts +75 -0
  36. package/build/src/accounts/lucid_account_store.js +396 -0
  37. package/build/src/adapters/adapter_contract.d.ts +18 -0
  38. package/build/src/adapters/adapter_contract.js +1 -0
  39. package/build/src/adapters/database_adapter.d.ts +15 -0
  40. package/build/src/adapters/database_adapter.js +63 -0
  41. package/build/src/adapters/factory.d.ts +30 -0
  42. package/build/src/adapters/factory.js +43 -0
  43. package/build/src/adapters/redis_adapter.d.ts +16 -0
  44. package/build/src/adapters/redis_adapter.js +95 -0
  45. package/build/src/audit/audit_sink.d.ts +54 -0
  46. package/build/src/audit/audit_sink.js +1 -0
  47. package/build/src/audit/lucid_audit_sink.d.ts +10 -0
  48. package/build/src/audit/lucid_audit_sink.js +60 -0
  49. package/build/src/controllers/oidc_callback_controller.d.ts +22 -0
  50. package/build/src/controllers/oidc_callback_controller.js +33 -0
  51. package/build/src/define_config.d.ts +261 -0
  52. package/build/src/define_config.js +115 -0
  53. package/build/src/host/account_lockout.d.ts +86 -0
  54. package/build/src/host/account_lockout.js +185 -0
  55. package/build/src/host/augmentations.d.ts +1 -0
  56. package/build/src/host/augmentations.js +1 -0
  57. package/build/src/host/branding.d.ts +17 -0
  58. package/build/src/host/branding.js +8 -0
  59. package/build/src/host/controllers/account_mfa_controller.d.ts +30 -0
  60. package/build/src/host/controllers/account_mfa_controller.js +157 -0
  61. package/build/src/host/controllers/account_session_controller.d.ts +7 -0
  62. package/build/src/host/controllers/account_session_controller.js +50 -0
  63. package/build/src/host/controllers/account_tokens_controller.d.ts +7 -0
  64. package/build/src/host/controllers/account_tokens_controller.js +55 -0
  65. package/build/src/host/controllers/admin/admin_audit_controller.d.ts +10 -0
  66. package/build/src/host/controllers/admin/admin_audit_controller.js +56 -0
  67. package/build/src/host/controllers/admin/admin_clients_controller.d.ts +10 -0
  68. package/build/src/host/controllers/admin/admin_clients_controller.js +24 -0
  69. package/build/src/host/controllers/admin/admin_dashboard_controller.d.ts +10 -0
  70. package/build/src/host/controllers/admin/admin_dashboard_controller.js +27 -0
  71. package/build/src/host/controllers/admin/admin_users_controller.d.ts +11 -0
  72. package/build/src/host/controllers/admin/admin_users_controller.js +53 -0
  73. package/build/src/host/controllers/interaction_controller.d.ts +44 -0
  74. package/build/src/host/controllers/interaction_controller.js +304 -0
  75. package/build/src/host/controllers/pat_introspection_controller.d.ts +22 -0
  76. package/build/src/host/controllers/pat_introspection_controller.js +46 -0
  77. package/build/src/host/controllers/registration_controller.d.ts +18 -0
  78. package/build/src/host/controllers/registration_controller.js +169 -0
  79. package/build/src/host/controllers/social_controller.d.ts +8 -0
  80. package/build/src/host/controllers/social_controller.js +82 -0
  81. package/build/src/host/default_mailer.d.ts +39 -0
  82. package/build/src/host/default_mailer.js +141 -0
  83. package/build/src/host/email_templates.d.ts +35 -0
  84. package/build/src/host/email_templates.js +66 -0
  85. package/build/src/host/i18n.d.ts +178 -0
  86. package/build/src/host/i18n.js +208 -0
  87. package/build/src/host/middleware/account_auth.d.ts +7 -0
  88. package/build/src/host/middleware/account_auth.js +11 -0
  89. package/build/src/host/rate_limit.d.ts +32 -0
  90. package/build/src/host/rate_limit.js +87 -0
  91. package/build/src/host/register_auth_host.d.ts +41 -0
  92. package/build/src/host/register_auth_host.js +133 -0
  93. package/build/src/host/renderers/edge_renderer.d.ts +3 -0
  94. package/build/src/host/renderers/edge_renderer.js +29 -0
  95. package/build/src/host/renderers/inertia_renderer.d.ts +5 -0
  96. package/build/src/host/renderers/inertia_renderer.js +26 -0
  97. package/build/src/host/validators.d.ts +39 -0
  98. package/build/src/host/validators.js +13 -0
  99. package/build/src/keys/jwks_manager.d.ts +6 -0
  100. package/build/src/keys/jwks_manager.js +11 -0
  101. package/build/src/mixins/with_audit_log.d.ts +19 -0
  102. package/build/src/mixins/with_audit_log.js +41 -0
  103. package/build/src/mixins/with_auth_user.d.ts +18 -0
  104. package/build/src/mixins/with_auth_user.js +39 -0
  105. package/build/src/mixins/with_credentials.d.ts +20 -0
  106. package/build/src/mixins/with_credentials.js +29 -0
  107. package/build/src/mixins/with_mfa.d.ts +31 -0
  108. package/build/src/mixins/with_mfa.js +39 -0
  109. package/build/src/mixins/with_personal_access_token.d.ts +19 -0
  110. package/build/src/mixins/with_personal_access_token.js +44 -0
  111. package/build/src/mixins/with_provider_identity.d.ts +20 -0
  112. package/build/src/mixins/with_provider_identity.js +32 -0
  113. package/build/src/mixins/with_webauthn_credential.d.ts +37 -0
  114. package/build/src/mixins/with_webauthn_credential.js +49 -0
  115. package/build/src/observability/metrics_controller.d.ts +5 -0
  116. package/build/src/observability/metrics_controller.js +24 -0
  117. package/build/src/observability/metrics_service.d.ts +2 -0
  118. package/build/src/observability/metrics_service.js +7 -0
  119. package/build/src/observability/otel_recorder.d.ts +10 -0
  120. package/build/src/observability/otel_recorder.js +59 -0
  121. package/build/src/observability/wire_provider_events.d.ts +12 -0
  122. package/build/src/observability/wire_provider_events.js +19 -0
  123. package/build/src/pat/lucid_pat_store.d.ts +6 -0
  124. package/build/src/pat/lucid_pat_store.js +62 -0
  125. package/build/src/pat/pat_store.d.ts +31 -0
  126. package/build/src/pat/pat_store.js +1 -0
  127. package/build/src/pat/pat_tokens.d.ts +4 -0
  128. package/build/src/pat/pat_tokens.js +9 -0
  129. package/build/src/provider/build_provider.d.ts +8 -0
  130. package/build/src/provider/build_provider.js +101 -0
  131. package/build/src/provider/interaction_actions.d.ts +21 -0
  132. package/build/src/provider/interaction_actions.js +32 -0
  133. package/build/src/provider/oidc_service.d.ts +17 -0
  134. package/build/src/provider/oidc_service.js +84 -0
  135. package/build/src/provider/token_exchange.d.ts +15 -0
  136. package/build/src/provider/token_exchange.js +72 -0
  137. package/build/src/register_routes.d.ts +16 -0
  138. package/build/src/register_routes.js +21 -0
  139. package/build/stubs/config/authkit.stub +29 -0
  140. package/build/stubs/main.d.ts +1 -0
  141. package/build/stubs/main.js +2 -0
  142. package/build/stubs/models/auth_user.stub +13 -0
  143. package/build/stubs/ui/edge/views/consent.edge +13 -0
  144. package/build/stubs/ui/edge/views/login.edge +19 -0
  145. package/build/stubs/ui/react/components/auth_shell.tsx +67 -0
  146. package/build/stubs/ui/react/pages/account/login.tsx +56 -0
  147. package/build/stubs/ui/react/pages/account/mfa.tsx +132 -0
  148. package/build/stubs/ui/react/pages/account/tokens.tsx +88 -0
  149. package/build/stubs/ui/react/pages/consent.tsx +39 -0
  150. package/build/stubs/ui/react/pages/forgot.tsx +44 -0
  151. package/build/stubs/ui/react/pages/login.tsx +171 -0
  152. package/build/stubs/ui/react/pages/mfa-challenge.tsx +72 -0
  153. package/build/stubs/ui/react/pages/reset.tsx +58 -0
  154. package/build/stubs/ui/react/pages/signup.tsx +78 -0
  155. package/build/stubs/ui/react/pages/verify-email.tsx +24 -0
  156. package/build/types.d.ts +7 -0
  157. package/build/types.js +1 -0
  158. package/package.json +108 -0
  159. package/stubs/config/authkit.stub +29 -0
  160. package/stubs/main.ts +2 -0
  161. package/stubs/models/auth_user.stub +13 -0
  162. package/stubs/ui/edge/views/consent.edge +13 -0
  163. package/stubs/ui/edge/views/login.edge +19 -0
  164. package/stubs/ui/react/components/auth_shell.tsx +67 -0
  165. package/stubs/ui/react/pages/account/login.tsx +56 -0
  166. package/stubs/ui/react/pages/account/mfa.tsx +132 -0
  167. package/stubs/ui/react/pages/account/tokens.tsx +88 -0
  168. package/stubs/ui/react/pages/consent.tsx +39 -0
  169. package/stubs/ui/react/pages/forgot.tsx +44 -0
  170. package/stubs/ui/react/pages/login.tsx +171 -0
  171. package/stubs/ui/react/pages/mfa-challenge.tsx +72 -0
  172. package/stubs/ui/react/pages/reset.tsx +58 -0
  173. package/stubs/ui/react/pages/signup.tsx +78 -0
  174. package/stubs/ui/react/pages/verify-email.tsx +24 -0
@@ -0,0 +1,133 @@
1
+ import { resolveRateLimit } from '../define_config.js';
2
+ import { createAuthThrottles } from './rate_limit.js';
3
+ import { ACCOUNT_SESSION_KEY } from './middleware/account_auth.js';
4
+ /**
5
+ * Guard inline do console de conta. Usamos uma closure (forma confiável do
6
+ * `.use()` do AdonisJS) em vez de `() => import(middleware)` — a forma lazy de
7
+ * classe NÃO era aplicada em runtime num grupo, deixando /account/tokens e
8
+ * /account/mfa acessíveis sem sessão.
9
+ */
10
+ const accountGuard = async (ctx, next) => {
11
+ if (!ctx.session?.get(ACCOUNT_SESSION_KEY)) {
12
+ return ctx.response.redirect('/account/login');
13
+ }
14
+ return next();
15
+ };
16
+ /**
17
+ * Guard do console admin (B6). Como o `accountGuard`, é uma closure inline (forma
18
+ * confiável do `.use()` num grupo). Exige:
19
+ * 1. sessão de conta ativa (senão → /account/login);
20
+ * 2. a conta logada com pelo menos UMA das `config.admin.roles` nas roles globais
21
+ * (senão → /account/tokens, evitando vazar a existência do /admin).
22
+ * As roles permitidas são resolvidas em runtime do `authkit.server` (config lazy).
23
+ */
24
+ export const adminGuard = async (ctx, next) => {
25
+ const accountId = ctx.session?.get(ACCOUNT_SESSION_KEY);
26
+ if (!accountId) {
27
+ return ctx.response.redirect('/account/login');
28
+ }
29
+ const service = await ctx.containerResolver.make('authkit.server');
30
+ const cfg = service.config;
31
+ const allowed = cfg.admin.roles;
32
+ const account = await cfg.accountStore.findById(accountId);
33
+ const roles = account?.globalRoles ?? [];
34
+ const isAdmin = roles.some((r) => allowed.includes(r));
35
+ if (!isAdmin) {
36
+ return ctx.response.redirect('/account/tokens');
37
+ }
38
+ return next();
39
+ };
40
+ const C = {
41
+ oidc: () => import('../controllers/oidc_callback_controller.js'),
42
+ interaction: () => import('./controllers/interaction_controller.js'),
43
+ registration: () => import('./controllers/registration_controller.js'),
44
+ social: () => import('./controllers/social_controller.js'),
45
+ patIntrospection: () => import('./controllers/pat_introspection_controller.js'),
46
+ accountSession: () => import('./controllers/account_session_controller.js'),
47
+ accountTokens: () => import('./controllers/account_tokens_controller.js'),
48
+ accountMfa: () => import('./controllers/account_mfa_controller.js'),
49
+ adminDashboard: () => import('./controllers/admin/admin_dashboard_controller.js'),
50
+ adminUsers: () => import('./controllers/admin/admin_users_controller.js'),
51
+ adminClients: () => import('./controllers/admin/admin_clients_controller.js'),
52
+ adminAudit: () => import('./controllers/admin/admin_audit_controller.js'),
53
+ };
54
+ /**
55
+ * Monta todas as rotas do host-kit do Authorization Server numa chamada.
56
+ * Substitui registerOidcRoutes + o hand-wiring do start/routes.ts do host.
57
+ */
58
+ export function registerAuthHost(router, opts) {
59
+ const mount = opts.mountPath;
60
+ // Throttles opt-in (anti-brute-force). `undefined` quando rate-limit desligado.
61
+ const throttles = createAuthThrottles(resolveRateLimit(opts.rateLimit));
62
+ // Helpers: aplicam o middleware de throttle quando presente; senão no-op.
63
+ const withLogin = (route) => {
64
+ if (throttles)
65
+ route.use([throttles.login]);
66
+ };
67
+ const withIntrospection = (route) => {
68
+ if (throttles)
69
+ route.use([throttles.introspection]);
70
+ };
71
+ // Provider OIDC (wildcard + root) — o que registerOidcRoutes fazia.
72
+ router.any(`${mount}/*`, [C.oidc]).as('authkit.oidc.wildcard');
73
+ router.any(mount, [C.oidc]).as('authkit.oidc.root');
74
+ // Interaction (login multi-step + consent + signup).
75
+ router.get('/auth/interaction/:uid', [C.interaction, 'show']);
76
+ router.post('/auth/interaction/:uid/identifier', [C.interaction, 'identifier']);
77
+ withLogin(router.post('/auth/interaction/:uid/login', [C.interaction, 'login']));
78
+ withLogin(router.post('/auth/interaction/:uid/mfa', [C.interaction, 'mfaVerify']));
79
+ // Passkey como 2º fator alternativo no login (begin/finish; challenge na sessão).
80
+ router.post('/auth/interaction/:uid/passkey/options', [C.interaction, 'passkeyOptions']);
81
+ withLogin(router.post('/auth/interaction/:uid/passkey/verify', [C.interaction, 'passkeyVerify']));
82
+ router.post('/auth/interaction/:uid/consent', [C.interaction, 'consent']);
83
+ router.get('/auth/interaction/:uid/switch', [C.interaction, 'switchIdentifier']);
84
+ router.get('/auth/interaction/:uid/signup', [C.registration, 'showSignup']);
85
+ withLogin(router.post('/auth/interaction/:uid/signup', [C.registration, 'signup']));
86
+ // Recuperação de senha (standalone).
87
+ router.get('/auth/forgot-password', [C.registration, 'showForgot']);
88
+ withLogin(router.post('/auth/forgot-password', [C.registration, 'forgot']));
89
+ router.get('/auth/reset-password', [C.registration, 'showReset']);
90
+ withLogin(router.post('/auth/reset-password', [C.registration, 'reset']));
91
+ // Verificação de e-mail (standalone, GET-only — consome o token do link).
92
+ router.get('/auth/verify-email', [C.registration, 'verifyEmail']);
93
+ // Login social (opt-in).
94
+ if (opts.social) {
95
+ router.get('/auth/:provider/redirect/:uid', [C.social, 'redirect']);
96
+ router.get('/auth/:provider/callback', [C.social, 'callback']);
97
+ }
98
+ // PAT introspection (server-to-server).
99
+ withIntrospection(router.post('/authkit/pat/introspect', [C.patIntrospection, 'handle']));
100
+ // Console de conta (login de sessão do IdP + gerência de PAT).
101
+ router.get('/account/login', [C.accountSession, 'show']);
102
+ router.post('/account/login', [C.accountSession, 'login']);
103
+ router.post('/account/logout', [C.accountSession, 'logout']);
104
+ // Rotas de tokens protegidas por AccountAuthMiddleware (redireciona para /account/login se não autenticado).
105
+ router
106
+ .group(() => {
107
+ router.get('/account/tokens', [C.accountTokens, 'index']);
108
+ router.post('/account/tokens', [C.accountTokens, 'store']);
109
+ router.post('/account/tokens/:id/revoke', [C.accountTokens, 'destroy']);
110
+ // MFA / TOTP (enrollment, confirmação, disable).
111
+ router.get('/account/mfa', [C.accountMfa, 'index']);
112
+ router.post('/account/mfa/enroll', [C.accountMfa, 'enroll']);
113
+ router.post('/account/mfa/confirm', [C.accountMfa, 'confirm']);
114
+ router.post('/account/mfa/disable', [C.accountMfa, 'disable']);
115
+ // MFA / WebAuthn (passkeys): registro (begin/finish) + remoção.
116
+ router.post('/account/mfa/passkeys/options', [C.accountMfa, 'passkeyRegisterOptions']);
117
+ router.post('/account/mfa/passkeys/verify', [C.accountMfa, 'passkeyRegisterVerify']);
118
+ router.post('/account/mfa/passkeys/:id/remove', [C.accountMfa, 'passkeyRemove']);
119
+ })
120
+ .use([accountGuard]);
121
+ // Console admin (opt-in — B6). Protegido pelo adminGuard (sessão + role global).
122
+ if (opts.admin) {
123
+ router
124
+ .group(() => {
125
+ router.get('/admin', [C.adminDashboard, 'index']);
126
+ router.get('/admin/users', [C.adminUsers, 'index']);
127
+ router.post('/admin/users/:id/roles', [C.adminUsers, 'updateRoles']);
128
+ router.get('/admin/clients', [C.adminClients, 'index']);
129
+ router.get('/admin/audit', [C.adminAudit, 'index']);
130
+ })
131
+ .use([adminGuard]);
132
+ }
133
+ }
@@ -0,0 +1,3 @@
1
+ import type { HttpContext } from '@adonisjs/core/http';
2
+ /** Renderer do seam para hosts Edge. As views são donas-da-lib (disco `authkit::`). */
3
+ export declare function edgeRenderer(): (ctx: HttpContext, view: string, props: Record<string, unknown>) => Promise<any>;
@@ -0,0 +1,29 @@
1
+ import { DEFAULT_MESSAGES, translate } from '../i18n.js';
2
+ /**
3
+ * Resolve o catálogo de mensagens ativo a partir do `authkit.server` (config
4
+ * resolvida com o locale do host). Defensivo: se o container/serviço não estiver
5
+ * disponível (ex.: teste unitário do renderer com ctx mockado), cai no default
6
+ * pt-BR embutido — as views continuam renderizando.
7
+ */
8
+ async function resolveMessagesFromCtx(ctx) {
9
+ try {
10
+ const service = await ctx.containerResolver?.make?.('authkit.server');
11
+ const messages = service?.config?.messages;
12
+ if (messages)
13
+ return messages;
14
+ }
15
+ catch {
16
+ // sem container/serviço — usa o default
17
+ }
18
+ return { ...DEFAULT_MESSAGES };
19
+ }
20
+ /** Renderer do seam para hosts Edge. As views são donas-da-lib (disco `authkit::`). */
21
+ export function edgeRenderer() {
22
+ return async (ctx, view, props) => {
23
+ const messages = await resolveMessagesFromCtx(ctx);
24
+ // Helper `t` exposto às views: `{{ t('login.title') }}` ou
25
+ // `{{ t('login.greeting', { name: account.fullName }) }}`.
26
+ const t = (key, params) => translate(messages, key, params);
27
+ return ctx.view.render(`authkit::${view}`, { ...props, t, messages });
28
+ };
29
+ }
@@ -0,0 +1,5 @@
1
+ import type { HttpContext } from '@adonisjs/core/http';
2
+ /** Renderer do seam para hosts Inertia/React. As páginas vivem no host em `inertia/pages/<prefix>/*`. */
3
+ export declare function inertiaRenderer(opts: {
4
+ prefix: string;
5
+ }): (ctx: HttpContext, view: string, props: Record<string, unknown>) => Promise<any>;
@@ -0,0 +1,26 @@
1
+ import { DEFAULT_MESSAGES } from '../i18n.js';
2
+ /**
3
+ * Resolve o catálogo de mensagens ativo a partir do `authkit.server`. Defensivo:
4
+ * sem container/serviço (ex.: teste unitário com ctx mockado), cai no default
5
+ * pt-BR embutido.
6
+ */
7
+ async function resolveMessagesFromCtx(ctx) {
8
+ try {
9
+ const service = await ctx.containerResolver?.make?.('authkit.server');
10
+ const messages = service?.config?.messages;
11
+ if (messages)
12
+ return messages;
13
+ }
14
+ catch {
15
+ // sem container/serviço — usa o default
16
+ }
17
+ return { ...DEFAULT_MESSAGES };
18
+ }
19
+ /** Renderer do seam para hosts Inertia/React. As páginas vivem no host em `inertia/pages/<prefix>/*`. */
20
+ export function inertiaRenderer(opts) {
21
+ return async (ctx, view, props) => {
22
+ // `messages` vai como shared prop para as páginas React traduzirem.
23
+ const messages = await resolveMessagesFromCtx(ctx);
24
+ return ctx.inertia.render(`${opts.prefix}/${view}`, { ...props, messages });
25
+ };
26
+ }
@@ -0,0 +1,39 @@
1
+ export declare const signupValidator: import("@vinejs/vine").VineValidator<import("@vinejs/vine").VineObject<{
2
+ email: import("@vinejs/vine").VineString;
3
+ fullName: import("@vinejs/vine").VineString;
4
+ password: import("@vinejs/vine").VineString;
5
+ }, {
6
+ email: string;
7
+ password: string;
8
+ fullName: string;
9
+ }, {
10
+ email: string;
11
+ password: string;
12
+ fullName: string;
13
+ }, {
14
+ email: string;
15
+ password: string;
16
+ fullName: string;
17
+ }>, Record<string, any> | undefined>;
18
+ export declare const forgotPasswordValidator: import("@vinejs/vine").VineValidator<import("@vinejs/vine").VineObject<{
19
+ email: import("@vinejs/vine").VineString;
20
+ }, {
21
+ email: string;
22
+ }, {
23
+ email: string;
24
+ }, {
25
+ email: string;
26
+ }>, Record<string, any> | undefined>;
27
+ export declare const resetPasswordValidator: import("@vinejs/vine").VineValidator<import("@vinejs/vine").VineObject<{
28
+ token: import("@vinejs/vine").VineString;
29
+ password: import("@vinejs/vine").VineString;
30
+ }, {
31
+ password: string;
32
+ token: string;
33
+ }, {
34
+ password: string;
35
+ token: string;
36
+ }, {
37
+ password: string;
38
+ token: string;
39
+ }>, Record<string, any> | undefined>;
@@ -0,0 +1,13 @@
1
+ import vine from '@vinejs/vine';
2
+ export const signupValidator = vine.compile(vine.object({
3
+ email: vine.string().trim().email().normalizeEmail(),
4
+ fullName: vine.string().trim().minLength(2).maxLength(255),
5
+ password: vine.string().minLength(8).maxLength(255),
6
+ }));
7
+ export const forgotPasswordValidator = vine.compile(vine.object({
8
+ email: vine.string().trim().email().normalizeEmail(),
9
+ }));
10
+ export const resetPasswordValidator = vine.compile(vine.object({
11
+ token: vine.string().trim().minLength(1),
12
+ password: vine.string().minLength(8).maxLength(255),
13
+ }));
@@ -0,0 +1,6 @@
1
+ export type SigningAlg = 'RS256' | 'ES256' | 'PS256' | 'EdDSA';
2
+ export interface ManagedJwks {
3
+ keys: Record<string, any>[];
4
+ }
5
+ /** Gera um JWKS privado (1 chave) pronto para `oidc-provider`. */
6
+ export declare function generateJwks(alg: SigningAlg): Promise<ManagedJwks>;
@@ -0,0 +1,11 @@
1
+ import { generateKeyPair, exportJWK } from 'jose';
2
+ import { randomUUID } from 'node:crypto';
3
+ /** Gera um JWKS privado (1 chave) pronto para `oidc-provider`. */
4
+ export async function generateJwks(alg) {
5
+ const { privateKey } = await generateKeyPair(alg, { extractable: true });
6
+ const jwk = (await exportJWK(privateKey));
7
+ jwk.use = 'sig';
8
+ jwk.alg = alg;
9
+ jwk.kid = randomUUID();
10
+ return { keys: [jwk] };
11
+ }
@@ -0,0 +1,19 @@
1
+ import { BaseModel } from '@adonisjs/lucid/orm';
2
+ import type { NormalizeConstructor } from '@adonisjs/core/types/helpers';
3
+ import { DateTime } from 'luxon';
4
+ import type { AuditEventType } from '../audit/audit_sink.js';
5
+ /** Instância composta pelo mixin {@link withAuditLog}. */
6
+ export interface AuditLogRow {
7
+ type: AuditEventType;
8
+ accountId: string | null;
9
+ email: string | null;
10
+ clientId: string | null;
11
+ actorId: string | null;
12
+ ip: string | null;
13
+ metadata: Record<string, unknown> | null;
14
+ createdAt: DateTime;
15
+ }
16
+ export type AuditLogClass<Model extends NormalizeConstructor<typeof BaseModel>> = Model & {
17
+ new (...args: any[]): AuditLogRow;
18
+ };
19
+ export declare function withAuditLog(): <Model extends NormalizeConstructor<typeof BaseModel>>(superclass: Model) => AuditLogClass<Model>;
@@ -0,0 +1,41 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { column } from '@adonisjs/lucid/orm';
8
+ export function withAuditLog() {
9
+ return (superclass) => {
10
+ class AuditLogMixin extends superclass {
11
+ }
12
+ __decorate([
13
+ column()
14
+ ], AuditLogMixin.prototype, "type", void 0);
15
+ __decorate([
16
+ column()
17
+ ], AuditLogMixin.prototype, "accountId", void 0);
18
+ __decorate([
19
+ column()
20
+ ], AuditLogMixin.prototype, "email", void 0);
21
+ __decorate([
22
+ column()
23
+ ], AuditLogMixin.prototype, "clientId", void 0);
24
+ __decorate([
25
+ column()
26
+ ], AuditLogMixin.prototype, "actorId", void 0);
27
+ __decorate([
28
+ column()
29
+ ], AuditLogMixin.prototype, "ip", void 0);
30
+ __decorate([
31
+ column({
32
+ prepare: (value) => value ? JSON.stringify(value) : null,
33
+ consume: (value) => (value ? JSON.parse(value) : null),
34
+ })
35
+ ], AuditLogMixin.prototype, "metadata", void 0);
36
+ __decorate([
37
+ column.dateTime({ autoCreate: true })
38
+ ], AuditLogMixin.prototype, "createdAt", void 0);
39
+ return AuditLogMixin;
40
+ };
41
+ }
@@ -0,0 +1,18 @@
1
+ import { BaseModel } from '@adonisjs/lucid/orm';
2
+ import type { NormalizeConstructor } from '@adonisjs/core/types/helpers';
3
+ /**
4
+ * Instância composta pelo mixin {@link withAuthUser}.
5
+ */
6
+ export interface AuthUserRow {
7
+ email: string;
8
+ password: string;
9
+ globalRoles: string[];
10
+ verifyPassword(plain: string): Promise<boolean>;
11
+ }
12
+ /**
13
+ * Classe resultante da composição com o mixin {@link withAuthUser}.
14
+ */
15
+ export type AuthUserClass<Model extends NormalizeConstructor<typeof BaseModel>> = Model & {
16
+ new (...args: any[]): AuthUserRow;
17
+ };
18
+ export declare function withAuthUser(): <Model extends NormalizeConstructor<typeof BaseModel>>(superclass: Model) => AuthUserClass<Model>;
@@ -0,0 +1,39 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { column, beforeSave } from '@adonisjs/lucid/orm';
8
+ import { Scrypt } from '@adonisjs/core/hash/drivers/scrypt';
9
+ const hasher = new Scrypt({});
10
+ export function withAuthUser() {
11
+ return (superclass) => {
12
+ class AuthUserMixin extends superclass {
13
+ static async hashAuthUserPassword(user) {
14
+ if (user.$dirty.password) {
15
+ user.password = await hasher.make(user.password);
16
+ }
17
+ }
18
+ async verifyPassword(plain) {
19
+ return hasher.verify(this.password, plain);
20
+ }
21
+ }
22
+ __decorate([
23
+ column()
24
+ ], AuthUserMixin.prototype, "email", void 0);
25
+ __decorate([
26
+ column({ serializeAs: null })
27
+ ], AuthUserMixin.prototype, "password", void 0);
28
+ __decorate([
29
+ column({
30
+ prepare: (value) => JSON.stringify(value ?? []),
31
+ consume: (value) => (value ? JSON.parse(value) : []),
32
+ })
33
+ ], AuthUserMixin.prototype, "globalRoles", void 0);
34
+ __decorate([
35
+ beforeSave()
36
+ ], AuthUserMixin, "hashAuthUserPassword", null);
37
+ return AuthUserMixin;
38
+ };
39
+ }
@@ -0,0 +1,20 @@
1
+ import { BaseModel } from '@adonisjs/lucid/orm';
2
+ import type { NormalizeConstructor } from '@adonisjs/core/types/helpers';
3
+ import { DateTime } from 'luxon';
4
+ /**
5
+ * Instância composta pelo mixin {@link withCredentials}.
6
+ */
7
+ export interface CredentialsRow {
8
+ emailVerifiedAt: DateTime | null;
9
+ emailVerificationToken: string | null;
10
+ passwordResetToken: string | null;
11
+ passwordResetExpiresAt: DateTime | null;
12
+ readonly isEmailVerified: boolean;
13
+ }
14
+ /**
15
+ * Classe resultante da composição com o mixin {@link withCredentials}.
16
+ */
17
+ export type CredentialsClass<Model extends NormalizeConstructor<typeof BaseModel>> = Model & {
18
+ new (...args: any[]): CredentialsRow;
19
+ };
20
+ export declare function withCredentials(): <Model extends NormalizeConstructor<typeof BaseModel>>(superclass: Model) => CredentialsClass<Model>;
@@ -0,0 +1,29 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { column } from '@adonisjs/lucid/orm';
8
+ export function withCredentials() {
9
+ return (superclass) => {
10
+ class CredentialsMixin extends superclass {
11
+ get isEmailVerified() {
12
+ return this.emailVerifiedAt !== null;
13
+ }
14
+ }
15
+ __decorate([
16
+ column.dateTime()
17
+ ], CredentialsMixin.prototype, "emailVerifiedAt", void 0);
18
+ __decorate([
19
+ column({ serializeAs: null })
20
+ ], CredentialsMixin.prototype, "emailVerificationToken", void 0);
21
+ __decorate([
22
+ column({ serializeAs: null })
23
+ ], CredentialsMixin.prototype, "passwordResetToken", void 0);
24
+ __decorate([
25
+ column.dateTime({ serializeAs: null })
26
+ ], CredentialsMixin.prototype, "passwordResetExpiresAt", void 0);
27
+ return CredentialsMixin;
28
+ };
29
+ }
@@ -0,0 +1,31 @@
1
+ import { BaseModel } from '@adonisjs/lucid/orm';
2
+ import type { NormalizeConstructor } from '@adonisjs/core/types/helpers';
3
+ import { DateTime } from 'luxon';
4
+ /**
5
+ * Instância composta pelo mixin {@link withMfa}.
6
+ *
7
+ * NOTA DE SEGURANÇA: `totpSecret` é o segredo TOTP em claro. No MVP ele é
8
+ * armazenado as-is — encriptar em repouso (ex.: app-key / KMS) é um follow-up.
9
+ */
10
+ export interface MfaRow {
11
+ /** Segredo TOTP (base32). null = sem enrollment. Pendente enquanto `mfaEnabledAt` for null. */
12
+ totpSecret: string | null;
13
+ /** Setado quando o enrollment é confirmado. null = MFA desligado. */
14
+ mfaEnabledAt: DateTime | null;
15
+ /** Hashes (sha256) dos recovery codes; consumidos single-use. */
16
+ recoveryCodes: string[] | null;
17
+ /** true quando o MFA está ativo (segredo confirmado). */
18
+ readonly isMfaEnabled: boolean;
19
+ }
20
+ /**
21
+ * Classe resultante da composição com o mixin {@link withMfa}.
22
+ */
23
+ export type MfaClass<Model extends NormalizeConstructor<typeof BaseModel>> = Model & {
24
+ new (...args: any[]): MfaRow;
25
+ };
26
+ /**
27
+ * Mixin de MFA/TOTP. Adiciona as colunas `totp_secret`, `mfa_enabled_at` e
28
+ * `recovery_codes` ao model de credenciais. Mantido separado de
29
+ * {@link withCredentials} para clareza; componha ambos no model do host.
30
+ */
31
+ export declare function withMfa(): <Model extends NormalizeConstructor<typeof BaseModel>>(superclass: Model) => MfaClass<Model>;
@@ -0,0 +1,39 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { column } from '@adonisjs/lucid/orm';
8
+ /**
9
+ * Mixin de MFA/TOTP. Adiciona as colunas `totp_secret`, `mfa_enabled_at` e
10
+ * `recovery_codes` ao model de credenciais. Mantido separado de
11
+ * {@link withCredentials} para clareza; componha ambos no model do host.
12
+ */
13
+ export function withMfa() {
14
+ return (superclass) => {
15
+ class MfaMixin extends superclass {
16
+ get isMfaEnabled() {
17
+ return this.mfaEnabledAt !== null;
18
+ }
19
+ }
20
+ __decorate([
21
+ column({ serializeAs: null })
22
+ ], MfaMixin.prototype, "totpSecret", void 0);
23
+ __decorate([
24
+ column.dateTime()
25
+ ], MfaMixin.prototype, "mfaEnabledAt", void 0);
26
+ __decorate([
27
+ column({
28
+ serializeAs: null,
29
+ prepare: (value) => (value ? JSON.stringify(value) : null),
30
+ consume: (value) => {
31
+ if (value === null || value === undefined)
32
+ return null;
33
+ return Array.isArray(value) ? value : JSON.parse(value);
34
+ },
35
+ })
36
+ ], MfaMixin.prototype, "recoveryCodes", void 0);
37
+ return MfaMixin;
38
+ };
39
+ }
@@ -0,0 +1,19 @@
1
+ import { BaseModel } from '@adonisjs/lucid/orm';
2
+ import type { NormalizeConstructor } from '@adonisjs/core/types/helpers';
3
+ import { DateTime } from 'luxon';
4
+ /** Instância composta pelo mixin {@link withPersonalAccessToken}. */
5
+ export interface PersonalAccessTokenRow {
6
+ userId: string;
7
+ name: string;
8
+ tokenHash: string;
9
+ scopes: string[];
10
+ audience: string | null;
11
+ expiresAt: DateTime | null;
12
+ lastUsedAt: DateTime | null;
13
+ createdAt: DateTime;
14
+ updatedAt: DateTime;
15
+ }
16
+ export type PersonalAccessTokenClass<Model extends NormalizeConstructor<typeof BaseModel>> = Model & {
17
+ new (...args: any[]): PersonalAccessTokenRow;
18
+ };
19
+ export declare function withPersonalAccessToken(): <Model extends NormalizeConstructor<typeof BaseModel>>(superclass: Model) => PersonalAccessTokenClass<Model>;
@@ -0,0 +1,44 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { column } from '@adonisjs/lucid/orm';
8
+ export function withPersonalAccessToken() {
9
+ return (superclass) => {
10
+ class PatMixin extends superclass {
11
+ }
12
+ __decorate([
13
+ column()
14
+ ], PatMixin.prototype, "userId", void 0);
15
+ __decorate([
16
+ column()
17
+ ], PatMixin.prototype, "name", void 0);
18
+ __decorate([
19
+ column({ serializeAs: null })
20
+ ], PatMixin.prototype, "tokenHash", void 0);
21
+ __decorate([
22
+ column({
23
+ prepare: (value) => (value ? JSON.stringify(value) : null),
24
+ consume: (value) => (value ? JSON.parse(value) : []),
25
+ })
26
+ ], PatMixin.prototype, "scopes", void 0);
27
+ __decorate([
28
+ column()
29
+ ], PatMixin.prototype, "audience", void 0);
30
+ __decorate([
31
+ column.dateTime()
32
+ ], PatMixin.prototype, "expiresAt", void 0);
33
+ __decorate([
34
+ column.dateTime()
35
+ ], PatMixin.prototype, "lastUsedAt", void 0);
36
+ __decorate([
37
+ column.dateTime({ autoCreate: true })
38
+ ], PatMixin.prototype, "createdAt", void 0);
39
+ __decorate([
40
+ column.dateTime({ autoCreate: true, autoUpdate: true })
41
+ ], PatMixin.prototype, "updatedAt", void 0);
42
+ return PatMixin;
43
+ };
44
+ }
@@ -0,0 +1,20 @@
1
+ import { BaseModel } from '@adonisjs/lucid/orm';
2
+ import type { NormalizeConstructor } from '@adonisjs/core/types/helpers';
3
+ import { DateTime } from 'luxon';
4
+ /**
5
+ * Instância composta pelo mixin {@link withProviderIdentity}. Liga uma conta
6
+ * (`accountId`) a uma identidade externa `(provider, providerUserId)` — ex.:
7
+ * Google, GitHub. Uma conta pode ter várias identidades (account linking).
8
+ */
9
+ export interface ProviderIdentityRow {
10
+ provider: string;
11
+ providerUserId: string;
12
+ accountId: string;
13
+ email: string | null;
14
+ createdAt: DateTime;
15
+ updatedAt: DateTime;
16
+ }
17
+ export type ProviderIdentityClass<Model extends NormalizeConstructor<typeof BaseModel>> = Model & {
18
+ new (...args: any[]): ProviderIdentityRow;
19
+ };
20
+ export declare function withProviderIdentity(): <Model extends NormalizeConstructor<typeof BaseModel>>(superclass: Model) => ProviderIdentityClass<Model>;
@@ -0,0 +1,32 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { column } from '@adonisjs/lucid/orm';
8
+ export function withProviderIdentity() {
9
+ return (superclass) => {
10
+ class ProviderIdentityMixin extends superclass {
11
+ }
12
+ __decorate([
13
+ column()
14
+ ], ProviderIdentityMixin.prototype, "provider", void 0);
15
+ __decorate([
16
+ column()
17
+ ], ProviderIdentityMixin.prototype, "providerUserId", void 0);
18
+ __decorate([
19
+ column()
20
+ ], ProviderIdentityMixin.prototype, "accountId", void 0);
21
+ __decorate([
22
+ column()
23
+ ], ProviderIdentityMixin.prototype, "email", void 0);
24
+ __decorate([
25
+ column.dateTime({ autoCreate: true })
26
+ ], ProviderIdentityMixin.prototype, "createdAt", void 0);
27
+ __decorate([
28
+ column.dateTime({ autoCreate: true, autoUpdate: true })
29
+ ], ProviderIdentityMixin.prototype, "updatedAt", void 0);
30
+ return ProviderIdentityMixin;
31
+ };
32
+ }