@dudousxd/adonis-authkit-server 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +137 -0
  3. package/build/assets/grafana/authkit-dashboard.json +118 -0
  4. package/build/commands/commands.json +30 -0
  5. package/build/commands/configure.d.ts +2 -0
  6. package/build/commands/configure.js +42 -0
  7. package/build/commands/eject.d.ts +11 -0
  8. package/build/commands/eject.js +96 -0
  9. package/build/commands/main.d.ts +12 -0
  10. package/build/commands/main.js +38 -0
  11. package/build/commands/ui_preset.d.ts +4 -0
  12. package/build/commands/ui_preset.js +32 -0
  13. package/build/database/migrations/make_authkit_oidc_table.d.ts +6 -0
  14. package/build/database/migrations/make_authkit_oidc_table.js +19 -0
  15. package/build/host/views/account/login.edge +29 -0
  16. package/build/host/views/account/mfa.edge +151 -0
  17. package/build/host/views/account/tokens.edge +70 -0
  18. package/build/host/views/admin/audit.edge +72 -0
  19. package/build/host/views/admin/clients.edge +51 -0
  20. package/build/host/views/admin/dashboard.edge +58 -0
  21. package/build/host/views/admin/users.edge +76 -0
  22. package/build/host/views/consent.edge +19 -0
  23. package/build/host/views/forgot.edge +30 -0
  24. package/build/host/views/login.edge +91 -0
  25. package/build/host/views/mfa-challenge.edge +88 -0
  26. package/build/host/views/reset.edge +29 -0
  27. package/build/host/views/signup.edge +44 -0
  28. package/build/host/views/verify-email.edge +16 -0
  29. package/build/index.d.ts +42 -0
  30. package/build/index.js +28 -0
  31. package/build/providers/authkit_server_provider.d.ts +19 -0
  32. package/build/providers/authkit_server_provider.js +81 -0
  33. package/build/src/accounts/account_store.d.ts +136 -0
  34. package/build/src/accounts/account_store.js +1 -0
  35. package/build/src/accounts/lucid_account_store.d.ts +75 -0
  36. package/build/src/accounts/lucid_account_store.js +396 -0
  37. package/build/src/adapters/adapter_contract.d.ts +18 -0
  38. package/build/src/adapters/adapter_contract.js +1 -0
  39. package/build/src/adapters/database_adapter.d.ts +15 -0
  40. package/build/src/adapters/database_adapter.js +63 -0
  41. package/build/src/adapters/factory.d.ts +30 -0
  42. package/build/src/adapters/factory.js +43 -0
  43. package/build/src/adapters/redis_adapter.d.ts +16 -0
  44. package/build/src/adapters/redis_adapter.js +95 -0
  45. package/build/src/audit/audit_sink.d.ts +54 -0
  46. package/build/src/audit/audit_sink.js +1 -0
  47. package/build/src/audit/lucid_audit_sink.d.ts +10 -0
  48. package/build/src/audit/lucid_audit_sink.js +60 -0
  49. package/build/src/controllers/oidc_callback_controller.d.ts +22 -0
  50. package/build/src/controllers/oidc_callback_controller.js +33 -0
  51. package/build/src/define_config.d.ts +261 -0
  52. package/build/src/define_config.js +115 -0
  53. package/build/src/host/account_lockout.d.ts +86 -0
  54. package/build/src/host/account_lockout.js +185 -0
  55. package/build/src/host/augmentations.d.ts +1 -0
  56. package/build/src/host/augmentations.js +1 -0
  57. package/build/src/host/branding.d.ts +17 -0
  58. package/build/src/host/branding.js +8 -0
  59. package/build/src/host/controllers/account_mfa_controller.d.ts +30 -0
  60. package/build/src/host/controllers/account_mfa_controller.js +157 -0
  61. package/build/src/host/controllers/account_session_controller.d.ts +7 -0
  62. package/build/src/host/controllers/account_session_controller.js +50 -0
  63. package/build/src/host/controllers/account_tokens_controller.d.ts +7 -0
  64. package/build/src/host/controllers/account_tokens_controller.js +55 -0
  65. package/build/src/host/controllers/admin/admin_audit_controller.d.ts +10 -0
  66. package/build/src/host/controllers/admin/admin_audit_controller.js +56 -0
  67. package/build/src/host/controllers/admin/admin_clients_controller.d.ts +10 -0
  68. package/build/src/host/controllers/admin/admin_clients_controller.js +24 -0
  69. package/build/src/host/controllers/admin/admin_dashboard_controller.d.ts +10 -0
  70. package/build/src/host/controllers/admin/admin_dashboard_controller.js +27 -0
  71. package/build/src/host/controllers/admin/admin_users_controller.d.ts +11 -0
  72. package/build/src/host/controllers/admin/admin_users_controller.js +53 -0
  73. package/build/src/host/controllers/interaction_controller.d.ts +44 -0
  74. package/build/src/host/controllers/interaction_controller.js +304 -0
  75. package/build/src/host/controllers/pat_introspection_controller.d.ts +22 -0
  76. package/build/src/host/controllers/pat_introspection_controller.js +46 -0
  77. package/build/src/host/controllers/registration_controller.d.ts +18 -0
  78. package/build/src/host/controllers/registration_controller.js +169 -0
  79. package/build/src/host/controllers/social_controller.d.ts +8 -0
  80. package/build/src/host/controllers/social_controller.js +82 -0
  81. package/build/src/host/default_mailer.d.ts +39 -0
  82. package/build/src/host/default_mailer.js +141 -0
  83. package/build/src/host/email_templates.d.ts +35 -0
  84. package/build/src/host/email_templates.js +66 -0
  85. package/build/src/host/i18n.d.ts +178 -0
  86. package/build/src/host/i18n.js +208 -0
  87. package/build/src/host/middleware/account_auth.d.ts +7 -0
  88. package/build/src/host/middleware/account_auth.js +11 -0
  89. package/build/src/host/rate_limit.d.ts +32 -0
  90. package/build/src/host/rate_limit.js +87 -0
  91. package/build/src/host/register_auth_host.d.ts +41 -0
  92. package/build/src/host/register_auth_host.js +133 -0
  93. package/build/src/host/renderers/edge_renderer.d.ts +3 -0
  94. package/build/src/host/renderers/edge_renderer.js +29 -0
  95. package/build/src/host/renderers/inertia_renderer.d.ts +5 -0
  96. package/build/src/host/renderers/inertia_renderer.js +26 -0
  97. package/build/src/host/validators.d.ts +39 -0
  98. package/build/src/host/validators.js +13 -0
  99. package/build/src/keys/jwks_manager.d.ts +6 -0
  100. package/build/src/keys/jwks_manager.js +11 -0
  101. package/build/src/mixins/with_audit_log.d.ts +19 -0
  102. package/build/src/mixins/with_audit_log.js +41 -0
  103. package/build/src/mixins/with_auth_user.d.ts +18 -0
  104. package/build/src/mixins/with_auth_user.js +39 -0
  105. package/build/src/mixins/with_credentials.d.ts +20 -0
  106. package/build/src/mixins/with_credentials.js +29 -0
  107. package/build/src/mixins/with_mfa.d.ts +31 -0
  108. package/build/src/mixins/with_mfa.js +39 -0
  109. package/build/src/mixins/with_personal_access_token.d.ts +19 -0
  110. package/build/src/mixins/with_personal_access_token.js +44 -0
  111. package/build/src/mixins/with_provider_identity.d.ts +20 -0
  112. package/build/src/mixins/with_provider_identity.js +32 -0
  113. package/build/src/mixins/with_webauthn_credential.d.ts +37 -0
  114. package/build/src/mixins/with_webauthn_credential.js +49 -0
  115. package/build/src/observability/metrics_controller.d.ts +5 -0
  116. package/build/src/observability/metrics_controller.js +24 -0
  117. package/build/src/observability/metrics_service.d.ts +2 -0
  118. package/build/src/observability/metrics_service.js +7 -0
  119. package/build/src/observability/otel_recorder.d.ts +10 -0
  120. package/build/src/observability/otel_recorder.js +59 -0
  121. package/build/src/observability/wire_provider_events.d.ts +12 -0
  122. package/build/src/observability/wire_provider_events.js +19 -0
  123. package/build/src/pat/lucid_pat_store.d.ts +6 -0
  124. package/build/src/pat/lucid_pat_store.js +62 -0
  125. package/build/src/pat/pat_store.d.ts +31 -0
  126. package/build/src/pat/pat_store.js +1 -0
  127. package/build/src/pat/pat_tokens.d.ts +4 -0
  128. package/build/src/pat/pat_tokens.js +9 -0
  129. package/build/src/provider/build_provider.d.ts +8 -0
  130. package/build/src/provider/build_provider.js +101 -0
  131. package/build/src/provider/interaction_actions.d.ts +21 -0
  132. package/build/src/provider/interaction_actions.js +32 -0
  133. package/build/src/provider/oidc_service.d.ts +17 -0
  134. package/build/src/provider/oidc_service.js +84 -0
  135. package/build/src/provider/token_exchange.d.ts +15 -0
  136. package/build/src/provider/token_exchange.js +72 -0
  137. package/build/src/register_routes.d.ts +16 -0
  138. package/build/src/register_routes.js +21 -0
  139. package/build/stubs/config/authkit.stub +29 -0
  140. package/build/stubs/main.d.ts +1 -0
  141. package/build/stubs/main.js +2 -0
  142. package/build/stubs/models/auth_user.stub +13 -0
  143. package/build/stubs/ui/edge/views/consent.edge +13 -0
  144. package/build/stubs/ui/edge/views/login.edge +19 -0
  145. package/build/stubs/ui/react/components/auth_shell.tsx +67 -0
  146. package/build/stubs/ui/react/pages/account/login.tsx +56 -0
  147. package/build/stubs/ui/react/pages/account/mfa.tsx +132 -0
  148. package/build/stubs/ui/react/pages/account/tokens.tsx +88 -0
  149. package/build/stubs/ui/react/pages/consent.tsx +39 -0
  150. package/build/stubs/ui/react/pages/forgot.tsx +44 -0
  151. package/build/stubs/ui/react/pages/login.tsx +171 -0
  152. package/build/stubs/ui/react/pages/mfa-challenge.tsx +72 -0
  153. package/build/stubs/ui/react/pages/reset.tsx +58 -0
  154. package/build/stubs/ui/react/pages/signup.tsx +78 -0
  155. package/build/stubs/ui/react/pages/verify-email.tsx +24 -0
  156. package/build/types.d.ts +7 -0
  157. package/build/types.js +1 -0
  158. package/package.json +108 -0
  159. package/stubs/config/authkit.stub +29 -0
  160. package/stubs/main.ts +2 -0
  161. package/stubs/models/auth_user.stub +13 -0
  162. package/stubs/ui/edge/views/consent.edge +13 -0
  163. package/stubs/ui/edge/views/login.edge +19 -0
  164. package/stubs/ui/react/components/auth_shell.tsx +67 -0
  165. package/stubs/ui/react/pages/account/login.tsx +56 -0
  166. package/stubs/ui/react/pages/account/mfa.tsx +132 -0
  167. package/stubs/ui/react/pages/account/tokens.tsx +88 -0
  168. package/stubs/ui/react/pages/consent.tsx +39 -0
  169. package/stubs/ui/react/pages/forgot.tsx +44 -0
  170. package/stubs/ui/react/pages/login.tsx +171 -0
  171. package/stubs/ui/react/pages/mfa-challenge.tsx +72 -0
  172. package/stubs/ui/react/pages/reset.tsx +58 -0
  173. package/stubs/ui/react/pages/signup.tsx +78 -0
  174. package/stubs/ui/react/pages/verify-email.tsx +24 -0
