@adonis-agora/authkit-server 0.46.0 → 0.48.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 (195) hide show
  1. package/build/commands/clients_create.js +4 -4
  2. package/build/commands/configure.js +3 -2
  3. package/build/commands/doctor.js +8 -5
  4. package/build/commands/eject.js +1 -1
  5. package/build/commands/expire_scan.js +7 -5
  6. package/build/commands/import_users.js +1 -1
  7. package/build/commands/keys_rotate.js +7 -4
  8. package/build/host/views/account/apps.edge +5 -5
  9. package/build/host/views/account/login.edge +1 -1
  10. package/build/host/views/account/mfa.edge +7 -7
  11. package/build/host/views/account/orgs.edge +5 -5
  12. package/build/host/views/account/security.edge +10 -10
  13. package/build/host/views/account/tokens.edge +5 -5
  14. package/build/host/views/otp-unlock.edge +2 -2
  15. package/build/index.d.ts +7 -7
  16. package/build/index.js +5 -5
  17. package/build/providers/authkit_server_provider.d.ts +2 -2
  18. package/build/providers/authkit_server_provider.js +26 -10
  19. package/build/services/booted_app.d.ts +8 -0
  20. package/build/services/booted_app.js +27 -0
  21. package/build/services/main.d.ts +8 -1
  22. package/build/services/main.js +10 -2
  23. package/build/src/accounts/lucid_account_store.d.ts +3 -3
  24. package/build/src/accounts/lucid_account_store.js +6 -8
  25. package/build/src/accounts/lucid_store/core.js +1 -1
  26. package/build/src/accounts/lucid_store/mfa.js +1 -1
  27. package/build/src/accounts/lucid_store/organizations.js +2 -4
  28. package/build/src/accounts/lucid_store/password_hygiene.js +1 -4
  29. package/build/src/accounts/lucid_store/shared.d.ts +1 -1
  30. package/build/src/accounts/lucid_stores.d.ts +3 -3
  31. package/build/src/accounts/lucid_stores.js +2 -2
  32. package/build/src/adapters/database_adapter.js +3 -1
  33. package/build/src/adapters/factory.js +1 -1
  34. package/build/src/adapters/redis_adapter.js +2 -1
  35. package/build/src/commands/expire_scan_command.js +19 -5
  36. package/build/src/commands/settings_commands.js +22 -5
  37. package/build/src/define_config.d.ts +20 -20
  38. package/build/src/define_config.js +62 -67
  39. package/build/src/doctor/checks.js +45 -20
  40. package/build/src/events/dispatcher.d.ts +1 -1
  41. package/build/src/events/dispatcher.js +10 -10
  42. package/build/src/host/account_api/account_api_controller.js +95 -39
  43. package/build/src/host/account_deletion_ops.d.ts +4 -4
  44. package/build/src/host/account_deletion_ops.js +5 -5
  45. package/build/src/host/account_deletion_service.d.ts +2 -2
  46. package/build/src/host/account_deletion_service.js +2 -2
  47. package/build/src/host/account_export_service.d.ts +1 -1
  48. package/build/src/host/account_export_service.js +4 -6
  49. package/build/src/host/account_home.d.ts +0 -8
  50. package/build/src/host/account_home.js +6 -4
  51. package/build/src/host/account_login_url.d.ts +44 -0
  52. package/build/src/host/account_login_url.js +58 -0
  53. package/build/src/host/account_paths.d.ts +120 -0
  54. package/build/src/host/account_paths.js +161 -0
  55. package/build/src/host/admin_api/admin_orgs_service.d.ts +2 -2
  56. package/build/src/host/admin_api/admin_orgs_service.js +3 -2
  57. package/build/src/host/admin_api/admin_users_service.d.ts +12 -12
  58. package/build/src/host/admin_api/admin_users_service.js +20 -20
  59. package/build/src/host/admin_api/api_clients_controller.js +2 -2
  60. package/build/src/host/admin_api/api_misc_controller.js +2 -2
  61. package/build/src/host/admin_api/api_orgs_controller.js +3 -3
  62. package/build/src/host/admin_api/api_settings_controller.js +11 -5
  63. package/build/src/host/admin_api/api_users_controller.d.ts +2 -2
  64. package/build/src/host/admin_api/api_users_controller.js +37 -38
  65. package/build/src/host/admin_api/dto.d.ts +4 -4
  66. package/build/src/host/admin_clients_service.js +5 -4
  67. package/build/src/host/admin_console/admin_shell_controller.js +4 -2
  68. package/build/src/host/admin_console/console_audit_controller.js +1 -1
  69. package/build/src/host/admin_console/console_clients_controller.js +5 -3
  70. package/build/src/host/admin_console/console_impersonation_controller.js +1 -1
  71. package/build/src/host/admin_console/console_keys_controller.js +2 -4
  72. package/build/src/host/admin_console/console_orgs_controller.js +3 -3
  73. package/build/src/host/admin_console/console_roles_controller.js +30 -12
  74. package/build/src/host/admin_console/console_sessions_controller.js +3 -3
  75. package/build/src/host/admin_console/console_settings_controller.js +11 -5
  76. package/build/src/host/admin_console/console_users_controller.d.ts +2 -2
  77. package/build/src/host/admin_console/console_users_controller.js +46 -47
  78. package/build/src/host/admin_prefix.js +2 -2
  79. package/build/src/host/admin_sessions_service.d.ts +3 -1
  80. package/build/src/host/admin_sessions_service.js +36 -6
  81. package/build/src/host/adonis_auth_sync.d.ts +1 -1
  82. package/build/src/host/adonis_auth_user_provider.d.ts +1 -1
  83. package/build/src/host/app_key.js +1 -1
  84. package/build/src/host/avatar_storage.js +1 -3
  85. package/build/src/host/config_locks.js +1 -3
  86. package/build/src/host/console_session.d.ts +4 -0
  87. package/build/src/host/console_session.js +9 -2
  88. package/build/src/host/controllers/account_apps_controller.js +3 -2
  89. package/build/src/host/controllers/account_confirm_controller.js +2 -6
  90. package/build/src/host/controllers/account_mfa_controller.js +63 -12
  91. package/build/src/host/controllers/account_orgs_controller.js +53 -30
  92. package/build/src/host/controllers/account_security_controller.d.ts +2 -2
  93. package/build/src/host/controllers/account_security_controller.js +116 -116
  94. package/build/src/host/controllers/account_session_controller.js +7 -5
  95. package/build/src/host/controllers/account_tokens_controller.js +14 -2
  96. package/build/src/host/controllers/interaction_controller.js +59 -22
  97. package/build/src/host/controllers/pat_introspection_controller.js +10 -0
  98. package/build/src/host/controllers/registration_controller.js +28 -13
  99. package/build/src/host/default_mailer.js +11 -7
  100. package/build/src/host/durable/account_deletion_workflow.d.ts +2 -2
  101. package/build/src/host/durable/account_deletion_workflow.js +14 -14
  102. package/build/src/host/durable/account_export_workflow.d.ts +2 -2
  103. package/build/src/host/durable/account_export_workflow.js +12 -12
  104. package/build/src/host/durable/index.d.ts +5 -5
  105. package/build/src/host/durable/index.js +6 -6
  106. package/build/src/host/i18n.d.ts +1376 -1376
  107. package/build/src/host/i18n.js +1380 -1380
  108. package/build/src/host/impersonation.js +1 -7
  109. package/build/src/host/key_rotation_actions.d.ts +1 -1
  110. package/build/src/host/login_attempt.js +35 -6
  111. package/build/src/host/login_notify.js +1 -1
  112. package/build/src/host/middleware/account_auth.js +3 -1
  113. package/build/src/host/otp_lockout.js +1 -3
  114. package/build/src/host/rate_limit.js +1 -1
  115. package/build/src/host/register_auth_host.d.ts +96 -2
  116. package/build/src/host/register_auth_host.js +196 -91
  117. package/build/src/host/renderers/edge_renderer.js +19 -1
  118. package/build/src/host/renderers/inertia_renderer.d.ts +63 -0
  119. package/build/src/host/renderers/inertia_renderer.js +63 -0
  120. package/build/src/host/runtime_settings.js +1 -1
  121. package/build/src/host/runtime_toggles.d.ts +3 -3
  122. package/build/src/host/runtime_toggles.js +69 -24
  123. package/build/src/host/security_notice_service.d.ts +1 -1
  124. package/build/src/host/security_notice_service.js +1 -1
  125. package/build/src/host/session_context.js +1 -1
  126. package/build/src/host/sudo/index.d.ts +3 -3
  127. package/build/src/host/sudo/index.js +3 -3
  128. package/build/src/host/sudo/methods/magic_link.js +6 -5
  129. package/build/src/host/sudo/methods/passkey.js +7 -4
  130. package/build/src/host/sudo/methods/password.js +8 -3
  131. package/build/src/host/sudo/runtime.js +6 -13
  132. package/build/src/host/sudo_mode.js +3 -2
  133. package/build/src/host/ui-dist/assets/index-BQoTDiLV.js +137 -0
  134. package/build/src/host/ui-dist/index.html +1 -1
  135. package/build/src/keys/jwks_manager.js +1 -1
  136. package/build/src/keys/keystore.js +1 -1
  137. package/build/src/keys/keystore_manager.d.ts +2 -2
  138. package/build/src/keys/keystore_manager.js +2 -2
  139. package/build/src/keys/keystore_vault.js +8 -4
  140. package/build/src/mixins/with_audit_log.d.ts +1 -1
  141. package/build/src/mixins/with_auth_user.d.ts +1 -1
  142. package/build/src/mixins/with_auth_user.js +1 -1
  143. package/build/src/mixins/with_credentials.d.ts +1 -1
  144. package/build/src/mixins/with_mfa.d.ts +1 -1
  145. package/build/src/mixins/with_personal_access_token.d.ts +1 -1
  146. package/build/src/mixins/with_provider_identity.d.ts +1 -1
  147. package/build/src/mixins/with_webauthn_credential.d.ts +1 -1
  148. package/build/src/observability/diagnostics_bridge.js +2 -2
  149. package/build/src/observability/metrics_service.js +1 -1
  150. package/build/src/observability/telescope/data_providers.d.ts +1 -1
  151. package/build/src/observability/telescope/data_providers.js +29 -43
  152. package/build/src/observability/telescope/extension.d.ts +1 -1
  153. package/build/src/observability/telescope/extension.js +58 -58
  154. package/build/src/observability/telescope/index.d.ts +2 -2
  155. package/build/src/observability/telescope/index.js +2 -2
  156. package/build/src/observability/wire_provider_events.d.ts +1 -1
  157. package/build/src/observability/wire_provider_events.js +11 -11
  158. package/build/src/password/common_passwords.js +1 -1
  159. package/build/src/password/password_manager.d.ts +1 -1
  160. package/build/src/password/password_manager.js +4 -4
  161. package/build/src/pat/lucid_pat_store.js +2 -6
  162. package/build/src/pat/pat_tokens.js +1 -1
  163. package/build/src/provider/interaction_actions.js +1 -1
  164. package/build/src/provider/key_rotation_scheduler.js +3 -1
  165. package/build/src/provider/logout_sources.js +2 -8
  166. package/build/src/provider/oidc_service.d.ts +2 -2
  167. package/build/src/provider/oidc_service.js +22 -14
  168. package/build/src/provider/single_flight_lock.js +1 -3
  169. package/build/src/provider/token_exchange.d.ts +1 -1
  170. package/build/src/schema/ensure.js +3 -1
  171. package/build/stubs/ui/react/components/auth_shell.tsx +19 -19
  172. package/build/stubs/ui/react/pages/account/login.tsx +6 -7
  173. package/build/stubs/ui/react/pages/account/mfa.tsx +16 -15
  174. package/build/stubs/ui/react/pages/account/tokens.tsx +27 -17
  175. package/build/stubs/ui/react/pages/consent.tsx +12 -12
  176. package/build/stubs/ui/react/pages/forgot.tsx +7 -7
  177. package/build/stubs/ui/react/pages/login.tsx +22 -24
  178. package/build/stubs/ui/react/pages/mfa-challenge.tsx +9 -10
  179. package/build/stubs/ui/react/pages/reset.tsx +10 -10
  180. package/build/stubs/ui/react/pages/signup.tsx +30 -16
  181. package/build/stubs/ui/react/pages/verify-email.tsx +6 -6
  182. package/package.json +1 -1
  183. package/stubs/main.ts +2 -2
  184. package/stubs/ui/react/components/auth_shell.tsx +19 -19
  185. package/stubs/ui/react/pages/account/login.tsx +6 -7
  186. package/stubs/ui/react/pages/account/mfa.tsx +16 -15
  187. package/stubs/ui/react/pages/account/tokens.tsx +27 -17
  188. package/stubs/ui/react/pages/consent.tsx +12 -12
  189. package/stubs/ui/react/pages/forgot.tsx +7 -7
  190. package/stubs/ui/react/pages/login.tsx +22 -24
  191. package/stubs/ui/react/pages/mfa-challenge.tsx +9 -10
  192. package/stubs/ui/react/pages/reset.tsx +10 -10
  193. package/stubs/ui/react/pages/signup.tsx +30 -16
  194. package/stubs/ui/react/pages/verify-email.tsx +6 -6
  195. package/build/src/host/ui-dist/assets/index-BmI_J87g.js +0 -137
