@adonis-agora/authkit-server 0.90.1 → 1.0.0-rc.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 (86) hide show
  1. package/CHANGELOG.md +3637 -0
  2. package/build/commands/clients_create.d.ts +1 -0
  3. package/build/commands/clients_create.js +15 -2
  4. package/build/commands/commands.json +7 -0
  5. package/build/host/ui/admin.html +1 -1
  6. package/build/host/views/account/apps.edge +8 -3
  7. package/build/host/views/partials/styles.edge +2 -2
  8. package/build/index.d.ts +3 -3
  9. package/build/index.js +3 -5
  10. package/build/src/accounts/lucid_account_store.d.ts +0 -18
  11. package/build/src/accounts/lucid_account_store.js +7 -48
  12. package/build/src/accounts/lucid_store/mfa.js +1 -1
  13. package/build/src/accounts/lucid_store/organizations.d.ts +1 -1
  14. package/build/src/accounts/lucid_store/password_hygiene.d.ts +2 -3
  15. package/build/src/accounts/lucid_store/password_hygiene.js +2 -3
  16. package/build/src/accounts/lucid_store/shared.d.ts +0 -7
  17. package/build/src/accounts/lucid_store/shared.js +0 -14
  18. package/build/src/accounts/lucid_stores.d.ts +1 -1
  19. package/build/src/adapters/redis_adapter.js +12 -2
  20. package/build/src/define_config.d.ts +6 -12
  21. package/build/src/define_config.js +3 -2
  22. package/build/src/doctor/checks.d.ts +3 -4
  23. package/build/src/doctor/checks.js +5 -7
  24. package/build/src/host/account_api/account_api_controller.d.ts +2 -1
  25. package/build/src/host/account_api/account_api_controller.js +5 -11
  26. package/build/src/host/admin_api/api_clients_controller.d.ts +9 -3
  27. package/build/src/host/admin_api/dto.d.ts +3 -1
  28. package/build/src/host/admin_api/dto.js +3 -1
  29. package/build/src/host/admin_clients_service.d.ts +13 -5
  30. package/build/src/host/admin_clients_service.js +18 -5
  31. package/build/src/host/admin_console/admin_shell_controller.d.ts +16 -1
  32. package/build/src/host/admin_console/admin_shell_controller.js +21 -7
  33. package/build/src/host/admin_console/console_clients_controller.d.ts +21 -2
  34. package/build/src/host/admin_console/console_clients_controller.js +9 -0
  35. package/build/src/host/admin_console/console_impersonation_controller.js +3 -8
  36. package/build/src/host/admin_console/console_overview_controller.d.ts +2 -2
  37. package/build/src/host/admin_console/console_overview_controller.js +4 -2
  38. package/build/src/host/admin_validators.d.ts +15 -11
  39. package/build/src/host/admin_validators.js +16 -9
  40. package/build/src/host/client_names.d.ts +9 -2
  41. package/build/src/host/client_names.js +11 -3
  42. package/build/src/host/consent_params.d.ts +7 -0
  43. package/build/src/host/consent_params.js +27 -0
  44. package/build/src/host/controllers/account_apps_controller.js +4 -3
  45. package/build/src/host/controllers/account_confirm_controller.d.ts +0 -7
  46. package/build/src/host/controllers/account_confirm_controller.js +0 -6
  47. package/build/src/host/controllers/account_orgs_controller.d.ts +0 -50
  48. package/build/src/host/controllers/account_orgs_controller.js +7 -113
  49. package/build/src/host/controllers/account_security_controller.js +7 -11
  50. package/build/src/host/controllers/agent_consent_controller.js +23 -5
  51. package/build/src/host/controllers/interaction_controller.js +6 -2
  52. package/build/src/host/controllers/poppy_consent_controller.js +33 -7
  53. package/build/src/host/controllers/registration_controller.js +7 -2
  54. package/build/src/host/i18n.d.ts +0 -4
  55. package/build/src/host/i18n.js +0 -6
  56. package/build/src/host/impersonation.d.ts +7 -15
  57. package/build/src/host/impersonation.js +7 -16
  58. package/build/src/host/interaction_recovery.js +4 -1
  59. package/build/src/host/login_attempt.js +5 -1
  60. package/build/src/host/middleware/account_auth.js +6 -0
  61. package/build/src/host/rate_limit.js +5 -1
  62. package/build/src/host/register_auth_host.d.ts +6 -0
  63. package/build/src/host/register_auth_host.js +32 -8
  64. package/build/src/host/renderers/inertia_renderer.d.ts +9 -21
  65. package/build/src/host/renderers/inertia_renderer.js +5 -6
  66. package/build/src/host/screen_props.d.ts +309 -0
  67. package/build/src/host/screen_props.js +11 -0
  68. package/build/src/host/sudo_mode.d.ts +5 -11
  69. package/build/src/host/sudo_mode.js +5 -11
  70. package/build/src/host/ui-dist/assets/index-D0EIeyKK.js +67 -0
  71. package/build/src/host/ui-dist/index.html +1 -1
  72. package/build/src/password/password_manager.js +5 -0
  73. package/build/src/provider/build_provider.js +0 -34
  74. package/build/src/provider/oidc_service.d.ts +0 -2
  75. package/build/src/provider/oidc_service.js +1 -12
  76. package/build/src/provider/token_exchange.d.ts +1 -3
  77. package/build/src/provider/token_exchange.js +1 -1
  78. package/build/stubs/config/authkit_react.stub +3 -3
  79. package/package.json +18 -17
  80. package/skills/authkit-interactions/SKILL.md +32 -5
  81. package/stubs/config/authkit_react.stub +3 -3
  82. package/build/src/commands/import_clients.d.ts +0 -40
  83. package/build/src/commands/import_clients.js +0 -81
  84. package/build/src/host/ui-dist/assets/index-C180q-la.js +0 -67
  85. package/build/src/mixins/with_mfa.d.ts +0 -35
  86. package/build/src/mixins/with_mfa.js +0 -21
@@ -24,6 +24,7 @@ export default class AuthkitClientsCreate extends BaseCommand {
24
24
  public?: boolean;
25
25
  native?: boolean;
26
26
  backchannelLogoutUri?: string;
27
+ clientSecret?: string;
27
28
  json?: boolean;
28
29
  run(): Promise<void>;
29
30
  }