@@ -0,0 +1,88 @@
1
+ <!doctype html>
2
+ <html lang="pt-br"><head><meta charset="utf-8"><title>{{ t('mfa_challenge.page_title') }} — {{ brand && brand.appName ? brand.appName : t('common.app_fallback') }}</title>
3
+ <script src="https://cdn.tailwindcss.com"></script></head>
4
+ <body class="min-h-screen flex items-center justify-center bg-gray-50">
5
+ <div class="w-full max-w-sm bg-white p-8 rounded-2xl shadow-xl ring-1 ring-black/5">
6
+ <form method="POST" action="/auth/interaction/{{ uid }}/mfa">
7
+ <input type="hidden" name="_csrf" value="{{ csrfToken }}">
8
+ <h1 class="text-xl font-semibold text-gray-900">{{ t('mfa_challenge.title') }}</h1>
9
+ <p class="mt-1 text-sm text-gray-500">
10
+ {{ t('mfa_challenge.intro') }}
11
+ </p>
12
+
13
+ @if(error)
14
+ <p class="mt-4 text-sm text-red-600">{{ error }}</p>
15
+ @end
16
+
17
+ <div class="mt-6">
18
+ <label for="code" class="mb-1 block text-sm font-medium text-gray-700">{{ t('mfa_challenge.code_label') }}</label>
19
+ <input id="code" name="code" inputmode="numeric" autocomplete="one-time-code"
20
+ pattern="[0-9]*" maxlength="6" autofocus
21
+ class="w-full rounded-lg border border-gray-300 px-3 py-2 text-center text-lg tracking-[0.4em] outline-none transition focus:border-gray-900 focus:ring-2 focus:ring-gray-900" />
22
+ </div>
23
+
24
+ <button type="submit"
25
+ class="mt-6 w-full rounded-lg bg-gray-900 py-2.5 text-sm font-semibold text-white transition hover:opacity-90">
26
+ {{ t('mfa_challenge.submit') }}
27
+ </button>
28
+ </form>
29
+
30
+ @if(passkeyAvailable)
31
+ {{-- Passkey como alternativa ao código TOTP. O form é submetido por página
32
+ inteira (não fetch) para que o 303 de volta ao client navegue o browser. --}}
33
+ <form id="passkey-form" method="POST" action="/auth/interaction/{{ uid }}/passkey/verify" class="mt-4">
34
+ <input type="hidden" name="_csrf" value="{{ csrfToken }}">
35
+ <input type="hidden" name="response" id="passkey-response">
36
+ </form>
37
+ <button type="button" id="passkey-button"
38
+ class="mt-4 w-full rounded-lg border border-gray-300 py-2.5 text-sm font-semibold text-gray-700 transition hover:bg-gray-50">
39
+ {{ t('mfa_challenge.passkey_button') }}
40
+ </button>
41
+ <p id="passkey-error" class="mt-3 hidden text-sm text-red-600">{{ t('mfa_challenge.passkey_error') }}</p>
42
+ @end
43
+
44
+ <details class="mt-6 text-sm text-gray-600">
45
+ <summary class="cursor-pointer hover:underline">{{ t('mfa_challenge.recovery_summary') }}</summary>
46
+ <form method="POST" action="/auth/interaction/{{ uid }}/mfa" class="mt-3">
47
+ <input type="hidden" name="_csrf" value="{{ csrfToken }}">
48
+ <input name="recoveryCode" placeholder="xxxxx-xxxxx"
49
+ class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-gray-900" />
50
+ <button type="submit"
51
+ class="mt-3 w-full rounded-lg border border-gray-300 py-2.5 text-sm font-semibold text-gray-700 transition hover:bg-gray-50">
52
+ {{ t('mfa_challenge.recovery_submit') }}
53
+ </button>
54
+ </form>
55
+ </details>
56
+ </div>
57
+
58
+ @if(passkeyAvailable)
59
+ <script type="module">
60
+ import { startAuthentication } from 'https://cdn.jsdelivr.net/npm/@simplewebauthn/browser@13/dist/bundle/index.js'
61
+ const btn = document.getElementById('passkey-button')
62
+ const errEl = document.getElementById('passkey-error')
63
+ const csrf = document.querySelector('#passkey-form input[name="_csrf"]').value
64
+ btn?.addEventListener('click', async () => {
65
+ errEl.classList.add('hidden')
66
+ btn.disabled = true
67
+ try {
68
+ // 1) Busca as opções de autenticação (challenge guardado na sessão server-side).
69
+ const res = await fetch('/auth/interaction/{{ uid }}/passkey/options', {
70
+ method: 'POST',
71
+ headers: { 'content-type': 'application/json', 'x-csrf-token': csrf },
72
+ body: JSON.stringify({}),
73
+ })
74
+ if (!res.ok) throw new Error('options')
75
+ const optionsJSON = await res.json()
76
+ // 2) Cerimônia no authenticator.
77
+ const assertion = await startAuthentication({ optionsJSON })
78
+ // 3) Submete a assertion como POST de página inteira (segue o 303).
79
+ document.getElementById('passkey-response').value = JSON.stringify(assertion)
80
+ document.getElementById('passkey-form').submit()
81
+ } catch (e) {
82
+ errEl.classList.remove('hidden')
83
+ btn.disabled = false
84
+ }
85
+ })
86
+ </script>
87
+ @end
88
+ </body></html>
@@ -0,0 +1,29 @@
1
+ <!doctype html>
2
+ <html lang="pt-br"><head><meta charset="utf-8"><title>{{ t('reset.page_title') }}</title>
3
+ <script src="https://cdn.tailwindcss.com"></script></head>
4
+ <body class="min-h-screen flex items-center justify-center bg-gray-50">
5
+ <div class="w-full max-w-sm bg-white p-8 rounded-2xl shadow-xl ring-1 ring-black/5">
6
+ @if(done)
7
+ <h1 class="text-xl font-semibold text-gray-900">{{ t('reset.done_title') }}</h1>
8
+ <p class="mt-2 text-sm text-gray-600">{{ t('reset.done_body') }}</p>
9
+ @else
10
+ <form method="POST" action="/auth/reset-password">
11
+ <input type="hidden" name="_csrf" value="{{ csrfToken }}">
12
+ <input type="hidden" name="token" value="{{ token }}">
13
+ <h1 class="text-xl font-semibold text-gray-900">{{ t('reset.title') }}</h1>
14
+ <p class="mt-1 text-sm text-gray-500">{{ t('reset.intro') }}</p>
15
+
16
+ <div class="mt-6">
17
+ <label for="password" class="mb-1 block text-sm font-medium text-gray-700">{{ t('reset.password_label') }}</label>
18
+ <input id="password" name="password" type="password" required minlength="8"
19
+ class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none transition focus:border-gray-900 focus:ring-2 focus:ring-gray-900" />
20
+ </div>
21
+
22
+ <button type="submit"
23
+ class="mt-6 w-full rounded-lg bg-gray-900 py-2.5 text-sm font-semibold text-white transition hover:opacity-90">
24
+ {{ t('reset.submit') }}
25
+ </button>
26
+ </form>
27
+ @end
28
+ </div>
29
+ </body></html>
@@ -0,0 +1,44 @@
1
+ <!doctype html>
2
+ <html lang="pt-br"><head><meta charset="utf-8"><title>{{ t('signup.page_title') }} — {{ brand && brand.appName ? brand.appName : t('common.app_fallback') }}</title>
3
+ <script src="https://cdn.tailwindcss.com"></script></head>
4
+ <body class="min-h-screen flex items-center justify-center bg-gray-50">
5
+ <div class="w-full max-w-sm bg-white p-8 rounded-2xl shadow-xl ring-1 ring-black/5">
6
+ <form method="POST" action="/auth/interaction/{{ uid }}/signup">
7
+ <input type="hidden" name="_csrf" value="{{ csrfToken }}">
8
+ <h1 class="text-xl font-semibold text-gray-900">{{ t('signup.title') }}</h1>
9
+ <p class="mt-1 text-sm text-gray-500">{{ t('signup.intro') }}</p>
10
+
11
+ @if(error)
12
+ <p class="mt-4 text-sm text-red-600">{{ error }}</p>
13
+ @end
14
+
15
+ <div class="mt-6">
16
+ <label for="fullName" class="mb-1 block text-sm font-medium text-gray-700">{{ t('signup.name_label') }}</label>
17
+ <input id="fullName" name="fullName" type="text" required
18
+ class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none transition focus:border-gray-900 focus:ring-2 focus:ring-gray-900" />
19
+ </div>
20
+
21
+ <div class="mt-4">
22
+ <label for="email" class="mb-1 block text-sm font-medium text-gray-700">{{ t('signup.email_label') }}</label>
23
+ <input id="email" name="email" type="email" required
24
+ class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none transition focus:border-gray-900 focus:ring-2 focus:ring-gray-900" />
25
+ </div>
26
+
27
+ <div class="mt-4">
28
+ <label for="password" class="mb-1 block text-sm font-medium text-gray-700">{{ t('signup.password_label') }}</label>
29
+ <input id="password" name="password" type="password" required minlength="8"
30
+ class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none transition focus:border-gray-900 focus:ring-2 focus:ring-gray-900" />
31
+ </div>
32
+
33
+ <button type="submit"
34
+ class="mt-6 w-full rounded-lg bg-gray-900 py-2.5 text-sm font-semibold text-white transition hover:opacity-90">
35
+ {{ t('signup.submit') }}
36
+ </button>
37
+
38
+ <a href="/auth/interaction/{{ uid }}"
39
+ class="mt-4 block text-center text-sm text-gray-600 hover:underline">
40
+ {{ t('signup.have_account') }}
41
+ </a>
42
+ </form>
43
+ </div>
44
+ </body></html>
@@ -0,0 +1,16 @@
1
+ <!doctype html>
2
+ <html lang="pt-br"><head><meta charset="utf-8"><title>{{ t('verify_email.page_title') }}</title>
3
+ <script src="https://cdn.tailwindcss.com"></script></head>
4
+ <body class="min-h-screen flex items-center justify-center bg-gray-50">
5
+ <div class="w-full max-w-sm bg-white p-8 rounded-2xl shadow-xl ring-1 ring-black/5">
6
+ @if(verified)
7
+ <h1 class="text-xl font-semibold text-gray-900">{{ t('verify_email.verified_title') }}</h1>
8
+ <p class="mt-2 text-sm text-gray-600">{{ t('verify_email.verified_body') }}</p>
9
+ @else
10
+ <h1 class="text-xl font-semibold text-gray-900">{{ t('verify_email.invalid_title') }}</h1>
11
+ <p class="mt-2 text-sm text-gray-600">
12
+ {{ t('verify_email.invalid_body') }}
13
+ </p>
14
+ @end
15
+ </div>
16
+ </body></html>
@@ -0,0 +1,42 @@
1
+ export { defineConfig, adapters, toSeconds } from './src/define_config.js';
2
+ export { generatePatToken, hashPatToken } from './src/pat/pat_tokens.js';
3
+ export { withAuthUser } from './src/mixins/with_auth_user.js';
4
+ export { withCredentials } from './src/mixins/with_credentials.js';
5
+ export { withMfa } from './src/mixins/with_mfa.js';
6
+ export { OidcService } from './src/provider/oidc_service.js';
7
+ export { registerOidcRoutes } from './src/register_routes.js';
8
+ export type { AuthServerConfigInput, ResolvedServerConfig, DynamicRegistrationConfigInput, ResolvedDynamicRegistrationConfig, AdminConfigInput, ResolvedAdminConfig, } from './src/define_config.js';
9
+ export { resolveAdmin, resolveWebauthn } from './src/define_config.js';
10
+ export type { WebauthnConfigInput, ResolvedWebauthnConfig } from './src/define_config.js';
11
+ export { lucidAccountStore } from './src/accounts/lucid_account_store.js';
12
+ export type { LucidAccountStoreOptions, AccountSecretEncrypter, } from './src/accounts/lucid_account_store.js';
13
+ export type { AccountStore, AuthAccount, CreateAccountInput, LinkProviderIdentityInput, ListAccountsParams, Paginated, PasskeySummary, } from './src/accounts/account_store.js';
14
+ export { withProviderIdentity } from './src/mixins/with_provider_identity.js';
15
+ export type { ProviderIdentityRow, ProviderIdentityClass, } from './src/mixins/with_provider_identity.js';
16
+ export { withWebauthnCredential } from './src/mixins/with_webauthn_credential.js';
17
+ export type { WebauthnCredentialRow, WebauthnCredentialClass, } from './src/mixins/with_webauthn_credential.js';
18
+ export { lucidPatStore } from './src/pat/lucid_pat_store.js';
19
+ export type { PatStore, PatRecord, IssuePatInput } from './src/pat/pat_store.js';
20
+ export { withPersonalAccessToken } from './src/mixins/with_personal_access_token.js';
21
+ export { lucidAuditSink } from './src/audit/lucid_audit_sink.js';
22
+ export type { AuditSink, AuditEvent, AuditEventType, StoredAuditEvent, ListAuditParams, AuditPage, } from './src/audit/audit_sink.js';
23
+ export { withAuditLog } from './src/mixins/with_audit_log.js';
24
+ export { inertiaRenderer } from './src/host/renderers/inertia_renderer.js';
25
+ export { edgeRenderer } from './src/host/renderers/edge_renderer.js';
26
+ export { brandFor, isFirstParty } from './src/host/branding.js';
27
+ export type { BrandingConfig, ClientBrand } from './src/host/branding.js';
28
+ export { resolveMessages, translate, DEFAULT_MESSAGES, DEFAULT_LOCALE } from './src/host/i18n.js';
29
+ export type { I18nConfig, AuthMessages } from './src/host/i18n.js';
30
+ export type { AuthHostRenderer, AuthSocialConfig } from './src/define_config.js';
31
+ export { registerAuthHost } from './src/host/register_auth_host.js';
32
+ export type { AuthHostOptions } from './src/host/register_auth_host.js';
33
+ export { resolveRateLimit } from './src/define_config.js';
34
+ export type { RateLimitConfigInput, RateLimitBucket, ResolvedRateLimitConfig, } from './src/define_config.js';
35
+ export { createAuthThrottles } from './src/host/rate_limit.js';
36
+ export type { AuthThrottles, ThrottleMiddleware } from './src/host/rate_limit.js';
37
+ /**
38
+ * Configure hook + stubsRoot resolvidos pelo `node ace configure @dudousxd/adonis-authkit-server`.
39
+ * O comando do AdonisJS importa o entrypoint principal e procura por estes exports.
40
+ */
41
+ export { configure } from './commands/configure.js';
42
+ export { stubsRoot } from './stubs/main.js';
package/build/index.js ADDED
@@ -0,0 +1,28 @@
1
+ export { defineConfig, adapters, toSeconds } from './src/define_config.js';
2
+ export { generatePatToken, hashPatToken } from './src/pat/pat_tokens.js';
3
+ export { withAuthUser } from './src/mixins/with_auth_user.js';
4
+ export { withCredentials } from './src/mixins/with_credentials.js';
5
+ export { withMfa } from './src/mixins/with_mfa.js';
6
+ export { OidcService } from './src/provider/oidc_service.js';
7
+ export { registerOidcRoutes } from './src/register_routes.js';
8
+ export { resolveAdmin, resolveWebauthn } from './src/define_config.js';
9
+ export { lucidAccountStore } from './src/accounts/lucid_account_store.js';
10
+ export { withProviderIdentity } from './src/mixins/with_provider_identity.js';
11
+ export { withWebauthnCredential } from './src/mixins/with_webauthn_credential.js';
12
+ export { lucidPatStore } from './src/pat/lucid_pat_store.js';
13
+ export { withPersonalAccessToken } from './src/mixins/with_personal_access_token.js';
14
+ export { lucidAuditSink } from './src/audit/lucid_audit_sink.js';
15
+ export { withAuditLog } from './src/mixins/with_audit_log.js';
16
+ export { inertiaRenderer } from './src/host/renderers/inertia_renderer.js';
17
+ export { edgeRenderer } from './src/host/renderers/edge_renderer.js';
18
+ export { brandFor, isFirstParty } from './src/host/branding.js';
19
+ export { resolveMessages, translate, DEFAULT_MESSAGES, DEFAULT_LOCALE } from './src/host/i18n.js';
20
+ export { registerAuthHost } from './src/host/register_auth_host.js';
21
+ export { resolveRateLimit } from './src/define_config.js';
22
+ export { createAuthThrottles } from './src/host/rate_limit.js';
23
+ /**
24
+ * Configure hook + stubsRoot resolvidos pelo `node ace configure @dudousxd/adonis-authkit-server`.
25
+ * O comando do AdonisJS importa o entrypoint principal e procura por estes exports.
26
+ */
27
+ export { configure } from './commands/configure.js';
28
+ export { stubsRoot } from './stubs/main.js';
@@ -0,0 +1,19 @@
1
+ import type { ApplicationService } from '@adonisjs/core/types';
2
+ import type { MetricsRecorder } from '@dudousxd/adonis-authkit-core';
3
+ import { OidcService } from '../src/provider/oidc_service.js';
4
+ import type { AccountStore } from '../src/accounts/account_store.js';
5
+ import type { PatStore } from '../src/pat/pat_store.js';
6
+ declare module '@adonisjs/core/types' {
7
+ interface ContainerBindings {
8
+ 'authkit.server': OidcService;
9
+ 'authkit.metrics': MetricsRecorder;
10
+ 'authkit.accountStore': AccountStore;
11
+ 'authkit.patStore': PatStore;
12
+ }
13
+ }
14
+ export default class AuthkitServerProvider {
15
+ protected app: ApplicationService;
16
+ constructor(app: ApplicationService);
17
+ boot(): Promise<void>;
18
+ register(): void;
19
+ }
@@ -0,0 +1,81 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { configProvider } from '@adonisjs/core';
4
+ import { RuntimeException } from '@adonisjs/core/exceptions';
5
+ import { OidcService } from '../src/provider/oidc_service.js';
6
+ export default class AuthkitServerProvider {
7
+ app;
8
+ constructor(app) {
9
+ this.app = app;
10
+ }
11
+ async boot() {
12
+ // Registra o disco "authkit" no edge.js para que os templates sejam referenciados
13
+ // como `authkit::login`, `authkit::account/tokens`, etc.
14
+ // Resolve o diretório das views tanto em produção (provider compilado em
15
+ // `build/providers/`, views copiadas para `build/host/views`) quanto em dev
16
+ // (rodando de `providers/` via ts-exec; views em `build/host/views` após build
17
+ // ou em `src/host/views` sem build).
18
+ const candidates = [
19
+ new URL('../host/views', import.meta.url), // prod: build/providers → build/host/views
20
+ new URL('../build/host/views', import.meta.url), // dev: providers → build/host/views
21
+ new URL('../src/host/views', import.meta.url), // dev sem build: providers → src/host/views
22
+ ];
23
+ const viewsUrl = candidates.find((u) => existsSync(fileURLToPath(u)));
24
+ if (!viewsUrl)
25
+ return; // nenhum dir de views resolvível
26
+ try {
27
+ const edge = await import('edge.js');
28
+ const edgeInstance = edge.default ?? edge;
29
+ edgeInstance.mount('authkit', viewsUrl);
30
+ }
31
+ catch {
32
+ // edge.js ausente (host headless/Inertia-only que não usa edgeRenderer) — ignora.
33
+ }
34
+ }
35
+ register() {
36
+ this.app.container.singleton('authkit.server', async () => {
37
+ const configProviderValue = this.app.config.get('authkit');
38
+ const config = (await configProvider.resolve(this.app, configProviderValue));
39
+ if (!config) {
40
+ throw new RuntimeException('Config inválido em "config/authkit.ts". Use o método defineConfig de @dudousxd/adonis-authkit-server.');
41
+ }
42
+ // `app.appKey` pode ser uma string crua ou um `Secret` do AdonisJS (config/app.ts
43
+ // expõe `export const appKey = new Secret(env.get('APP_KEY'))`). O oidc-provider
44
+ // assina cookies via keygrip e exige uma string — então liberamos o Secret aqui.
45
+ // Sem isso, a chave chegaria como objeto/undefined e o fluxo de authorize quebraria
46
+ // (keygrip: "key argument must be of type string ...").
47
+ const rawAppKey = this.app.config.get('app.appKey');
48
+ const appKey = rawAppKey && typeof rawAppKey.release === 'function'
49
+ ? rawAppKey.release()
50
+ : rawAppKey;
51
+ if (!appKey || typeof appKey !== 'string') {
52
+ throw new RuntimeException('APP_KEY ausente: defina `export const appKey = new Secret(env.get(\'APP_KEY\'))` em config/app.ts. ' +
53
+ 'O @dudousxd/adonis-authkit-server precisa dele para assinar os cookies do oidc-provider.');
54
+ }
55
+ const metrics = await this.app.container.make('authkit.metrics');
56
+ return new OidcService(config, appKey, metrics);
57
+ });
58
+ this.app.container.singleton('authkit.metrics', async () => {
59
+ const value = this.app.config.get('authkit');
60
+ const config = (await configProvider.resolve(this.app, value));
61
+ const { createMetricsRecorder } = await import('../src/observability/metrics_service.js');
62
+ return createMetricsRecorder(config?.observability ?? {}, 'authkit-server');
63
+ });
64
+ this.app.container.singleton('authkit.accountStore', async () => {
65
+ const value = this.app.config.get('authkit');
66
+ const config = (await configProvider.resolve(this.app, value));
67
+ if (!config?.accountStore) {
68
+ throw new RuntimeException('accountStore não configurado em "config/authkit.ts". Use defineConfig de @dudousxd/adonis-authkit-server.');
69
+ }
70
+ return config.accountStore;
71
+ });
72
+ this.app.container.singleton('authkit.patStore', async () => {
73
+ const value = this.app.config.get('authkit');
74
+ const config = (await configProvider.resolve(this.app, value));
75
+ if (!config?.patStore) {
76
+ throw new RuntimeException('patStore não configurado em "config/authkit.ts" — necessário para fluxos de PAT.');
77
+ }
78
+ return config.patStore;
79
+ });
80
+ }
81
+ }
@@ -0,0 +1,136 @@
1
+ /** DTO público da conta — o que o provider e os controllers enxergam. Nunca um model Lucid. */
2
+ export interface AuthAccount {
3
+ id: string;
4
+ email: string;
5
+ globalRoles?: string[];
6
+ name?: string;
7
+ avatarUrl?: string;
8
+ }
9
+ export interface CreateAccountInput {
10
+ email: string;
11
+ password: string;
12
+ fullName?: string | null;
13
+ globalRoles?: string[];
14
+ /** social/Google entra como true (provider já verificou o email). Default: false. */
15
+ emailVerified?: boolean;
16
+ }
17
+ /** Dados para ligar uma identidade de provider a uma conta. */
18
+ export interface LinkProviderIdentityInput {
19
+ accountId: string;
20
+ provider: string;
21
+ providerUserId: string;
22
+ email?: string;
23
+ }
24
+ /** Parâmetros de listagem paginada de contas (console admin). */
25
+ export interface ListAccountsParams {
26
+ /** Filtro por e-mail (substring, case-insensitive). */
27
+ search?: string;
28
+ /** Página (1-based). Default: 1. */
29
+ page?: number;
30
+ /** Itens por página. Default: 20. */
31
+ limit?: number;
32
+ }
33
+ /** Página de resultados + total absoluto (para paginação na UI). */
34
+ export interface Paginated<T> {
35
+ data: T[];
36
+ total: number;
37
+ }
38
+ /**
39
+ * Resumo de uma passkey (credencial WebAuthn) para exibição na UI de gerência.
40
+ * Nunca expõe a chave pública nem o counter.
41
+ */
42
+ export interface PasskeySummary {
43
+ /** Credential id (base64url). */
44
+ id: string;
45
+ /** Rótulo legível opcional (ex.: nome do dispositivo). */
46
+ label?: string;
47
+ /** ISO timestamp de criação. */
48
+ createdAt: string;
49
+ }
50
+ export interface AccountStore {
51
+ findById(id: string): Promise<AuthAccount | null>;
52
+ verifyCredentials(email: string, password: string): Promise<AuthAccount | null>;
53
+ findByEmail(email: string): Promise<AuthAccount | null>;
54
+ create(input: CreateAccountInput): Promise<AuthAccount>;
55
+ /** Acha a conta ligada a uma identidade de provider; null se desconhecida. */
56
+ findByProviderIdentity(provider: string, providerUserId: string): Promise<AuthAccount | null>;
57
+ /** Liga (upsert idempotente na chave única) uma identidade de provider a uma conta. */
58
+ linkProviderIdentity(data: LinkProviderIdentityInput): Promise<void>;
59
+ issuePasswordResetToken(email: string): Promise<{
60
+ token: string;
61
+ account: AuthAccount;
62
+ } | null>;
63
+ consumePasswordResetToken(token: string, newPassword: string): Promise<boolean>;
64
+ issueEmailVerificationToken(email: string): Promise<{
65
+ token: string;
66
+ account: AuthAccount;
67
+ } | null>;
68
+ consumeEmailVerificationToken(token: string): Promise<boolean>;
69
+ /** Lista contas paginadas, opcionalmente filtrando por e-mail. */
70
+ listAccounts(params: ListAccountsParams): Promise<Paginated<AuthAccount>>;
71
+ /** Substitui as roles globais de uma conta. */
72
+ setGlobalRoles(accountId: string, roles: string[]): Promise<void>;
73
+ /** Estado do MFA da conta (se o desafio TOTP deve ser exigido no login). */
74
+ getMfaState?(accountId: string): Promise<{
75
+ enabled: boolean;
76
+ }>;
77
+ /**
78
+ * Inicia o enrollment TOTP: gera um segredo PENDENTE (mfaEnabledAt continua
79
+ * null) e devolve o segredo + otpauth URI (keyuri). Não ativa o MFA ainda.
80
+ */
81
+ startTotpEnrollment?(accountId: string): Promise<{
82
+ secret: string;
83
+ otpauthUri: string;
84
+ } | null>;
85
+ /**
86
+ * Confirma o enrollment: verifica o código contra o segredo pendente; em caso
87
+ * de sucesso ativa o MFA, gera N recovery codes e devolve os códigos em claro
88
+ * (uma única vez).
89
+ */
90
+ confirmTotpEnrollment?(accountId: string, code: string): Promise<{
91
+ ok: boolean;
92
+ recoveryCodes?: string[];
93
+ }>;
94
+ /** Verifica um código TOTP contra o segredo ativo. */
95
+ verifyTotp?(accountId: string, code: string): Promise<boolean>;
96
+ /** Consome (single-use) um recovery code; true se casou e foi removido. */
97
+ consumeRecoveryCode?(accountId: string, code: string): Promise<boolean>;
98
+ /** Desliga o MFA: limpa segredo + mfaEnabledAt + recovery codes. */
99
+ disableMfa?(accountId: string): Promise<void>;
100
+ /**
101
+ * Inicia o registro de uma passkey: gera as opções de criação
102
+ * (`generateRegistrationOptions`) escopadas à conta (e excluindo credenciais já
103
+ * registradas). Devolve as opções JSON (o controller serializa pro browser) e o
104
+ * `challenge` (base64url) para guardar na sessão. null = conta inexistente.
105
+ */
106
+ generatePasskeyRegistrationOptions?(accountId: string): Promise<{
107
+ options: Record<string, unknown>;
108
+ challenge: string;
109
+ } | null>;
110
+ /**
111
+ * Finaliza o registro: verifica a resposta do browser
112
+ * (`verifyRegistrationResponse`) contra o `expectedChallenge` guardado. Em caso
113
+ * de sucesso persiste a credencial (id, publicKey, counter, transports) e
114
+ * habilita o MFA. Retorna true se registrou.
115
+ */
116
+ verifyPasskeyRegistration?(accountId: string, response: unknown, expectedChallenge: string): Promise<boolean>;
117
+ /**
118
+ * Inicia a autenticação por passkey no login: gera as opções
119
+ * (`generateAuthenticationOptions`) restritas às credenciais da conta. Devolve
120
+ * as opções JSON + o `challenge` para guardar na sessão. null = conta sem passkeys.
121
+ */
122
+ generatePasskeyAuthenticationOptions?(accountId: string): Promise<{
123
+ options: Record<string, unknown>;
124
+ challenge: string;
125
+ } | null>;
126
+ /**
127
+ * Verifica a resposta de autenticação por passkey
128
+ * (`verifyAuthenticationResponse`) contra o `expectedChallenge` guardado. Em
129
+ * caso de sucesso atualiza o signature counter armazenado. Retorna true se válido.
130
+ */
131
+ verifyPasskeyAuthentication?(accountId: string, response: unknown, expectedChallenge: string): Promise<boolean>;
132
+ /** Lista as passkeys da conta (sem expor chave pública / counter). */
133
+ listPasskeys?(accountId: string): Promise<PasskeySummary[]>;
134
+ /** Remove uma passkey (por credential id) da conta. */
135
+ removePasskey?(accountId: string, credentialId: string): Promise<void>;
136
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,75 @@
1
+ import { generateAuthenticationOptions, generateRegistrationOptions, verifyAuthenticationResponse, verifyRegistrationResponse } from '@simplewebauthn/server';
2
+ import type { AccountStore } from './account_store.js';
3
+ /**
4
+ * Encripta/decripta um valor (ex.: o segredo TOTP) em repouso. Mantém a lib
5
+ * desacoplada do serviço de encryption do app — qualquer implementação que
6
+ * faça round-trip serve (em prod, normalmente o `@adonisjs/core/services/encryption`).
7
+ * `decrypt` retorna `null` se o valor foi adulterado/é inválido.
8
+ */
9
+ export interface AccountSecretEncrypter {
10
+ encrypt(value: string): string;
11
+ decrypt(value: string): string | null;
12
+ }
13
+ /** Opções do {@link lucidAccountStore}. */
14
+ export interface LucidAccountStoreOptions {
15
+ /** Label de issuer mostrado no app autenticador (keyuri). Default: 'AuthKit'. */
16
+ mfaIssuer?: string;
17
+ /** Quantidade de recovery codes gerados no enrollment. Default: 8. */
18
+ recoveryCodeCount?: number;
19
+ /**
20
+ * Quando fornecido, o segredo TOTP é encriptado antes de persistir e
21
+ * decriptado na leitura. Ausente (ex.: testes) → segredo em claro.
22
+ */
23
+ encrypter?: AccountSecretEncrypter;
24
+ /**
25
+ * Model Lucid das identidades de provider (composto de `withProviderIdentity()`),
26
+ * usado por `findByProviderIdentity`/`linkProviderIdentity` (account linking
27
+ * social). Ausente → esses dois métodos lançam (hosts email-only continuam
28
+ * funcionando até optarem por habilitar).
29
+ */
30
+ providerIdentityModel?: any;
31
+ /**
32
+ * Model Lucid das credenciais WebAuthn / passkeys (composto de
33
+ * `withWebauthnCredential()`), usado pelos métodos `*Passkey*`. Ausente → esses
34
+ * métodos viram `undefined` na interface (hosts sem passkeys não mudam de
35
+ * comportamento; a UI esconde a seção de passkeys).
36
+ */
37
+ webauthnCredentialModel?: any;
38
+ /**
39
+ * Parâmetros do Relying Party (RP) usados nas cerimônias WebAuthn. Resolvidos a
40
+ * partir do `issuer` no `define_config` quando omitidos. Necessários sempre que
41
+ * `webauthnCredentialModel` é fornecido.
42
+ */
43
+ webauthn?: {
44
+ /** Nome do RP mostrado pelo authenticator. Default: `mfaIssuer`. */
45
+ rpName: string;
46
+ /** RP ID — normalmente o hostname (sem porta) do issuer. */
47
+ rpId: string;
48
+ /** Origin(s) esperada(s) na verificação (scheme://host[:port]). */
49
+ origin: string | string[];
50
+ };
51
+ /**
52
+ * Seam de injeção das cerimônias WebAuthn (generate/verify). Default: as funções
53
+ * reais do `@simplewebauthn/server`. Existe para testes (mockar a verificação SEM
54
+ * um authenticator real) — produção não precisa fornecer.
55
+ */
56
+ webauthnCeremonies?: Partial<WebauthnCeremonies>;
57
+ }
58
+ /**
59
+ * Funções das cerimônias WebAuthn. Espelham a assinatura do `@simplewebauthn/server`
60
+ * (subconjunto usado). Injetáveis via {@link LucidAccountStoreOptions.webauthnCeremonies}
61
+ * para testes.
62
+ */
63
+ export interface WebauthnCeremonies {
64
+ generateRegistrationOptions: typeof generateRegistrationOptions;
65
+ verifyRegistrationResponse: typeof verifyRegistrationResponse;
66
+ generateAuthenticationOptions: typeof generateAuthenticationOptions;
67
+ verifyAuthenticationResponse: typeof verifyAuthenticationResponse;
68
+ }
69
+ /**
70
+ * Implementação default do {@link AccountStore} sobre um model Lucid composto
71
+ * de `withAuthUser()` + `withCredentials()` (+ opcionalmente `withMfa()`). O
72
+ * model carrega `connection`/`table` (app-específico) e, por convenção, uma
73
+ * coluna `fullName` (mapeada de `name`).
74
+ */
75
+ export declare function lucidAccountStore(Model: any, options?: LucidAccountStoreOptions): AccountStore;