@@ -1,7 +1,7 @@
1
1
  import { createHmac } from 'node:crypto';
2
- import { checkPasswordPolicy, policyViolationParams, resolvePasswordConfig, DEFAULT_PWNED_TIMEOUT_MS, } from './policy.js';
3
- import { isPasswordPwned } from './pwned.js';
4
2
  import { isCommonPassword } from './common_passwords.js';
3
+ import { DEFAULT_PWNED_TIMEOUT_MS, checkPasswordPolicy, policyViolationParams, resolvePasswordConfig, } from './policy.js';
4
+ import { isPasswordPwned } from './pwned.js';
5
5
  /** Erro de política/vazamento de senha — carrega a chave i18n + params. */
6
6
  export class PasswordPolicyError extends Error {
7
7
  key;
@@ -154,8 +154,8 @@ export class PasswordManager {
154
154
  }
155
155
  if (nativeOk) {
156
156
  // Se este não é o pepper corrente → re-hash obrigatório com o corrente.
157
- const isCurrentPepper = (p === currentPepper) || (!p && !this.pepper);
158
- let rehash = isCurrentPepper ? false : true;
157
+ const isCurrentPepper = p === currentPepper || (!p && !this.pepper);
158
+ let rehash = !isCurrentPepper;
159
159
  if (!rehash) {
160
160
  try {
161
161
  rehash = hooks.needsRehash(hashedPassword);
@@ -23,16 +23,12 @@ export function lucidPatStore(Model) {
23
23
  tokenHash: hashPatToken(token),
24
24
  scopes: input.scopes ?? [],
25
25
  audience: input.audience ?? null,
26
- expiresAt: input.expiresInDays
27
- ? DateTime.now().plus({ days: input.expiresInDays })
28
- : null,
26
+ expiresAt: input.expiresInDays ? DateTime.now().plus({ days: input.expiresInDays }) : null,
29
27
  });
30
28
  return { token, pat: toRecord(row) };
31
29
  },
32
30
  async listForAccount(accountId) {
33
- const rows = await Model.query()
34
- .where('user_id', accountId)
35
- .orderBy('created_at', 'desc');
31
+ const rows = await Model.query().where('user_id', accountId).orderBy('created_at', 'desc');
36
32
  return rows.map(toRecord);
37
33
  },
38
34
  async revoke(accountId, id) {
@@ -1,7 +1,7 @@
1
1
  import { createHash, randomBytes } from 'node:crypto';
2
2
  /** Gera um Personal Access Token opaco no formato `pat_<base64url>`. */
3
3
  export function generatePatToken() {
4
- return 'pat_' + randomBytes(32).toString('base64url');
4
+ return `pat_${randomBytes(32).toString('base64url')}`;
5
5
  }
6
6
  /** SHA-256 (hex) do token — é o que se persiste; o token cru nunca é guardado. */
7
7
  export function hashPatToken(token) {
@@ -20,7 +20,7 @@ export function createInteractionActions(provider, deps) {
20
20
  const login = { accountId };
21
21
  if (extra?.acr)
22
22
  login.acr = extra.acr;
23
- if (extra?.amr && extra.amr.length)
23
+ if (extra?.amr?.length)
24
24
  login.amr = extra.amr;
25
25
  // remember-me: `remember: false` → sessão transiente (transient: true no provider);
26
26
  // `remember: true` ou ausente → sessão persistente (padrão do oidc-provider).
@@ -27,7 +27,9 @@ export class KeyRotationScheduler {
27
27
  start() {
28
28
  if (this.#timer)
29
29
  return;
30
- this.#timer = setInterval(() => { void this.tick(); }, this.opts.intervalMs);
30
+ this.#timer = setInterval(() => {
31
+ void this.tick();
32
+ }, this.opts.intervalMs);
31
33
  if (typeof this.#timer.unref === 'function')
32
34
  this.#timer.unref();
33
35
  }
@@ -45,20 +45,14 @@ export function createLogoutSources(messages) {
45
45
  async logoutSource(ctx, form) {
46
46
  // Injeta o campo de confirmação dentro do form fornecido pela lib.
47
47
  const confirmedForm = form.replace(/<\/form>/, '<input type="hidden" name="logout" value="yes"/></form>');
48
- ctx.body = page(t('logout.title'), `<div class="spinner" aria-hidden="true"></div>` +
49
- `<h1>${esc(t('logout.title'))}</h1>` +
50
- `<p>${esc(t('logout.body'))}</p>` +
51
- confirmedForm +
52
- `<noscript><button type="submit" form="op.logoutForm">${esc(t('logout.fallback'))}</button></noscript>` +
53
- `<script>(function(){var f=document.forms['op.logoutForm'];if(f){f.submit();}})();</script>`);
48
+ ctx.body = page(t('logout.title'), `<div class="spinner" aria-hidden="true"></div><h1>${esc(t('logout.title'))}</h1><p>${esc(t('logout.body'))}</p>${confirmedForm}<noscript><button type="submit" form="op.logoutForm">${esc(t('logout.fallback'))}</button></noscript><script>(function(){var f=document.forms['op.logoutForm'];if(f){f.submit();}})();</script>`);
54
49
  },
55
50
  /**
56
51
  * Mostrada apenas quando NÃO há `post_logout_redirect_uri` (senão o provider
57
52
  * redireciona o browser direto pro RP). Tela de marca em vez do default.
58
53
  */
59
54
  async postLogoutSuccessSource(ctx) {
60
- ctx.body = page(t('logout.success.title'), `<h1>${esc(t('logout.success.title'))}</h1>` +
61
- `<p>${esc(t('logout.success.body'))}</p>`);
55
+ ctx.body = page(t('logout.success.title'), `<h1>${esc(t('logout.success.title'))}</h1>` + `<p>${esc(t('logout.success.body'))}</p>`);
62
56
  },
63
57
  };
64
58
  }
@@ -1,9 +1,9 @@
1
1
  import { type MetricsRecorder } from '@adonis-agora/authkit-core';
2
2
  import type { ResolvedServerConfig } from '../define_config.js';
3
- import { buildProvider, type SessionTtlHolder, type TokenTtlHolder } from './build_provider.js';
4
- import { type InteractionActions } from './interaction_actions.js';
5
3
  import { type ManagedKeyInfo } from '../keys/keystore.js';
6
4
  import type { KeystoreManager } from '../keys/keystore_manager.js';
5
+ import { type SessionTtlHolder, type TokenTtlHolder, buildProvider } from './build_provider.js';
6
+ import { type InteractionActions } from './interaction_actions.js';
7
7
  export declare class OidcService {
8
8
  #private;
9
9
  get provider(): ReturnType<typeof buildProvider>;
@@ -2,21 +2,27 @@ import { timingSafeEqual } from 'node:crypto';
2
2
  import { NoopRecorder } from '@adonis-agora/authkit-core';
3
3
  import Koa from 'koa';
4
4
  import mount from 'koa-mount';
5
+ import { readActiveOrgFromKoaCtx } from '../host/active_org_cookie.js';
6
+ import { isFirstParty } from '../host/branding.js';
7
+ import { listKeyInfos, signingKeyAgeDays } from '../keys/keystore.js';
5
8
  import { wireProviderEvents } from '../observability/wire_provider_events.js';
6
9
  import { buildProvider } from './build_provider.js';
7
10
  import { createInteractionActions } from './interaction_actions.js';
8
11
  import { registerTokenExchange } from './token_exchange.js';
9
- import { readActiveOrgFromKoaCtx } from '../host/active_org_cookie.js';
10
- import { isFirstParty } from '../host/branding.js';
11
- import { signingKeyAgeDays, listKeyInfos } from '../keys/keystore.js';
12
12
  export class OidcService {
13
13
  #provider;
14
14
  #callback;
15
15
  #interactions;
16
16
  #appKey;
17
- get provider() { return this.#provider; }
18
- get callback() { return this.#callback; }
19
- get interactions() { return this.#interactions; }
17
+ get provider() {
18
+ return this.#provider;
19
+ }
20
+ get callback() {
21
+ return this.#callback;
22
+ }
23
+ get interactions() {
24
+ return this.#interactions;
25
+ }
20
26
  /** Pathname do issuer sem barra final (ex.: `/oidc`). Vazio quando montado na raiz. */
21
27
  mountPath;
22
28
  recorder;
@@ -83,9 +89,7 @@ export class OidcService {
83
89
  // do closure `claims()`, pois o `ctx` da request vive neste escopo). Um client
84
90
  // third-party NUNCA recebe roles/org — mesmo solicitando `scope=roles`.
85
91
  const clientId = ctx?.oidc?.client?.clientId;
86
- const firstParty = config.branding
87
- ? isFirstParty(config.branding, clientId)
88
- : false;
92
+ const firstParty = config.branding ? isFirstParty(config.branding, clientId) : false;
89
93
  return {
90
94
  accountId: user.id,
91
95
  claims: async (_use, _scope) => {
@@ -103,9 +107,9 @@ export class OidcService {
103
107
  : (user.globalRoles ?? []);
104
108
  // Emite claims de org somente quando há uma org ativa na sessão.
105
109
  if (activeOrg) {
106
- base['org_id'] = activeOrg.orgId;
107
- base['org_slug'] = activeOrg.orgSlug;
108
- base['org_role'] = activeOrg.orgRole;
110
+ base.org_id = activeOrg.orgId;
111
+ base.org_slug = activeOrg.orgSlug;
112
+ base.org_role = activeOrg.orgRole;
109
113
  }
110
114
  }
111
115
  return base;
@@ -150,7 +154,9 @@ export class OidcService {
150
154
  else {
151
155
  callback = provider.callback();
152
156
  }
153
- const interactions = createInteractionActions(provider, { verifyCredentials: config.verifyCredentials });
157
+ const interactions = createInteractionActions(provider, {
158
+ verifyCredentials: config.verifyCredentials,
159
+ });
154
160
  // Atribuição atômica no final: um throw antes deste ponto não corrompe o estado.
155
161
  this.#provider = provider;
156
162
  this.#callback = callback;
@@ -210,7 +216,9 @@ export class OidcService {
210
216
  const { newKid, retiredKids, store } = await m.rotate(keep, retire);
211
217
  await this.reloadKeys();
212
218
  const keptKids = store.keys.map((k) => k.kid);
213
- await this.#config.audit?.record({ type: 'keys.rotated', metadata: { newKid, retiredKids, keptKids, retire } }).catch(() => { });
219
+ await this.#config.audit
220
+ ?.record({ type: 'keys.rotated', metadata: { newKid, retiredKids, keptKids, retire } })
221
+ .catch(() => { });
214
222
  return { newKid, retiredKids, keptKids };
215
223
  };
216
224
  // serializa: encadeia após a rotação em voo; cada caller recebe o resultado da SUA rotação
@@ -8,9 +8,7 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
8
8
  };
9
9
  async function defaultLoadLock() {
10
10
  const spec = '@adonisjs/lock/services/main';
11
- return import(__rewriteRelativeImportExtension(spec))
12
- .then((m) => m.default ?? null)
13
- .catch(() => null);
11
+ return import(__rewriteRelativeImportExtension(spec)).then((m) => m.default ?? null).catch(() => null);
14
12
  }
15
13
  export function makeSingleFlightLock(opts) {
16
14
  const load = opts.loadLock ?? defaultLoadLock;
@@ -1,5 +1,5 @@
1
- import type { AuditSink } from '../audit/audit_sink.js';
2
1
  import type { AuthAccount } from '../accounts/account_store.js';
2
+ import type { AuditSink } from '../audit/audit_sink.js';
3
3
  export interface TokenExchangeAccount {
4
4
  id: string;
5
5
  email?: string;
@@ -255,7 +255,9 @@ export async function ensureAuthkitSchema(db, options = {}) {
255
255
  if (await columnExists(conn, def.name, column))
256
256
  continue;
257
257
  await conn.schema.alterTable(def.name, (t) => add(t));
258
- (report.altered[def.name] ??= []).push(column);
258
+ if (!report.altered[def.name])
259
+ report.altered[def.name] = [];
260
+ report.altered[def.name].push(column);
259
261
  }
260
262
  }
261
263
  return report;
@@ -1,18 +1,18 @@
1
- {{{
2
- exports({ to: app.makePath('inertia/components/auth_shell.tsx') })
3
- }}}
4
- import type { ReactNode } from 'react'
1
+ {
2
+ exports({ to: app.makePath('inertia/components/auth_shell.tsx') });
3
+ }
4
+ import type { ReactNode } from 'react';
5
5
 
6
6
  export interface AuthBrand {
7
- appName: string
8
- accent: string
9
- accentSoft?: string
10
- company?: string
11
- tagline?: string
12
- audienceLabel?: string
7
+ appName: string;
8
+ accent: string;
9
+ accentSoft?: string;
10
+ company?: string;
11
+ tagline?: string;
12
+ audienceLabel?: string;
13
13
  }
14
14
 
15
- const COMPANY_FALLBACK = 'Acme'
15
+ const COMPANY_FALLBACK = 'Acme';
16
16
 
17
17
  /**
18
18
  * Layout de duas colunas para as telas de autenticacao do IdP.
@@ -22,14 +22,14 @@ export default function AuthShell({
22
22
  brand,
23
23
  children,
24
24
  }: {
25
- brand?: AuthBrand
26
- children: ReactNode
25
+ brand?: AuthBrand;
26
+ children: ReactNode;
27
27
  }) {
28
- const accent = brand?.accent ?? '#111827'
29
- const accentSoft = brand?.accentSoft ?? accent
30
- const company = brand?.company ?? COMPANY_FALLBACK
31
- const appName = brand?.appName ?? 'Sua conta'
32
- const tagline = brand?.tagline ?? 'Acesso unificado'
28
+ const accent = brand?.accent ?? '#111827';
29
+ const accentSoft = brand?.accentSoft ?? accent;
30
+ const company = brand?.company ?? COMPANY_FALLBACK;
31
+ const appName = brand?.appName ?? 'Sua conta';
32
+ const tagline = brand?.tagline ?? 'Acesso unificado';
33
33
 
34
34
  return (
35
35
  <div className="min-h-screen flex items-center justify-center bg-gray-100 p-4">
@@ -63,5 +63,5 @@ export default function AuthShell({
63
63
  <div className="p-8 md:p-10">{children}</div>
64
64
  </div>
65
65
  </div>
66
- )
66
+ );
67
67
  }
@@ -1,9 +1,9 @@
1
- {{{
2
- exports({ to: app.makePath('inertia/pages/authkit/account/login.tsx') })
3
- }}}
1
+ {
2
+ exports({ to: app.makePath('inertia/pages/authkit/account/login.tsx') });
3
+ }
4
4
  interface Props {
5
- csrfToken: string
6
- error?: string
5
+ csrfToken: string;
6
+ error?: string;
7
7
  }
8
8
 
9
9
  export default function AccountLogin({ csrfToken, error }: Props) {
@@ -29,7 +29,6 @@ export default function AccountLogin({ csrfToken, error }: Props) {
29
29
  name="email"
30
30
  type="email"
31
31
  required
32
- autoFocus
33
32
  className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-gray-900"
34
33
  />
35
34
 
@@ -52,5 +51,5 @@ export default function AccountLogin({ csrfToken, error }: Props) {
52
51
  </button>
53
52
  </form>
54
53
  </div>
55
- )
54
+ );
56
55
  }
@@ -1,14 +1,14 @@
1
- {{{
2
- exports({ to: app.makePath('inertia/pages/authkit/account/mfa.tsx') })
3
- }}}
1
+ {
2
+ exports({ to: app.makePath('inertia/pages/authkit/account/mfa.tsx') });
3
+ }
4
4
  interface Props {
5
- csrfToken: string
6
- enabled: boolean
7
- enrolling?: boolean
8
- secret?: string | null
9
- qrDataUrl?: string | null
10
- error?: string
11
- recoveryCodes?: string[] | null
5
+ csrfToken: string;
6
+ enabled: boolean;
7
+ enrolling?: boolean;
8
+ secret?: string | null;
9
+ qrDataUrl?: string | null;
10
+ error?: string;
11
+ recoveryCodes?: string[] | null;
12
12
  }
13
13
 
14
14
  export default function AccountMfa({
@@ -25,7 +25,9 @@ export default function AccountMfa({
25
25
  <div className="mx-auto max-w-2xl">
26
26
  <div className="flex items-center justify-between py-6">
27
27
  <div>
28
- <div className="text-xs font-semibold uppercase tracking-[0.2em] text-gray-400">Acme</div>
28
+ <div className="text-xs font-semibold uppercase tracking-[0.2em] text-gray-400">
29
+ Acme
30
+ </div>
29
31
  <h1 className="text-xl font-semibold text-gray-900">Verificação em duas etapas</h1>
30
32
  </div>
31
33
  <form method="POST" action="/account/logout">
@@ -82,7 +84,6 @@ export default function AccountMfa({
82
84
  inputMode="numeric"
83
85
  pattern="[0-9]*"
84
86
  maxLength={6}
85
- autoFocus
86
87
  className="w-full rounded-lg border border-gray-300 px-3 py-2 text-center text-lg tracking-[0.4em] outline-none focus:border-gray-900"
87
88
  />
88
89
  <button
@@ -112,8 +113,8 @@ export default function AccountMfa({
112
113
  ) : (
113
114
  <div className="rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
114
115
  <p className="text-sm text-gray-700">
115
- A verificação em duas etapas está desativada. Ative-a para proteger sua conta com um app
116
- autenticador.
116
+ A verificação em duas etapas está desativada. Ative-a para proteger sua conta com um
117
+ app autenticador.
117
118
  </p>
118
119
  <form method="POST" action="/account/mfa/enroll" className="mt-4">
119
120
  <input type="hidden" name="_csrf" value={csrfToken} />
@@ -128,5 +129,5 @@ export default function AccountMfa({
128
129
  )}
129
130
  </div>
130
131
  </div>
131
- )
132
+ );
132
133
  }
@@ -1,18 +1,18 @@
1
- {{{
2
- exports({ to: app.makePath('inertia/pages/authkit/account/tokens.tsx') })
3
- }}}
1
+ {
2
+ exports({ to: app.makePath('inertia/pages/authkit/account/tokens.tsx') });
3
+ }
4
4
  interface TokenRow {
5
- id: string
6
- name: string
7
- scopes: string[]
8
- audience: string | null
9
- lastUsedAt: string | null
10
- createdAt: string
5
+ id: string;
6
+ name: string;
7
+ scopes: string[];
8
+ audience: string | null;
9
+ lastUsedAt: string | null;
10
+ createdAt: string;
11
11
  }
12
12
  interface Props {
13
- csrfToken: string
14
- createdToken: string | null
15
- tokens: TokenRow[]
13
+ csrfToken: string;
14
+ createdToken: string | null;
15
+ tokens: TokenRow[];
16
16
  }
17
17
 
18
18
  export default function AccountTokens({ csrfToken, createdToken, tokens }: Props) {
@@ -21,7 +21,9 @@ export default function AccountTokens({ csrfToken, createdToken, tokens }: Props
21
21
  <div className="mx-auto max-w-2xl">
22
22
  <div className="flex items-center justify-between py-6">
23
23
  <div>
24
- <div className="text-xs font-semibold uppercase tracking-[0.2em] text-gray-400">Acme</div>
24
+ <div className="text-xs font-semibold uppercase tracking-[0.2em] text-gray-400">
25
+ Acme
26
+ </div>
25
27
  <h1 className="text-xl font-semibold text-gray-900">Tokens de acesso</h1>
26
28
  </div>
27
29
  <form method="POST" action="/account/logout">
@@ -54,7 +56,10 @@ export default function AccountTokens({ csrfToken, createdToken, tokens }: Props
54
56
  placeholder="Nome do token (ex.: CI deploy)"
55
57
  className="flex-1 rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-gray-900"
56
58
  />
57
- <button type="submit" className="rounded-lg bg-gray-900 px-4 text-sm font-semibold text-white">
59
+ <button
60
+ type="submit"
61
+ className="rounded-lg bg-gray-900 px-4 text-sm font-semibold text-white"
62
+ >
58
63
  Criar
59
64
  </button>
60
65
  </form>
@@ -64,12 +69,17 @@ export default function AccountTokens({ csrfToken, createdToken, tokens }: Props
64
69
  <p className="p-6 text-sm text-gray-500">Nenhum token ainda.</p>
65
70
  ) : (
66
71
  tokens.map((t) => (
67
- <div key={t.id} className="flex items-center justify-between border-b border-gray-100 p-4 last:border-0">
72
+ <div
73
+ key={t.id}
74
+ className="flex items-center justify-between border-b border-gray-100 p-4 last:border-0"
75
+ >
68
76
  <div>
69
77
  <p className="text-sm font-medium text-gray-900">{t.name}</p>
70
78
  <p className="text-xs text-gray-500">
71
79
  Criado em {new Date(t.createdAt).toLocaleDateString('pt-BR')}
72
- {t.lastUsedAt ? ` · último uso ${new Date(t.lastUsedAt).toLocaleDateString('pt-BR')}` : ' · nunca usado'}
80
+ {t.lastUsedAt
81
+ ? ` · último uso ${new Date(t.lastUsedAt).toLocaleDateString('pt-BR')}`
82
+ : ' · nunca usado'}
73
83
  </p>
74
84
  </div>
75
85
  <form method="POST" action={`/account/tokens/${t.id}/revoke`}>
@@ -84,5 +94,5 @@ export default function AccountTokens({ csrfToken, createdToken, tokens }: Props
84
94
  </div>
85
95
  </div>
86
96
  </div>
87
- )
97
+ );
88
98
  }
@@ -1,7 +1,7 @@
1
- {{{
2
- exports({ to: app.makePath('inertia/pages/authkit/consent.tsx') })
3
- }}}
4
- import AuthShell, { type AuthBrand } from '../../components/auth_shell'
1
+ {
2
+ exports({ to: app.makePath('inertia/pages/authkit/consent.tsx') });
3
+ }
4
+ import AuthShell, { type AuthBrand } from '../../components/auth_shell';
5
5
 
6
6
  export default function AuthkitConsent({
7
7
  uid,
@@ -9,17 +9,17 @@ export default function AuthkitConsent({
9
9
  csrfToken,
10
10
  brand,
11
11
  }: {
12
- uid: string
13
- params: { client_id: string }
14
- csrfToken: string
15
- brand?: AuthBrand
12
+ uid: string;
13
+ params: { client_id: string };
14
+ csrfToken: string;
15
+ brand?: AuthBrand;
16
16
  }) {
17
- const accent = brand?.accent ?? '#111827'
18
- const appName = brand?.appName ?? params.client_id
17
+ const accent = brand?.accent ?? '#111827';
18
+ const appName = brand?.appName ?? params.client_id;
19
19
 
20
20
  return (
21
21
  <AuthShell brand={brand}>
22
- <form method="POST" action={'/auth/interaction/' + uid + '/consent'}>
22
+ <form method="POST" action={`/auth/interaction/${uid}/consent`}>
23
23
  <input type="hidden" name="_csrf" value={csrfToken} />
24
24
  <h1 className="text-xl font-semibold text-gray-900">Autorizar acesso</h1>
25
25
  <p className="mt-2 text-sm text-gray-600">
@@ -35,5 +35,5 @@ export default function AuthkitConsent({
35
35
  </button>
36
36
  </form>
37
37
  </AuthShell>
38
- )
38
+ );
39
39
  }
@@ -1,10 +1,10 @@
1
- {{{
2
- exports({ to: app.makePath('inertia/pages/authkit/forgot.tsx') })
3
- }}}
4
- import AuthShell from '../../components/auth_shell'
1
+ {
2
+ exports({ to: app.makePath('inertia/pages/authkit/forgot.tsx') });
3
+ }
4
+ import AuthShell from '../../components/auth_shell';
5
5
 
6
6
  const inputClass =
7
- 'w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none transition focus:border-transparent focus:ring-2 focus:ring-gray-800'
7
+ 'w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none transition focus:border-transparent focus:ring-2 focus:ring-gray-800';
8
8
 
9
9
  export default function AuthkitForgot({ csrfToken, sent }: { csrfToken: string; sent?: boolean }) {
10
10
  if (sent) {
@@ -15,7 +15,7 @@ export default function AuthkitForgot({ csrfToken, sent }: { csrfToken: string;
15
15
  Se o e-mail existir, enviaremos instruções de redefinição.
16
16
  </p>
17
17
  </AuthShell>
18
- )
18
+ );
19
19
  }
20
20
 
21
21
  return (
@@ -40,5 +40,5 @@ export default function AuthkitForgot({ csrfToken, sent }: { csrfToken: string;
40
40
  </button>
41
41
  </form>
42
42
  </AuthShell>
43
- )
43
+ );
44
44
  }
@@ -1,10 +1,10 @@
1
- {{{
2
- exports({ to: app.makePath('inertia/pages/authkit/login.tsx') })
3
- }}}
4
- import AuthShell, { type AuthBrand } from '../../components/auth_shell'
1
+ {
2
+ exports({ to: app.makePath('inertia/pages/authkit/login.tsx') });
3
+ }
4
+ import AuthShell, { type AuthBrand } from '../../components/auth_shell';
5
5
 
6
6
  const inputClass =
7
- 'w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none transition focus:border-transparent focus:ring-2'
7
+ 'w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none transition focus:border-transparent focus:ring-2';
8
8
 
9
9
  export default function AuthkitLogin({
10
10
  uid,
@@ -15,21 +15,21 @@ export default function AuthkitLogin({
15
15
  error,
16
16
  brand,
17
17
  }: {
18
- uid: string
19
- csrfToken: string
20
- step?: 'identifier' | 'password'
21
- email?: string
22
- account?: { fullName: string | null; globalRoles: string[] } | null
23
- error?: string
24
- brand?: AuthBrand
18
+ uid: string;
19
+ csrfToken: string;
20
+ step?: 'identifier' | 'password';
21
+ email?: string;
22
+ account?: { fullName: string | null; globalRoles: string[] } | null;
23
+ error?: string;
24
+ brand?: AuthBrand;
25
25
  }) {
26
- const accent = brand?.accent ?? '#111827'
27
- const focusStyle = { ['--tw-ring-color' as any]: accent }
26
+ const accent = brand?.accent ?? '#111827';
27
+ const focusStyle = { ['--tw-ring-color' as any]: accent };
28
28
 
29
29
  if (step === 'identifier') {
30
30
  return (
31
31
  <AuthShell brand={brand}>
32
- <form method="POST" action={'/auth/interaction/' + uid + '/identifier'}>
32
+ <form method="POST" action={`/auth/interaction/${uid}/identifier`}>
33
33
  <input type="hidden" name="_csrf" value={csrfToken} />
34
34
  <h1 className="text-xl font-semibold text-gray-900">Entrar</h1>
35
35
  <p className="mt-1 text-sm text-gray-500">Informe seu e-mail para continuar.</p>
@@ -45,7 +45,6 @@ export default function AuthkitLogin({
45
45
  name="email"
46
46
  type="email"
47
47
  required
48
- autoFocus
49
48
  className={inputClass}
50
49
  style={focusStyle}
51
50
  />
@@ -60,7 +59,7 @@ export default function AuthkitLogin({
60
59
  </button>
61
60
 
62
61
  <div className="mt-4 flex justify-between text-sm text-gray-600">
63
- <a href={'/auth/interaction/' + uid + '/signup'} className="hover:underline">
62
+ <a href={`/auth/interaction/${uid}/signup`} className="hover:underline">
64
63
  Criar conta
65
64
  </a>
66
65
  <a href="/auth/forgot-password" className="hover:underline">
@@ -76,7 +75,7 @@ export default function AuthkitLogin({
76
75
  </div>
77
76
 
78
77
  <a
79
- href={'/auth/google/redirect/' + uid}
78
+ href={`/auth/google/redirect/${uid}`}
80
79
  className="flex w-full items-center justify-center gap-2 rounded-lg border border-gray-300 bg-white py-2.5 text-sm font-medium text-gray-700 transition hover:bg-gray-50"
81
80
  >
82
81
  <svg className="h-4 w-4" viewBox="0 0 24 24" aria-hidden="true">
@@ -100,15 +99,15 @@ export default function AuthkitLogin({
100
99
  Entrar com Google
101
100
  </a>
102
101
  </AuthShell>
103
- )
102
+ );
104
103
  }
105
104
 
106
105
  // step === 'password'
107
- const isAdmin = account?.globalRoles?.includes('ADMIN') ?? false
106
+ const isAdmin = account?.globalRoles?.includes('ADMIN') ?? false;
108
107
 
109
108
  return (
110
109
  <AuthShell brand={brand}>
111
- <form method="POST" action={'/auth/interaction/' + uid + '/login'}>
110
+ <form method="POST" action={`/auth/interaction/${uid}/login`}>
112
111
  <input type="hidden" name="_csrf" value={csrfToken} />
113
112
 
114
113
  {account?.fullName ? (
@@ -127,7 +126,7 @@ export default function AuthkitLogin({
127
126
  <div className="mt-1 flex items-center gap-2">
128
127
  <span className="text-sm text-gray-500">{email}</span>
129
128
  <a
130
- href={'/auth/interaction/' + uid + '/switch'}
129
+ href={`/auth/interaction/${uid}/switch`}
131
130
  className="text-xs font-medium hover:underline"
132
131
  style={{ color: accent }}
133
132
  >
@@ -146,7 +145,6 @@ export default function AuthkitLogin({
146
145
  name="password"
147
146
  type="password"
148
147
  required
149
- autoFocus
150
148
  className={inputClass}
151
149
  style={focusStyle}
152
150
  />
@@ -167,5 +165,5 @@ export default function AuthkitLogin({
167
165
  </div>
168
166
  </form>
169
167
  </AuthShell>
170
- )
168
+ );
171
169
  }