@@ -58,10 +58,15 @@ export default class AuthkitClientsCreate extends BaseCommand {
58
58
  const isPublic = !!this.public || !!this.native;
59
59
  const applicationType = this.native ? 'native' : 'web';
60
60
  const tokenEndpointAuthMethod = isPublic ? 'none' : 'client_secret_basic';
61
+ if (isPublic && this.clientSecret) {
62
+ this.logger.logError('❌ --client-secret não se aplica a um client público (--public/--native).');
63
+ this.exitCode = 1;
64
+ return;
65
+ }
61
66
  const { ClientMetadataError } = await import('../src/host/client_metadata.js');
62
67
  let created;
63
68
  try {
64
- created = await svc.create({
69
+ const input = {
65
70
  clientId: this.clientId,
66
71
  applicationType,
67
72
  redirectUris,
@@ -69,7 +74,10 @@ export default class AuthkitClientsCreate extends BaseCommand {
69
74
  grantTypes,
70
75
  tokenEndpointAuthMethod,
71
76
  backchannelLogoutUri: this.backchannelLogoutUri,
72
- });
77
+ };
78
+ created = this.clientSecret
79
+ ? await svc.createWithSecret(input, this.clientSecret)
80
+ : await svc.create(input);
73
81
  }
74
82
  catch (err) {
75
83
  if (err instanceof ClientMetadataError) {
@@ -142,6 +150,11 @@ __decorate([
142
150
  description: 'Endpoint de OIDC Back-Channel Logout do RP (POST de logout_token).',
143
151
  })
144
152
  ], AuthkitClientsCreate.prototype, "backchannelLogoutUri", void 0);
153
+ __decorate([
154
+ flags.string({
155
+ description: 'Usa este client_secret em vez de gerar um (client confidencial). Útil para manter o secret que os RPs já usam.',
156
+ })
157
+ ], AuthkitClientsCreate.prototype, "clientSecret", void 0);
145
158
  __decorate([
146
159
  flags.boolean({
147
160
  description: 'Output em JSON machine-readable (inclui clientId e clientSecret).',
@@ -123,6 +123,13 @@
123
123
  "type": "string",
124
124
  "description": "Endpoint de OIDC Back-Channel Logout do RP."
125
125
  },
126
+ {
127
+ "name": "clientSecret",
128
+ "flagName": "client-secret",
129
+ "required": false,
130
+ "type": "string",
131
+ "description": "Usa este client_secret em vez de gerar um (client confidencial)."
132
+ },
126
133
  {
127
134
  "name": "json",
128
135
  "flagName": "json",
@@ -17,7 +17,7 @@
17
17
  <!--
18
18
  AuthKit Admin Console — React SPA shell.
19
19
  Mission B will replace this placeholder with the full bundled React SPA.
20
- The server injects window.__AUTHKIT__ before this comment (see admin_shell_controller.ts).
20
+ The server embeds the #authkit-admin-config JSON data block before this comment (see admin_shell_controller.ts).
21
21
  -->
22
22
  <div id="app">
23
23
  <div class="loading">
@@ -40,9 +40,14 @@
40
40
  @else
41
41
  @each(app in apps)
42
42
  <div class="flex items-center justify-between border-b border-gray-100 p-4 last:border-0">
43
- <div>
44
- <p class="text-sm font-medium text-gray-900">{{ app.name }}</p>
45
- <p class="text-xs text-gray-500">{{ t('account.apps.tokens', { accessTokens: app.accessTokens, refreshTokens: app.refreshTokens }) }}</p>
43
+ <div class="flex items-center gap-3">
44
+ @if(app.logoUrl)
45
+ <img src="{{ app.logoUrl }}" alt="" class="h-8 w-8 rounded-md object-contain">
46
+ @end
47
+ <div>
48
+ <p class="text-sm font-medium text-gray-900">{{ app.name }}</p>
49
+ <p class="text-xs text-gray-500">{{ t('account.apps.tokens', { accessTokens: app.accessTokens, refreshTokens: app.refreshTokens }) }}</p>
50
+ </div>
46
51
  </div>
47
52
  <form method="POST" action="{{ accountPaths?.apps ?? '/account/apps' }}/{{ app.clientId }}/revoke"
48
53
  onsubmit="return confirm('{{ t('account.apps.revoke_confirm') }}')">
@@ -1,3 +1,3 @@
1
1
  {{-- Gerado por scripts/build_host_css.mjs — não editar à mão. --}}
2
- <style>/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
3
- @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-300:oklch(80.8% .114 19.571);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-red-900:oklch(39.6% .141 25.723);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-100:oklch(96.2% .059 95.617);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-800:oklch(47.3% .137 46.201);--color-green-50:oklch(98.2% .018 155.826);--color-green-700:oklch(52.7% .154 150.069);--color-emerald-50:oklch(97.9% .021 166.113);--color-emerald-100:oklch(95% .052 163.051);--color-emerald-200:oklch(90.5% .093 164.15);--color-emerald-300:oklch(84.5% .143 164.978);--color-emerald-600:oklch(59.6% .145 163.225);--color-emerald-700:oklch(50.8% .118 165.612);--color-emerald-800:oklch(43.2% .095 166.913);--color-emerald-900:oklch(37.8% .077 168.94);--color-blue-50:oklch(97% .014 254.604);--color-blue-200:oklch(88.2% .059 254.128);--color-blue-300:oklch(80.9% .105 251.813);--color-blue-800:oklch(42.4% .199 265.638);--color-blue-900:oklch(37.9% .146 265.522);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-md:28rem;--container-2xl:42rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-medium:500;--font-weight-semibold:600;--tracking-wide:.025em;--tracking-widest:.1em;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}button:not(:disabled),[role=button]:not(:disabled){cursor:pointer}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.absolute{position:absolute}.relative{position:relative}.top-1\/2{top:50%}.right-2{right:calc(var(--spacing) * 2)}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-4{margin-block:calc(var(--spacing) * 4)}.my-6{margin-block:calc(var(--spacing) * 6)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mb-1{margin-bottom:var(--spacing)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.h-4{height:calc(var(--spacing) * 4)}.h-6{height:calc(var(--spacing) * 6)}.h-12{height:calc(var(--spacing) * 12)}.h-16{height:calc(var(--spacing) * 16)}.h-48{height:calc(var(--spacing) * 48)}.h-px{height:1px}.min-h-screen{min-height:100vh}.w-4{width:calc(var(--spacing) * 4)}.w-6{width:calc(var(--spacing) * 6)}.w-12{width:calc(var(--spacing) * 12)}.w-16{width:calc(var(--spacing) * 16)}.w-48{width:calc(var(--spacing) * 48)}.w-fit{width:fit-content}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.flex-1{flex:1}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.cursor-pointer{cursor:pointer}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-gray-100>:not(:last-child)){border-color:var(--color-gray-100)}.overflow-hidden{overflow:hidden}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-blue-300{border-color:var(--color-blue-300)}.border-emerald-300{border-color:var(--color-emerald-300)}.border-gray-100{border-color:var(--color-gray-100)}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.border-gray-900{border-color:var(--color-gray-900)}.border-red-200{border-color:var(--color-red-200)}.border-red-300{border-color:var(--color-red-300)}.bg-amber-50{background-color:var(--color-amber-50)}.bg-amber-100{background-color:var(--color-amber-100)}.bg-blue-50{background-color:var(--color-blue-50)}.bg-emerald-50{background-color:var(--color-emerald-50)}.bg-emerald-100{background-color:var(--color-emerald-100)}.bg-gray-50{background-color:var(--color-gray-50)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-200{background-color:var(--color-gray-200)}.bg-gray-800{background-color:var(--color-gray-800)}.bg-gray-900{background-color:var(--color-gray-900)}.bg-green-50{background-color:var(--color-green-50)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-red-600{background-color:var(--color-red-600)}.bg-white{background-color:var(--color-white)}.object-cover{object-fit:cover}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-6{padding-block:calc(var(--spacing) * 6)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[11px\]{font-size:11px}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[0\.4em\]{--tw-tracking:.4em;letter-spacing:.4em}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.break-all{word-break:break-all}.text-amber-600{color:var(--color-amber-600)}.text-amber-800{color:var(--color-amber-800)}.text-blue-800{color:var(--color-blue-800)}.text-blue-900{color:var(--color-blue-900)}.text-emerald-600{color:var(--color-emerald-600)}.text-emerald-700{color:var(--color-emerald-700)}.text-emerald-800{color:var(--color-emerald-800)}.text-emerald-900{color:var(--color-emerald-900)}.text-gray-400{color:var(--color-gray-400)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-gray-800{color:var(--color-gray-800)}.text-gray-900{color:var(--color-gray-900)}.text-green-700{color:var(--color-green-700)}.text-red-600{color:var(--color-red-600)}.text-red-700{color:var(--color-red-700)}.text-red-800{color:var(--color-red-800)}.text-red-900{color:var(--color-red-900)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.underline{text-decoration-line:underline}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xs{--tw-shadow:0 1px 2px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-amber-200{--tw-ring-color:var(--color-amber-200)}.ring-black\/5{--tw-ring-color:#0000000d}@supports (color:color-mix(in lab, red, red)){.ring-black\/5{--tw-ring-color:color-mix(in oklab, var(--color-black) 5%, transparent)}}.ring-blue-200{--tw-ring-color:var(--color-blue-200)}.ring-emerald-200{--tw-ring-color:var(--color-emerald-200)}.ring-gray-900{--tw-ring-color:var(--color-gray-900)}.ring-red-200{--tw-ring-color:var(--color-red-200)}.outline-hidden{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.outline-hidden{outline-offset:2px;outline:2px solid #0000}}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.select-none{-webkit-user-select:none;user-select:none}.last\:border-0:last-child{border-style:var(--tw-border-style);border-width:0}@media (hover:hover){.hover\:bg-gray-50:hover{background-color:var(--color-gray-50)}.hover\:bg-gray-700:hover{background-color:var(--color-gray-700)}.hover\:bg-red-50:hover{background-color:var(--color-red-50)}.hover\:bg-red-700:hover{background-color:var(--color-red-700)}.hover\:text-gray-600:hover{color:var(--color-gray-600)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-90:hover{opacity:.9}}.focus\:border-gray-900:focus{border-color:var(--color-gray-900)}.focus\:border-red-600:focus{border-color:var(--color-red-600)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-gray-900:focus{--tw-ring-color:var(--color-gray-900)}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}</style>
2
+ <style>/*! tailwindcss v4.3.5 | MIT License | https://tailwindcss.com */
3
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:block))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-300:oklch(80.8% .114 19.571);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-red-900:oklch(39.6% .141 25.723);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-100:oklch(96.2% .059 95.617);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-800:oklch(47.3% .137 46.201);--color-green-50:oklch(98.2% .018 155.826);--color-green-700:oklch(52.7% .154 150.069);--color-emerald-50:oklch(97.9% .021 166.113);--color-emerald-100:oklch(95% .052 163.051);--color-emerald-200:oklch(90.5% .093 164.15);--color-emerald-300:oklch(84.5% .143 164.978);--color-emerald-600:oklch(59.6% .145 163.225);--color-emerald-700:oklch(50.8% .118 165.612);--color-emerald-800:oklch(43.2% .095 166.913);--color-emerald-900:oklch(37.8% .077 168.94);--color-blue-50:oklch(97% .014 254.604);--color-blue-200:oklch(88.2% .059 254.128);--color-blue-300:oklch(80.9% .105 251.813);--color-blue-800:oklch(42.4% .199 265.638);--color-blue-900:oklch(37.9% .146 265.522);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-md:28rem;--container-2xl:42rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-medium:500;--font-weight-semibold:600;--tracking-wide:.025em;--tracking-widest:.1em;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}button:not(:disabled),[role=button]:not(:disabled){cursor:pointer}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.absolute{position:absolute}.relative{position:relative}.top-1\/2{top:50%}.right-2{right:calc(var(--spacing) * 2)}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-4{margin-block:calc(var(--spacing) * 4)}.my-6{margin-block:calc(var(--spacing) * 6)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mb-1{margin-bottom:var(--spacing)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.h-4{height:calc(var(--spacing) * 4)}.h-6{height:calc(var(--spacing) * 6)}.h-8{height:calc(var(--spacing) * 8)}.h-12{height:calc(var(--spacing) * 12)}.h-16{height:calc(var(--spacing) * 16)}.h-48{height:calc(var(--spacing) * 48)}.h-px{height:1px}.min-h-screen{min-height:100vh}.w-4{width:calc(var(--spacing) * 4)}.w-6{width:calc(var(--spacing) * 6)}.w-8{width:calc(var(--spacing) * 8)}.w-12{width:calc(var(--spacing) * 12)}.w-16{width:calc(var(--spacing) * 16)}.w-48{width:calc(var(--spacing) * 48)}.w-fit{width:fit-content}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.flex-1{flex:1}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.cursor-pointer{cursor:pointer}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-gray-100>:not(:last-child)){border-color:var(--color-gray-100)}.overflow-hidden{overflow:hidden}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:2147483647px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-blue-300{border-color:var(--color-blue-300)}.border-emerald-300{border-color:var(--color-emerald-300)}.border-gray-100{border-color:var(--color-gray-100)}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.border-gray-900{border-color:var(--color-gray-900)}.border-red-200{border-color:var(--color-red-200)}.border-red-300{border-color:var(--color-red-300)}.bg-amber-50{background-color:var(--color-amber-50)}.bg-amber-100{background-color:var(--color-amber-100)}.bg-blue-50{background-color:var(--color-blue-50)}.bg-emerald-50{background-color:var(--color-emerald-50)}.bg-emerald-100{background-color:var(--color-emerald-100)}.bg-gray-50{background-color:var(--color-gray-50)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-200{background-color:var(--color-gray-200)}.bg-gray-800{background-color:var(--color-gray-800)}.bg-gray-900{background-color:var(--color-gray-900)}.bg-green-50{background-color:var(--color-green-50)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-red-600{background-color:var(--color-red-600)}.bg-white{background-color:var(--color-white)}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-6{padding-block:calc(var(--spacing) * 6)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[11px\]{font-size:11px}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[0\.4em\]{--tw-tracking:.4em;letter-spacing:.4em}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.break-all{word-break:break-all}.text-amber-600{color:var(--color-amber-600)}.text-amber-800{color:var(--color-amber-800)}.text-blue-800{color:var(--color-blue-800)}.text-blue-900{color:var(--color-blue-900)}.text-emerald-600{color:var(--color-emerald-600)}.text-emerald-700{color:var(--color-emerald-700)}.text-emerald-800{color:var(--color-emerald-800)}.text-emerald-900{color:var(--color-emerald-900)}.text-gray-400{color:var(--color-gray-400)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-gray-800{color:var(--color-gray-800)}.text-gray-900{color:var(--color-gray-900)}.text-green-700{color:var(--color-green-700)}.text-red-600{color:var(--color-red-600)}.text-red-700{color:var(--color-red-700)}.text-red-800{color:var(--color-red-800)}.text-red-900{color:var(--color-red-900)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.underline{text-decoration-line:underline}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xs{--tw-shadow:0 1px 2px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset, ) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-amber-200{--tw-ring-color:var(--color-amber-200)}.ring-black\/5{--tw-ring-color:#0000000d}@supports (color:color-mix(in lab, red, red)){.ring-black\/5{--tw-ring-color:color-mix(in oklab, var(--color-black) 5%, transparent)}}.ring-blue-200{--tw-ring-color:var(--color-blue-200)}.ring-emerald-200{--tw-ring-color:var(--color-emerald-200)}.ring-gray-900{--tw-ring-color:var(--color-gray-900)}.ring-red-200{--tw-ring-color:var(--color-red-200)}.outline-hidden{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.outline-hidden{outline-offset:2px;outline:2px solid #0000}}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.select-none{-webkit-user-select:none;user-select:none}.last\:border-0:last-child{border-style:var(--tw-border-style);border-width:0}@media (hover:hover){.hover\:bg-gray-50:hover{background-color:var(--color-gray-50)}.hover\:bg-gray-700:hover{background-color:var(--color-gray-700)}.hover\:bg-red-50:hover{background-color:var(--color-red-50)}.hover\:bg-red-700:hover{background-color:var(--color-red-700)}.hover\:text-gray-600:hover{color:var(--color-gray-600)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-90:hover{opacity:.9}}.focus\:border-gray-900:focus{border-color:var(--color-gray-900)}.focus\:border-red-600:focus{border-color:var(--color-red-600)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset, ) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-gray-900:focus{--tw-ring-color:var(--color-gray-900)}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}</style>
package/build/index.d.ts CHANGED
@@ -115,18 +115,19 @@ export { createAuthThrottles } from './src/host/rate_limit.js';
115
115
  export type { AccountScreensOptions, AuthHostOptions, AuthHostRouteMap, } from './src/host/register_auth_host.js';
116
116
  export { registerAuthHost } from './src/host/register_auth_host.js';
117
117
  export { edgeRenderer } from './src/host/renderers/edge_renderer.js';
118
- export type { AuthkitScreen, InertiaRendererOptions, } from './src/host/renderers/inertia_renderer.js';
118
+ export type { InertiaRendererOptions } from './src/host/renderers/inertia_renderer.js';
119
119
  export { inertiaRenderer } from './src/host/renderers/inertia_renderer.js';
120
120
  export type { RuntimeSettingsOptions, SettingRow, SettingsCapability, } from './src/host/runtime_settings.js';
121
121
  export { RuntimeSettings, supportsSettings } from './src/host/runtime_settings.js';
122
122
  export type { AuthMethodsCapabilities, AuthMethodsConfigOverride, AuthMethodsSetting, MaintenanceModeSetting, RegistrationSetting, RequireVerifiedEmailSetting, ResolvedAuthMethods, ResolvedMaintenanceMode, SettingKey, } from './src/host/runtime_toggles.js';
123
123
  export { configLockedAuthMethods, resolveEffectiveAuthMethods, resolveEffectiveMaintenanceMode, resolveEffectiveRegistration, resolveEffectiveRequireVerifiedEmail, SETTING_KEYS, } from './src/host/runtime_toggles.js';
124
+ export type { AccountAppsProps, AccountOrg, AccountOrgsProps, AccountTokensProps, AgentConsentRequest, AgentsConsentProps, AgentsDoneProps, AuthkitScreen, AuthkitScreenProps, AuthkitScreenRenderer, ConsentProps, ConsentRequestParams, ConsentScope, ForgotProps, LoginAccount, LoginProps, MaintenanceProps, MfaChallengeCustomMethod, MfaChallengeProps, OtpUnlockProps, ResetProps, ScreenRenderProps, SessionExpiredProps, SignupProps, ThrottledProps, VerifyEmailProps, } from './src/host/screen_props.js';
124
125
  export { enrichSessionsWithContext } from './src/host/session_context.js';
125
126
  export { sudoMethods } from './src/host/sudo/index.js';
126
127
  export { completeSudo, fail as failSudo, LAST_METHOD_SESSION_KEY, sudoContextFrom, } from './src/host/sudo/runtime.js';
127
128
  export type { SudoContext, SudoMethod, SudoMethodDescriptor, SudoRouteHelpers, } from './src/host/sudo/types.js';
128
129
  export type { ResolvedSudoModeSetting, SudoModeSetting, } from './src/host/sudo_mode.js';
129
- export { isSudoActive, isSudoSatisfied, markSudo, requireSudo, resolveEffectiveSudoMode, SUDO_ACCOUNT_SESSION_KEY, SUDO_MODE_DEFAULTS, SUDO_SESSION_KEY, } from './src/host/sudo_mode.js';
130
+ export { isSudoActive, isSudoSatisfied, requireSudo, resolveEffectiveSudoMode, SUDO_ACCOUNT_SESSION_KEY, SUDO_MODE_DEFAULTS, SUDO_SESSION_KEY, } from './src/host/sudo_mode.js';
130
131
  export { barChartSvg } from './src/host/svg_chart.js';
131
132
  export type { ResolvedTrustedDevicesConfig, TrustedDevicePayload, TrustedDevicesConfigInput, } from './src/host/trusted_device.js';
132
133
  export { buildTrustedDevicePayload, isTrustedDeviceValid, resolveTrustedDevices, TRUSTED_DEVICE_COOKIE, } from './src/host/trusted_device.js';
@@ -140,7 +141,6 @@ export { MCP_CLIENT_REDIRECTS, MCP_RESOURCE_SCOPES, type McpOAuthConfigInput, ty
140
141
  export { withAuditLog } from './src/mixins/with_audit_log.js';
141
142
  export { withAuthUser } from './src/mixins/with_auth_user.js';
142
143
  export { withCredentials } from './src/mixins/with_credentials.js';
143
- export { withMfa } from './src/mixins/with_mfa.js';
144
144
  export { withPersonalAccessToken } from './src/mixins/with_personal_access_token.js';
145
145
  export type { ProviderIdentityClass, ProviderIdentityRow, } from './src/mixins/with_provider_identity.js';
146
146
  export { withProviderIdentity } from './src/mixins/with_provider_identity.js';
package/build/index.js CHANGED
@@ -103,9 +103,8 @@ export { enrichSessionsWithContext } from './src/host/session_context.js';
103
103
  //
104
104
  // `completeSudo` é público porque o host PRECISA chamá-lo: o `oidcStepUp` não
105
105
  // registra rotas — quem valida o grant é o callback do host, e é lá que a
106
- // concessão acontece. Sem exportá-lo, o host improvisaria com `markSudo` e
107
- // perderia o audit `sudo.confirmed`, a preferência de método e o redirect para
108
- // o `return_to`.
106
+ // concessão acontece, com o audit `sudo.confirmed`, a preferência de método e o
107
+ // redirect para o `return_to`.
109
108
  export { sudoMethods } from './src/host/sudo/index.js';
110
109
  // Montador do `SudoContext` que `completeSudo`/`failSudo` recebem. Exportado
111
110
  // junto porque sem ele o host teria de montar o contexto na mão (resolver o
@@ -116,7 +115,7 @@ export { completeSudo, fail as failSudo, LAST_METHOD_SESSION_KEY, sudoContextFro
116
115
  export { isSudoActive,
117
116
  // A DECISÃO de sudo sem efeito na resposta — o que o host chama quando quer
118
117
  // recusar em JSON em vez de redirecionar para `/account/confirm`.
119
- isSudoSatisfied, markSudo, requireSudo, resolveEffectiveSudoMode, SUDO_ACCOUNT_SESSION_KEY, SUDO_MODE_DEFAULTS, SUDO_SESSION_KEY, } from './src/host/sudo_mode.js';
118
+ isSudoSatisfied, requireSudo, resolveEffectiveSudoMode, SUDO_ACCOUNT_SESSION_KEY, SUDO_MODE_DEFAULTS, SUDO_SESSION_KEY, } from './src/host/sudo_mode.js';
120
119
  export { barChartSvg } from './src/host/svg_chart.js';
121
120
  export { buildTrustedDevicePayload, isTrustedDeviceValid, resolveTrustedDevices, TRUSTED_DEVICE_COOKIE, } from './src/host/trusted_device.js';
122
121
  export { parseUserAgent } from './src/host/user_agent.js';
@@ -127,7 +126,6 @@ export { MCP_CLIENT_REDIRECTS, MCP_RESOURCE_SCOPES, registeredOAuthResources, re
127
126
  export { withAuditLog } from './src/mixins/with_audit_log.js';
128
127
  export { withAuthUser } from './src/mixins/with_auth_user.js';
129
128
  export { withCredentials } from './src/mixins/with_credentials.js';
130
- export { withMfa } from './src/mixins/with_mfa.js';
131
129
  export { withPersonalAccessToken } from './src/mixins/with_personal_access_token.js';
132
130
  export { withProviderIdentity } from './src/mixins/with_provider_identity.js';
133
131
  export { withWebauthnCredential } from './src/mixins/with_webauthn_credential.js';
@@ -171,21 +171,3 @@ export interface LucidAccountStoreOptions {
171
171
  emailTokens?: EmailTokensConfigInput;
172
172
  }
173
173
  export declare function lucidAccountStore(Model: any, options?: LucidAccountStoreOptions): AccountStore;
174
- /**
175
- * Options estendidas para uso em testes ou quando o DB é injetado diretamente
176
- * para o probe de histórico de senhas. Uso interno/avançado.
177
- */
178
- export interface LucidAccountStoreAsyncOptions extends LucidAccountStoreOptions {
179
- /**
180
- * Instância de DB Lucid. Quando fornecida, o factory faz o probe da tabela
181
- * `auth_password_history` e monta a capability automaticamente.
182
- */
183
- db?: any;
184
- }
185
- /**
186
- * Versão assíncrona do {@link lucidAccountStore} que faz o probe de DB para
187
- * detectar tabelas opcionais (`auth_password_history`) e monta as capabilities
188
- * correspondentes. Use esta versão quando o `db` está disponível no momento do
189
- * boot (ex.: no `register_auth_host.ts` ou no `app/providers/auth_provider.ts`).
190
- */
191
- export declare function lucidAccountStoreAsync(Model: any, options?: LucidAccountStoreAsyncOptions): Promise<AccountStore>;
@@ -6,7 +6,7 @@ import { defaultOrganizationModels } from './lucid_store/organization_models.js'
6
6
  import { buildOrganizations } from './lucid_store/organizations.js';
7
7
  import { buildPasswordExpiration, buildPasswordHistory } from './lucid_store/password_hygiene.js';
8
8
  import { buildProviderIdentity } from './lucid_store/provider_identity.js';
9
- import { defaultWebauthnCeremonies, hasTable, } from './lucid_store/shared.js';
9
+ import { defaultWebauthnCeremonies, } from './lucid_store/shared.js';
10
10
  import { buildDeletion, buildEmailVerificationStatus, buildProfile, buildStatus, hasColumn, } from './lucid_store/status_profile.js';
11
11
  import { buildWebauthn } from './lucid_store/webauthn.js';
12
12
  export function resolveEmailTokens(input) {
@@ -317,52 +317,11 @@ export function lucidAccountStore(Model, options = {}) {
317
317
  */
318
318
  __model: Model,
319
319
  };
320
- // Histórico de senhas: capability-probed via tabela `auth_password_history`.
321
- // A versão síncrona não pode fazer o probe de DB, então a capability fica
322
- // AUSENTE aqui. Use `lucidAccountStoreAsync` para probe automático, OU injete
323
- // o `passwordHistoryDb` nas options para montagem síncrona com DB explícito.
324
- const passwordHistoryDb = options.passwordHistoryDb;
325
- if (passwordHistoryDb) {
326
- Object.assign(store, buildPasswordHistory(ctx, passwordHistoryDb));
327
- }
320
+ // Histórico de senhas: tabela lib-owned `auth_password_history`, pela conexão
321
+ // do model (resolvida a cada uso, como no MFA).
322
+ Object.assign(store, buildPasswordHistory(ctx, {
323
+ query: () => Model.query().client.query(),
324
+ table: (name) => Model.query().client.table(name),
325
+ }));
328
326
  return store;
329
327
  }
330
- /**
331
- * Versão assíncrona do {@link lucidAccountStore} que faz o probe de DB para
332
- * detectar tabelas opcionais (`auth_password_history`) e monta as capabilities
333
- * correspondentes. Use esta versão quando o `db` está disponível no momento do
334
- * boot (ex.: no `register_auth_host.ts` ou no `app/providers/auth_provider.ts`).
335
- */
336
- export async function lucidAccountStoreAsync(Model, options = {}) {
337
- const { db, ...rest } = options;
338
- const base = lucidAccountStore(Model, rest);
339
- if (db) {
340
- // Probe da tabela auth_password_history.
341
- const historyPresent = await hasTable(db, 'auth_password_history');
342
- if (historyPresent) {
343
- const ctx = {
344
- Model,
345
- mfaIssuer: options.mfaIssuer ?? 'AuthKit',
346
- recoveryCodeCount: options.recoveryCodeCount ?? 8,
347
- passwords: new PasswordManager(options.password, {
348
- logger: options.logger,
349
- fetchImpl: options.pwnedFetch,
350
- }),
351
- audit: options.audit,
352
- sealSecret: (s) => s,
353
- openSecret: (s) => s ?? null,
354
- toAccount: (row) => ({
355
- // Ver a nota em `toAccount` acima: `sub` precisa ser string.
356
- id: String(row.id),
357
- email: row.email,
358
- ...(row.phone && row.phoneVerifiedAt ? { phone: row.phone } : {}),
359
- globalRoles: row.globalRoles ?? [],
360
- name: row.fullName ?? undefined,
361
- avatarUrl: row.avatarUrl ?? undefined,
362
- }),
363
- };
364
- Object.assign(base, buildPasswordHistory(ctx, db));
365
- }
366
- }
367
- return base;
368
- }
@@ -6,7 +6,7 @@ import { buildMfaStateRepo, generateRecoveryCode, hashesEqual, sha256, } from '.
6
6
  * O estado de MFA (`totp_secret`, `mfa_enabled_at`, `recovery_codes`,
7
7
  * `last_totp_step`) vive numa tabela PRÓPRIA auto-gerida `auth_mfa` (criada pelo
8
8
  * {@link ensureAuthkitSchema}), keyed por `account_id`. O host NÃO precisa migrar
9
- * nada — `withMfa()` continua componível no model mas não declara mais colunas.
9
+ * nada e o model não declara colunas de MFA.
10
10
  *
11
11
  * Acesso ao banco via a CONEXÃO do próprio model (`Model.query().client`), a mesma
12
12
  * usada pelo restante do store. O `email` (para o keyuri/QR) continua vindo do
@@ -1,7 +1,7 @@
1
1
  import type { OrganizationsCapability } from '../account_store.js';
2
2
  /**
3
3
  * Contexto mínimo para o builder de organizations. Recebe os três models direto
4
- * (já construídos pelo lucidAccountStore após o hasTable probing).
4
+ * (já construídos pelo lucidAccountStore).
5
5
  */
6
6
  export interface OrgStoreContext {
7
7
  OrgModel: any;
@@ -1,9 +1,8 @@
1
1
  import type { PasswordExpirationCapability, PasswordHistoryCapability } from '../account_store.js';
2
2
  import type { LucidStoreContext } from './shared.js';
3
3
  /**
4
- * Histórico de senhas (disallow_password_reuse). Só deve ser montado quando a
5
- * tabela `auth_password_history` está presente (capability-probed via
6
- * {@link hasTable} no `lucidAccountStore`).
4
+ * Histórico de senhas (disallow_password_reuse), sobre a tabela lib-owned
5
+ * `auth_password_history` (criada pelo `ensureAuthkitSchema`).
7
6
  *
8
7
  * Tabela: `auth_password_history`
9
8
  * id UUID/SERIAL PK
@@ -2,9 +2,8 @@ import { DateTime } from 'luxon';
2
2
  /** Stub de noop para quando não há verify disponível (fail-safe: não bloqueia). */
3
3
  const _noopVerify = async (_hash, _plain) => false;
4
4
  /**
5
- * Histórico de senhas (disallow_password_reuse). Só deve ser montado quando a
6
- * tabela `auth_password_history` está presente (capability-probed via
7
- * {@link hasTable} no `lucidAccountStore`).
5
+ * Histórico de senhas (disallow_password_reuse), sobre a tabela lib-owned
6
+ * `auth_password_history` (criada pelo `ensureAuthkitSchema`).
8
7
  *
9
8
  * Tabela: `auth_password_history`
10
9
  * id UUID/SERIAL PK
@@ -84,13 +84,6 @@ export declare const sha256: (value: string) => string;
84
84
  export declare function generateRecoveryCode(): string;
85
85
  /** Comparação de hashes hex resistente a timing. */
86
86
  export declare function hashesEqual(a: string, b: string): boolean;
87
- /**
88
- * Indica se o model Lucid tem sua tabela registrada no adapter do DB (runtime probe).
89
- * Usado para detectar tabelas opcionais como auth_organizations/members/invitations.
90
- * Mais robusto do que checar colunas: detecta a presença da tabela inteira.
91
- * Em testes, as tabelas existem se foram criadas na migration do teste.
92
- */
93
- export declare function hasTable(db: any, tableName: string): Promise<boolean>;
94
87
  /**
95
88
  * Estado de MFA (lido de `auth_mfa`). Forma normalizada usada pela {@link MfaCapability}:
96
89
  * - `mfaEnabledAt` em EPOCH MS (ou null) — o trusted-device compara contra isso.
@@ -37,20 +37,6 @@ export function hashesEqual(a, b) {
37
37
  return false;
38
38
  return timingSafeEqual(ba, bb);
39
39
  }
40
- /**
41
- * Indica se o model Lucid tem sua tabela registrada no adapter do DB (runtime probe).
42
- * Usado para detectar tabelas opcionais como auth_organizations/members/invitations.
43
- * Mais robusto do que checar colunas: detecta a presença da tabela inteira.
44
- * Em testes, as tabelas existem se foram criadas na migration do teste.
45
- */
46
- export async function hasTable(db, tableName) {
47
- try {
48
- return await db.connection().schema.hasTable(tableName);
49
- }
50
- catch {
51
- return false;
52
- }
53
- }
54
40
  /**
55
41
  * Repositório do estado de MFA na tabela LIB-OWNED `auth_mfa`, keyed por
56
42
  * `account_id`. Acessa o banco pela CONEXÃO do próprio model (`Model.query().client`),
@@ -4,7 +4,7 @@ import type { AccountStore } from './account_store.js';
4
4
  import { type LucidAccountStoreOptions } from './lucid_account_store.js';
5
5
  /** Models Lucid (compostos dos mixins do authkit) para montar os stores de uma vez. */
6
6
  export interface LucidStoresModels {
7
- /** Model de `withAuthUser()` (+withCredentials/+withMfa). Obrigatório. */
7
+ /** Model de `withAuthUser()` (+withCredentials). Obrigatório. */
8
8
  account: any;
9
9
  /** Model de `withPersonalAccessToken()` — habilita o patStore. */
10
10
  pat?: any;
@@ -140,13 +140,18 @@ export class RedisAdapter {
140
140
  */
141
141
  async list() {
142
142
  const prefix = `${this.prefix}:${this.name}:`;
143
+ // A connection `keyPrefix` (ioredis) is added to every key a command takes,
144
+ // but NOT to a SCAN `MATCH` pattern, and SCAN returns the stored (prefixed)
145
+ // keys. So match with it and strip it before `MGET`, which adds it again.
146
+ const keyPrefix = this.#connectionKeyPrefix();
143
147
  const result = [];
144
148
  let cursor = '0';
145
149
  do {
146
- const [next, keys] = await this.redis.scan(cursor, 'MATCH', `${prefix}*`, 'COUNT', 1000);
150
+ const [next, stored] = await this.redis.scan(cursor, 'MATCH', `${keyPrefix}${prefix}*`, 'COUNT', 1000);
147
151
  cursor = next;
148
- if (keys.length === 0)
152
+ if (stored.length === 0)
149
153
  continue;
154
+ const keys = stored.map((key) => key.slice(keyPrefix.length));
150
155
  const values = await this.redis.mget(...keys);
151
156
  keys.forEach((key, i) => {
152
157
  const data = values[i];
@@ -161,4 +166,9 @@ export class RedisAdapter {
161
166
  result.sort((a, b) => a.id.localeCompare(b.id));
162
167
  return result;
163
168
  }
169
+ /** The `keyPrefix` of the ioredis connection (or of the AdonisJS connection wrapping it). */
170
+ #connectionKeyPrefix() {
171
+ const client = this.redis;
172
+ return client.options?.keyPrefix ?? client.ioConnection?.options?.keyPrefix ?? '';
173
+ }
164
174
  }
@@ -1,4 +1,4 @@
1
- import type { AccessTokenFormat, AccessTokensConfig, ClientConfig, JwksConfig, KeystoreStoreConfig, ObservabilityConfig, TtlConfig } from '@adonis-agora/authkit-core';
1
+ import type { AccessTokenFormat, AccessTokensConfig, JwksConfig, KeystoreStoreConfig, ObservabilityConfig, TtlConfig } from '@adonis-agora/authkit-core';
2
2
  import type { HttpContext } from '@adonisjs/core/http';
3
3
  import type { AccountStore, AuthAccount } from './accounts/account_store.js';
4
4
  import { type AdapterFactory, adapters, type OidcAdapterClass } from './adapters/factory.js';
@@ -14,6 +14,7 @@ import type { ResolveGeo } from './host/geo.js';
14
14
  import { type AuthMessages, type I18nConfig } from './host/i18n.js';
15
15
  import { type OtpLoginConfigInput, type ResolvedOtpLoginConfig } from './host/otp_login.js';
16
16
  import type { AuthHostOptions } from './host/register_auth_host.js';
17
+ import type { AuthkitScreenRenderer } from './host/screen_props.js';
17
18
  import type { SudoMethod } from './host/sudo/types.js';
18
19
  import { type ResolvedTrustedDevicesConfig, type TrustedDevicesConfigInput } from './host/trusted_device.js';
19
20
  import type { WhatsappCodeSenderBinding } from './host/whatsapp_code_sender.js';
@@ -354,8 +355,8 @@ export declare function resolveNotifications(): ResolvedNotificationsConfig;
354
355
  *
355
356
  * Quando habilitado, o oidc-provider expõe o endpoint de registro (`/reg`) e os
356
357
  * clients criados ali são PERSISTIDOS pelo MESMO adapter usado para os demais
357
- * artefatos OIDC. Assim, clients dinâmicos coexistem com os `clients` estáticos
358
- * da config e ficam disponíveis para uma futura UI admin que gerencia clients no DB.
358
+ * artefatos OIDC, junto dos clients criados pelo console admin, pela Admin API e
359
+ * pelo `node ace authkit:clients:create`.
359
360
  */
360
361
  export interface DynamicRegistrationConfigInput {
361
362
  /** Liga o endpoint de registro dinâmico. Default: false (comportamento atual). */
@@ -872,13 +873,6 @@ export interface AuthServerConfigInput {
872
873
  session?: {
873
874
  adapter?: AdapterFactory;
874
875
  };
875
- /**
876
- * Clientes OIDC pré-carregados no provider ao subir. Útil para testes e
877
- * migrações pontuais. Para uso em produção, gerencie clients via console admin
878
- * ou Admin API (`node ace authkit:clients:create`).
879
- * @internal
880
- */
881
- clients?: ClientConfig[];
882
876
  /**
883
877
  * Config de JWKS, ou `'auto'` (recomendado p/ deploys efêmeros): se a env
884
878
  * `AUTHKIT_JWKS` (JSON `{"keys":[...]}`) estiver presente, usa-a inline
@@ -1256,7 +1250,6 @@ export interface ResolvedServerConfig {
1256
1250
  * `session.adapter` no input é a MESMA classe do `AdapterClass`.
1257
1251
  */
1258
1252
  SessionAdapterClass: OidcAdapterClass;
1259
- clients: ClientConfig[];
1260
1253
  jwks: {
1261
1254
  keys: Record<string, any>[];
1262
1255
  };
@@ -1296,7 +1289,8 @@ export interface ResolvedServerConfig {
1296
1289
  mountPath: string;
1297
1290
  /** Destino default pós-confirmação do console (sudo mode) e demais fallbacks de conta. Default: '/account/security'. */
1298
1291
  accountHome?: string;
1299
- render?: AuthHostRenderer;
1292
+ /** O renderer das telas, tipado por view (ver `AuthkitScreenProps`). */
1293
+ render?: AuthkitScreenRenderer;
1300
1294
  /** Recuperação de sessão de interaction perdida resolvida (default `{ mode: 'screen' }`). */
1301
1295
  interactionRecovery: ResolvedInteractionRecoveryConfig;
1302
1296
  /** Branding resolvido: sempre presente (default neutro quando o host não declara `branding`). */
@@ -387,7 +387,6 @@ export function defineConfig(config) {
387
387
  issuer: config.issuer,
388
388
  AdapterClass,
389
389
  SessionAdapterClass,
390
- clients: config.clients ?? [],
391
390
  jwks: jwks,
392
391
  jwksConfig,
393
392
  ttl: {
@@ -419,7 +418,9 @@ export function defineConfig(config) {
419
418
  // importa `edge.js` eagerly, então é seguro construir mesmo em hosts
420
419
  // sem o peer opcional instalado (só quebraria se a request realmente
421
420
  // chegasse sem Edge configurado, exatamente como hoje).
422
- render: config.render ?? edgeRenderer(),
421
+ // O renderer do host recebe `(ctx, view, props)`; aqui ele é tipado por view para que
422
+ // os controllers não possam divergir de `AuthkitScreenProps`.
423
+ render: (config.render ?? edgeRenderer()),
423
424
  // Recuperação de sessão de interaction perdida: default gracioso 'screen'
424
425
  // (tela themeável `session-expired`); 'redirect' opt-in para 302 ao login.
425
426
  interactionRecovery: {
@@ -41,13 +41,12 @@ export declare function checkConfigResolves(input: DoctorInput): Finding;
41
41
  /** issuer é uma URL válida e seu pathname casa com o mountPath. */
42
42
  export declare function checkIssuer(input: DoctorInput): Finding[];
43
43
  /**
44
- * Clients: verifica que não há clients estáticos no config (clients são 100%
45
- * runtime via console admin / Admin API).
44
+ * Clients: vivem no adapter, gerenciados pelo console admin / Admin API /
45
+ * `node ace authkit:clients:create`.
46
46
  */
47
47
  export declare function checkClients(input: DoctorInput): Finding;
48
48
  /**
49
- * Aviso de adapter volátil. Como clients são 100% runtime (sem config estático),
50
- * um adapter volátil (Redis sem persistência, in-memory) pode perder os clients
49
+ * Aviso de adapter volátil. Como os clients vivem no adapter, um adapter volátil (Redis sem persistência, in-memory) pode perder os clients
51
50
  * num restart sem aviso prévio.
52
51
  *
53
52
  * Detecção de volatilidade: o `AdapterClass` é inspecionado estruturalmente:
@@ -48,22 +48,20 @@ export function checkIssuer(input) {
48
48
  return findings;
49
49
  }
50
50
  /**
51
- * Clients: verifica que não há clients estáticos no config (clients são 100%
52
- * runtime via console admin / Admin API).
51
+ * Clients: vivem no adapter, gerenciados pelo console admin / Admin API /
52
+ * `node ace authkit:clients:create`.
53
53
  */
54
54
  export function checkClients(input) {
55
55
  const cfg = input.authkitConfig;
56
56
  if (!cfg)
57
57
  return { level: 'error', message: 'no config to validate clients.' };
58
- // clients sempre é [] no config resolvido — info apenas.
59
58
  return {
60
59
  level: 'ok',
61
60
  message: 'clients are managed at runtime via admin console / Admin API — use `node ace authkit:clients:create` to add clients.',
62
61
  };
63
62
  }
64
63
  /**
65
- * Aviso de adapter volátil. Como clients são 100% runtime (sem config estático),
66
- * um adapter volátil (Redis sem persistência, in-memory) pode perder os clients
64
+ * Aviso de adapter volátil. Como os clients vivem no adapter, um adapter volátil (Redis sem persistência, in-memory) pode perder os clients
67
65
  * num restart sem aviso prévio.
68
66
  *
69
67
  * Detecção de volatilidade: o `AdapterClass` é inspecionado estruturalmente:
@@ -98,9 +96,9 @@ export function checkAdapterVolatility(input) {
98
96
  // Adapter desconhecido ou volátil (Redis, memory, custom) — warn informativo.
99
97
  return {
100
98
  level: 'warn',
101
- message: 'No static clients in config: if the OIDC adapter is volatile (Redis without AOF/RDB, in-memory, etc.), ' +
99
+ message: 'Clients live in the OIDC adapter: if it is volatile (Redis without AOF/RDB, in-memory, etc.), ' +
102
100
  'clients managed via console/API will be lost on restart. ' +
103
- 'Use a persistent adapter (DatabaseAdapter) or keep static clients as a fallback.',
101
+ 'Use a persistent adapter (DatabaseAdapter).',
104
102
  };
105
103
  }
106
104
  /** accountStore presente + quais capacidades implementa. */