@adonis-agora/authkit-server 0.75.0 → 0.77.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +148 -0
- package/build/host/views/account/apps.edge +30 -0
- package/build/host/views/agents/consent.edge +60 -0
- package/build/host/views/agents/done.edge +24 -0
- package/build/host/views/consent.edge +9 -1
- package/build/host/views/mfa-challenge.edge +42 -3
- package/build/host/views/partials/styles.edge +1 -1
- package/build/index.d.ts +18 -1
- package/build/index.js +9 -1
- package/build/providers/authkit_server_provider.js +75 -50
- package/build/src/accounts/account_store.d.ts +2 -0
- package/build/src/accounts/lucid_account_store.js +2 -0
- package/build/src/adapters/adapter_contract.d.ts +2 -0
- package/build/src/adapters/database_adapter.d.ts +1 -0
- package/build/src/adapters/database_adapter.js +26 -0
- package/build/src/adapters/redis_adapter.d.ts +1 -0
- package/build/src/adapters/redis_adapter.js +30 -0
- package/build/src/agents/agent_identity.d.ts +33 -0
- package/build/src/agents/agent_identity.js +156 -0
- package/build/src/agents/config.d.ts +99 -0
- package/build/src/agents/config.js +101 -0
- package/build/src/agents/delegation_service.d.ts +154 -0
- package/build/src/agents/delegation_service.js +394 -0
- package/build/src/agents/delegation_store.d.ts +93 -0
- package/build/src/agents/delegation_store.js +222 -0
- package/build/src/agents/middleware.d.ts +73 -0
- package/build/src/agents/middleware.js +113 -0
- package/build/src/agents/protocol.d.ts +62 -0
- package/build/src/agents/protocol.js +51 -0
- package/build/src/agents/runtime.d.ts +36 -0
- package/build/src/agents/runtime.js +65 -0
- package/build/src/agents/signer.d.ts +34 -0
- package/build/src/agents/signer.js +70 -0
- package/build/src/audit/audit_sink.d.ts +1 -1
- package/build/src/audit/audit_sink.js +5 -0
- package/build/src/controllers/authorization_server_metadata_controller.d.ts +10 -0
- package/build/src/controllers/authorization_server_metadata_controller.js +20 -0
- package/build/src/define_config.d.ts +50 -3
- package/build/src/define_config.js +42 -1
- package/build/src/host/account_api/account_api_controller.d.ts +12 -0
- package/build/src/host/account_api/account_api_controller.js +40 -0
- package/build/src/host/admin_api/dto.d.ts +1 -1
- package/build/src/host/admin_sessions_service.d.ts +2 -0
- package/build/src/host/admin_sessions_service.js +18 -0
- package/build/src/host/auth_host_config.d.ts +4 -0
- package/build/src/host/client_names.d.ts +11 -0
- package/build/src/host/client_names.js +12 -0
- package/build/src/host/controllers/account_apps_controller.d.ts +2 -0
- package/build/src/host/controllers/account_apps_controller.js +39 -3
- package/build/src/host/controllers/account_orgs_controller.js +2 -1
- package/build/src/host/controllers/account_security_controller.js +8 -4
- package/build/src/host/controllers/account_session_controller.js +2 -1
- package/build/src/host/controllers/agent_consent_controller.d.ts +17 -0
- package/build/src/host/controllers/agent_consent_controller.js +138 -0
- package/build/src/host/controllers/agent_oauth_controller.d.ts +23 -0
- package/build/src/host/controllers/agent_oauth_controller.js +110 -0
- package/build/src/host/controllers/interaction_controller.d.ts +13 -1
- package/build/src/host/controllers/interaction_controller.js +380 -24
- package/build/src/host/csrf.d.ts +8 -18
- package/build/src/host/csrf.js +28 -2
- package/build/src/host/custom_login.d.ts +36 -0
- package/build/src/host/custom_login.js +76 -0
- package/build/src/host/custom_login_completion.d.ts +4 -0
- package/build/src/host/custom_login_completion.js +33 -0
- package/build/src/host/custom_mfa.d.ts +77 -0
- package/build/src/host/custom_mfa.js +268 -0
- package/build/src/host/i18n.d.ts +58 -0
- package/build/src/host/i18n.js +64 -6
- package/build/src/host/idp_session_bridge.d.ts +5 -3
- package/build/src/host/idp_session_bridge.js +5 -3
- package/build/src/host/oidc_rp_guard.d.ts +5 -1
- package/build/src/host/oidc_rp_guard.js +15 -2
- package/build/src/host/persistent_rp_session.d.ts +8 -0
- package/build/src/host/persistent_rp_session.js +74 -0
- package/build/src/host/redirect_exact.d.ts +12 -0
- package/build/src/host/redirect_exact.js +17 -0
- package/build/src/host/register_auth_host.js +60 -7
- package/build/src/host/request_url.d.ts +10 -0
- package/build/src/host/request_url.js +17 -0
- package/build/src/host/sudo/methods/magic_link.js +2 -1
- package/build/src/host/sudo/runtime.js +3 -2
- package/build/src/host/sudo_mode.js +4 -4
- package/build/src/host/whatsapp_code_sender.d.ts +20 -0
- package/build/src/host/whatsapp_code_sender.js +9 -0
- package/build/src/host/whatsapp_senders/http.d.ts +5 -0
- package/build/src/host/whatsapp_senders/http.js +33 -0
- package/build/src/host/whatsapp_senders/meta_whatsapp_code_sender.d.ts +20 -0
- package/build/src/host/whatsapp_senders/meta_whatsapp_code_sender.js +51 -0
- package/build/src/host/whatsapp_senders/whatsmiau_code_sender.d.ts +14 -0
- package/build/src/host/whatsapp_senders/whatsmiau_code_sender.js +36 -0
- package/build/src/mcp/mcp_oauth.d.ts +85 -0
- package/build/src/mcp/mcp_oauth.js +154 -0
- package/build/src/provider/build_provider.js +15 -1
- package/build/src/provider/oidc_service.d.ts +8 -0
- package/build/src/provider/oidc_service.js +16 -2
- package/build/src/schema/ensure.js +92 -0
- package/build/stubs/ui/react/pages/consent.tsx +17 -1
- package/build/stubs/ui/react/pages/mfa-challenge.tsx +207 -37
- package/package.json +2 -1
- package/stubs/ui/react/pages/consent.tsx +17 -1
- package/stubs/ui/react/pages/mfa-challenge.tsx +207 -37
package/README.md
CHANGED
|
@@ -11,6 +11,9 @@ wrapper around [`oidc-provider`](https://github.com/panva/node-oidc-provider).
|
|
|
11
11
|
- **Passwordless** — magic-link email login and passkey-first login.
|
|
12
12
|
- **Protocol extensions** — Device Flow (RFC 8628), DPoP (RFC 9449), PAR (RFC 9126), step-up
|
|
13
13
|
auth via `acr_values`, Dynamic Client Registration (RFC 7591/7592).
|
|
14
|
+
- **Personal agents** — AI assistants acting for your users: agent identity by signed JWT
|
|
15
|
+
(JWKS, no shared secrets) and user-approved, revocable delegation with scopes you define,
|
|
16
|
+
over a pluggable protocol adapter ([PACT](https://openpactprotocol.org) built in).
|
|
14
17
|
- **Native apps** — RFC 8252 native clients (`authkit:clients:create --native`: private-use
|
|
15
18
|
scheme / claimed https / loopback redirects, always public + PKCE) and the `@adonisjs/auth`
|
|
16
19
|
guards `oidcRpGuard` (session) and `oidcBearerGuard` (`Authorization: Bearer` access tokens,
|
|
@@ -160,3 +163,148 @@ OTel para que as séries existam.
|
|
|
160
163
|
## Notas
|
|
161
164
|
- Access tokens são opacos; ID tokens são JWT (assinados pelo JWKS gerido).
|
|
162
165
|
- PKCE (S256) é obrigatório; refresh tokens são rotacionados.
|
|
166
|
+
|
|
167
|
+
### Custom primary login methods
|
|
168
|
+
|
|
169
|
+
Register a class or instance for a host-specific authentication flow. AuthKit resolves classes through the request container, so normal Adonis `@inject()` dependencies work. The host validates and consumes the credential; AuthKit applies account policies, maintenance, MFA, audit, and OIDC session completion.
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
import { inject } from '@adonisjs/core';
|
|
173
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
174
|
+
import type { CustomLoginMethod } from '@adonis-agora/authkit-server';
|
|
175
|
+
import PhoneProofs from '#services/phone_proofs';
|
|
176
|
+
|
|
177
|
+
@inject()
|
|
178
|
+
export default class WhatsappLogin implements CustomLoginMethod {
|
|
179
|
+
readonly passwordless = true;
|
|
180
|
+
constructor(private proofs: PhoneProofs) {}
|
|
181
|
+
|
|
182
|
+
async authenticate(ctx: HttpContext) {
|
|
183
|
+
// Host service validates and atomically consumes the proof bound to this
|
|
184
|
+
// browser session and OIDC interaction. Never trust request.accountId.
|
|
185
|
+
const accountId = await this.proofs.consume(ctx);
|
|
186
|
+
return accountId ? { accountId } : null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
// config/authkit.ts
|
|
193
|
+
import WhatsappLogin from '#auth/whatsapp_login';
|
|
194
|
+
export default defineConfig({
|
|
195
|
+
// issuer, adapter, accountStore, ...
|
|
196
|
+
customLoginMethods: { whatsapp: WhatsappLogin },
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// Native form controller, on the host's POST /auth/interaction/:uid/... route
|
|
200
|
+
return authenticateCustomLogin(ctx, 'whatsapp');
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`authenticate` returns `{ accountId }` only after a valid proof, or `null` to deny login. It may also return a host-validated `remember` boolean; persistence is disabled by default and subject to the runtime session policy. The same session policy applies after MFA. Returning an identity does not skip MFA. The method name is retained in the OIDC `amr` claim, including when a second factor follows. Methods default to password-based policy; set `passwordless = true` for OTP, hardware or external proofs that do not use the account password.
|
|
204
|
+
|
|
205
|
+
A method may implement `begin(ctx)` to initiate a challenge. Call `beginCustomLogin(ctx, 'whatsapp')` from the host route. Both dispatchers check the current OIDC interaction and its `:uid` before invoking the method. Method names contain lowercase letters, digits, `:`, `_` or `-`, start with a letter, and have at most 64 characters. Built-in factor names (`pwd`, `email`, `mfa`, `totp`, `webauthn`, `recovery`) are reserved. Registration does not automatically create routes or UI: the host owns validation, CSRF, rate limits, bot protection, delivery, proof expiry, replay protection, account lookup/signup and the response renderer. Use native form navigation for final completion so AuthKit can render MFA or redirect to the relying party.
|
|
206
|
+
|
|
207
|
+
For an arbitrary flow that already verified its credential, `completeCustomLogin(ctx, { accountId, method, passwordless })` is the trusted escape hatch. Do not expose it as an endpoint accepting an account ID from the browser. A phone-only account may represent absent email as an empty string in the legacy `AuthAccount` DTO; OIDC omits `email` and `email_verified` in that case. `AuthAccount.phone`, when supplied, must represent a verified phone identity.
|
|
208
|
+
|
|
209
|
+
Denied proofs and handler exceptions produce a `login.failure` audit event with the method ID and a sanitized reason; proof contents and exception messages are never included. Host-owned challenge/OTP verification endpoints must audit their own earlier failures.
|
|
210
|
+
|
|
211
|
+
### WhatsApp OTP delivery providers
|
|
212
|
+
|
|
213
|
+
`whatsapp.sender` accepts any `WhatsappCodeSender` instance or class. It is separate from `customLoginMethods`: login classes verify identity, senders only deliver a code. Configuring a sender does not create an OTP store, route, validator, signup flow, or UI.
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
import { inject } from '@adonisjs/core';
|
|
217
|
+
import type { WhatsappCodeInput, WhatsappCodeSender } from '@adonis-agora/authkit-server';
|
|
218
|
+
import MyWhatsappSdk from '#services/my_whatsapp_sdk';
|
|
219
|
+
|
|
220
|
+
@inject()
|
|
221
|
+
class MyProvider implements WhatsappCodeSender {
|
|
222
|
+
constructor(private sdk: MyWhatsappSdk) {}
|
|
223
|
+
async sendCode(input: WhatsappCodeInput): Promise<void> {
|
|
224
|
+
await this.sdk.sendText(input.phone, input.text ?? input.code);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export default defineConfig({
|
|
229
|
+
// issuer, adapter, accountStore, ...
|
|
230
|
+
customLoginMethods: { whatsapp: WhatsappLogin },
|
|
231
|
+
whatsapp: { sender: MyProvider }, // or an already-created instance
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
After generating an OTP, resolve the sender through the request container:
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
const service = await ctx.containerResolver.make('authkit.server');
|
|
239
|
+
const binding = service.config.whatsapp?.sender;
|
|
240
|
+
if (!binding) throw new Error('WhatsApp login is not configured');
|
|
241
|
+
const sender = await resolveWhatsappCodeSender(ctx.containerResolver, binding);
|
|
242
|
+
await sender.sendCode({
|
|
243
|
+
phone: '5511999999999', code, locale: 'pt-BR', expiresInSeconds: 300,
|
|
244
|
+
text: ctx.i18n.t('auth.code_message', { code }),
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
The sender must reject when delivery fails. The host should activate its stored OTP only after delivery acceptance and enforce expiry, retries, request binding, replay protection and rate limits. Provider acceptance is not a delivery/read receipt. `text` is optional localized copy for providers supporting free-form messages; structured providers receive the original code independently.
|
|
249
|
+
|
|
250
|
+
Native adapters can be supplied as instances:
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
whatsapp: {
|
|
254
|
+
sender: new WhatsmiauCodeSender({ apiKey, instanceName }),
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
whatsapp: {
|
|
258
|
+
sender: new MetaWhatsappCodeSender({
|
|
259
|
+
accessToken, phoneNumberId, apiVersion, templateName,
|
|
260
|
+
languageCode: 'pt_BR',
|
|
261
|
+
}),
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Whatsmiau's optional `baseUrl` includes the API version path and defaults to `https://api.whatsmiau.dev/v2`. Meta requires an explicit supported Graph API version and an approved authentication template with an OTP/copy-code button. Template language must exist for the configured template; `languageCode` overrides automatic locale mapping (`pt-BR` → `pt_BR`, `en` → `en_US`, `es` → `es`). Meta template text and displayed expiry are managed in the approved template; `expiresInSeconds` describes the host's verification TTL and does not change template expiry. Both native adapters use bounded requests, reject redirects and discard provider error bodies so credentials/OTPs do not enter error messages.
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
### Custom MFA methods (classes or instances)
|
|
269
|
+
|
|
270
|
+
Register additional methods separately from primary login methods. Registration never enrolls an account: `isEnabled` must consult your enrollment store. A method may send a challenge in `begin`, describe its form fields, and verify a proof in `verify`.
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
import { inject } from '@adonisjs/core'
|
|
274
|
+
import type { CustomMfaContext, CustomMfaMethod } from '@adonis-agora/authkit-server'
|
|
275
|
+
|
|
276
|
+
// Enrollment and challenge services belong to your application and are injected by Adonis.
|
|
277
|
+
@inject()
|
|
278
|
+
export class WhatsappMfa implements CustomMfaMethod {
|
|
279
|
+
readonly factorId = 'phone'
|
|
280
|
+
constructor(private enrollment: PhoneEnrollment, private challenges: PhoneChallenges) {}
|
|
281
|
+
|
|
282
|
+
async isEnabled({ accountId }: CustomMfaContext) {
|
|
283
|
+
return this.enrollment.hasVerifiedPhone(accountId)
|
|
284
|
+
}
|
|
285
|
+
async describe() {
|
|
286
|
+
return { label: 'WhatsApp', fields: [{ name: 'code', label: 'Code', inputMode: 'numeric' as const }] }
|
|
287
|
+
}
|
|
288
|
+
async begin({ accountId, challengeId }: CustomMfaContext) {
|
|
289
|
+
await this.challenges.sendWhatsapp(accountId, challengeId)
|
|
290
|
+
}
|
|
291
|
+
async verify({ ctx, accountId, challengeId }: CustomMfaContext) {
|
|
292
|
+
const { code } = await ctx.request.validateUsing(phoneCodeValidator)
|
|
293
|
+
return this.challenges.consume(accountId, challengeId, code)
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// In defineConfig:
|
|
298
|
+
mfa: {
|
|
299
|
+
methods: { whatsapp: WhatsappMfa, sms: smsMfaInstance },
|
|
300
|
+
requiredFactors: 3,
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
The sample enrollment/challenge services and Vine validator are host implementations. Bind proofs to both `accountId` and `challengeId`; expire them, hash stored codes, limit sends, and atomically consume successful proofs. Delivery can use the WhatsApp sender interface or any SMS/provider SDK. Proofs and provider credentials must never appear in descriptors.
|
|
305
|
+
|
|
306
|
+
`requiredFactors` counts the primary login plus distinct additional groups (2 by default, configurable from 2 through 8). Omit it to challenge only accounts with an enrolled method; setting it explicitly also requires unenrolled accounts to enroll before they can authenticate. This extension does not supply an enrollment screen. Native TOTP, recovery codes and passkeys remain available; recovery and TOTP share one group. WhatsApp and SMS using the same phone must share `factorId: 'phone'`. A WhatsApp primary login class should also expose that group, preventing the same phone from counting twice. Group names express application policy; they do not prove independent physical authentication factors.
|
|
307
|
+
|
|
308
|
+
AuthKit binds the challenge to the current login interaction and browser session, expires it after ten minutes, caps failed custom proofs at five, and completes OIDC only after the required number of groups. Edge and generated React challenge screens render custom descriptors and optional start buttons. The named POST routes `authkit.mfa.custom.begin` and `authkit.mfa.custom.verify` use the existing login throttling and CSRF pipeline. Public `beginCustomMfa`, `verifyCustomMfa`, and `customMfaViewProps` support host integrations; proof verification alone does not complete authentication. Use the registered routes for policy-controlled completion.
|
|
309
|
+
|
|
310
|
+
Without `mfa` configuration, existing native MFA behavior is preserved. Applications can ship this extension without enabling additional methods.
|
|
@@ -56,5 +56,35 @@
|
|
|
56
56
|
@end
|
|
57
57
|
</div>
|
|
58
58
|
@end
|
|
59
|
+
|
|
60
|
+
@if(agents)
|
|
61
|
+
<h2 class="mt-8 mb-1 text-base font-semibold text-gray-900">{{ t('account.apps.agents_title') }}</h2>
|
|
62
|
+
<p class="mb-3 text-sm text-gray-500">{{ t('account.apps.agents_intro') }}</p>
|
|
63
|
+
<div class="overflow-hidden rounded-xl bg-white shadow-xs ring-1 ring-black/5">
|
|
64
|
+
@if(agents.length === 0)
|
|
65
|
+
<p class="p-6 text-sm text-gray-500">{{ t('account.apps.agents_empty') }}</p>
|
|
66
|
+
@else
|
|
67
|
+
@each(agent in agents)
|
|
68
|
+
<div class="flex items-center justify-between border-b border-gray-100 p-4 last:border-0">
|
|
69
|
+
<div>
|
|
70
|
+
<p class="text-sm font-medium text-gray-900">{{ agent.name }}</p>
|
|
71
|
+
<ul class="mt-1 text-xs text-gray-500">
|
|
72
|
+
@each(scope in agent.scopes)
|
|
73
|
+
<li>{{ scope.description }}</li>
|
|
74
|
+
@end
|
|
75
|
+
</ul>
|
|
76
|
+
</div>
|
|
77
|
+
<form method="POST" action="{{ accountPaths?.apps ?? '/account/apps' }}/agents/{{ agent.id }}/revoke"
|
|
78
|
+
onsubmit="return confirm('{{ t('account.apps.revoke_confirm') }}')">
|
|
79
|
+
<input type="hidden" name="_csrf" value="{{ csrfToken }}">
|
|
80
|
+
<button type="submit" class="rounded-lg border border-red-300 px-3 py-1.5 text-sm font-semibold text-red-700 hover:bg-red-50">
|
|
81
|
+
{{ t('account.apps.revoke') }}
|
|
82
|
+
</button>
|
|
83
|
+
</form>
|
|
84
|
+
</div>
|
|
85
|
+
@end
|
|
86
|
+
@end
|
|
87
|
+
</div>
|
|
88
|
+
@end
|
|
59
89
|
</div>
|
|
60
90
|
</body></html>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="pt-br"><head><meta charset="utf-8"><title>{{ t('agents.consent.page_title') }}</title>
|
|
3
|
+
@include('authkit::partials/styles')
|
|
4
|
+
@include('authkit::partials/submit_lock')
|
|
5
|
+
</head>
|
|
6
|
+
<body class="min-h-screen flex items-center justify-center bg-gray-50">
|
|
7
|
+
<div class="w-full max-w-md bg-white p-8 rounded-2xl shadow-xl ring-1 ring-black/5">
|
|
8
|
+
@if(request)
|
|
9
|
+
<form method="POST" action="{{ action }}">
|
|
10
|
+
<input type="hidden" name="_csrf" value="{{ csrfToken }}">
|
|
11
|
+
<input type="hidden" name="user_code" value="{{ request.userCode }}">
|
|
12
|
+
<h1 class="text-xl font-semibold text-gray-900">{{ t('agents.consent.title', { agent: request.agentName }) }}</h1>
|
|
13
|
+
<p class="mt-2 text-sm text-gray-600">{{ t('agents.consent.body', { origin: request.agentOrigin }) }}</p>
|
|
14
|
+
@if(account && account.email)
|
|
15
|
+
<p class="mt-2 text-xs text-gray-500">{{ t('agents.consent.signed_in_as', { email: account.email }) }}</p>
|
|
16
|
+
@end
|
|
17
|
+
|
|
18
|
+
<fieldset class="mt-6 space-y-3">
|
|
19
|
+
<legend class="text-sm font-medium text-gray-900">{{ t('agents.consent.scopes_label') }}</legend>
|
|
20
|
+
@each(scope in request.scopes)
|
|
21
|
+
<label class="flex items-start gap-3 rounded-lg border border-gray-200 p-3">
|
|
22
|
+
<input type="checkbox" name="scope" value="{{ scope.id }}" checked class="mt-0.5">
|
|
23
|
+
<span class="text-sm text-gray-800">{{ scope.description }}</span>
|
|
24
|
+
</label>
|
|
25
|
+
@end
|
|
26
|
+
</fieldset>
|
|
27
|
+
|
|
28
|
+
<p class="mt-4 text-xs text-gray-500">{{ t('agents.consent.code', { code: request.userCode }) }}</p>
|
|
29
|
+
|
|
30
|
+
<div class="mt-6 flex gap-3">
|
|
31
|
+
<button type="submit" name="decision" value="deny"
|
|
32
|
+
class="flex-1 rounded-lg border border-gray-300 py-2.5 text-sm font-semibold text-gray-700 transition hover:bg-gray-50">
|
|
33
|
+
{{ t('agents.consent.deny') }}
|
|
34
|
+
</button>
|
|
35
|
+
<button type="submit" name="decision" value="allow"
|
|
36
|
+
class="flex-1 rounded-lg bg-gray-900 py-2.5 text-sm font-semibold text-white transition hover:opacity-90">
|
|
37
|
+
{{ t('agents.consent.allow') }}
|
|
38
|
+
</button>
|
|
39
|
+
</div>
|
|
40
|
+
</form>
|
|
41
|
+
@else
|
|
42
|
+
<h1 class="text-xl font-semibold text-gray-900">{{ t('agents.consent.enter_code_title') }}</h1>
|
|
43
|
+
@if(error)
|
|
44
|
+
<p class="mt-2 rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-800">{{ t(error) }}</p>
|
|
45
|
+
@end
|
|
46
|
+
@if(error !== 'agents.consent.impersonating')
|
|
47
|
+
<form method="GET" action="{{ action }}" class="mt-4">
|
|
48
|
+
<label class="block text-sm text-gray-600" for="user_code">{{ t('agents.consent.enter_code_body') }}</label>
|
|
49
|
+
<input id="user_code" name="user_code" autocomplete="one-time-code" autocapitalize="characters"
|
|
50
|
+
class="mt-2 w-full rounded-lg border border-gray-300 px-3 py-2 text-center font-mono text-lg tracking-widest"
|
|
51
|
+
placeholder="XXXX-XXXX" required>
|
|
52
|
+
<button type="submit"
|
|
53
|
+
class="mt-4 w-full rounded-lg bg-gray-900 py-2.5 text-sm font-semibold text-white transition hover:opacity-90">
|
|
54
|
+
{{ t('agents.consent.continue') }}
|
|
55
|
+
</button>
|
|
56
|
+
</form>
|
|
57
|
+
@end
|
|
58
|
+
@end
|
|
59
|
+
</div>
|
|
60
|
+
</body></html>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="pt-br"><head><meta charset="utf-8"><title>{{ t('agents.consent.page_title') }}</title>
|
|
3
|
+
@include('authkit::partials/styles')
|
|
4
|
+
</head>
|
|
5
|
+
<body class="min-h-screen flex items-center justify-center bg-gray-50">
|
|
6
|
+
<div class="w-full max-w-md bg-white p-8 rounded-2xl shadow-xl ring-1 ring-black/5">
|
|
7
|
+
@if(status === 'approved')
|
|
8
|
+
<h1 class="text-xl font-semibold text-gray-900">{{ t('agents.done.approved_title', { agent: agentName }) }}</h1>
|
|
9
|
+
<ul class="mt-4 space-y-1 text-sm text-gray-700">
|
|
10
|
+
@each(scope in scopes)
|
|
11
|
+
<li>• {{ scope.description }}</li>
|
|
12
|
+
@end
|
|
13
|
+
</ul>
|
|
14
|
+
<p class="mt-4 text-sm text-gray-600">{{ t('agents.done.return') }}</p>
|
|
15
|
+
<p class="mt-2 text-xs text-gray-500">{{ t('agents.done.revoke_hint') }}</p>
|
|
16
|
+
@elseif(status === 'denied')
|
|
17
|
+
<h1 class="text-xl font-semibold text-gray-900">{{ t('agents.done.denied_title') }}</h1>
|
|
18
|
+
<p class="mt-2 text-sm text-gray-600">{{ t('agents.done.return') }}</p>
|
|
19
|
+
@else
|
|
20
|
+
<h1 class="text-xl font-semibold text-gray-900">{{ t('agents.done.expired_title') }}</h1>
|
|
21
|
+
<p class="mt-2 text-sm text-gray-600">{{ t('agents.done.expired_body') }}</p>
|
|
22
|
+
@end
|
|
23
|
+
</div>
|
|
24
|
+
</body></html>
|
|
@@ -9,8 +9,16 @@
|
|
|
9
9
|
<input type="hidden" name="_csrf" value="{{ csrfToken }}">
|
|
10
10
|
<h1 class="text-xl font-semibold text-gray-900">{{ t('consent.title') }}</h1>
|
|
11
11
|
<p class="mt-2 text-sm text-gray-600">
|
|
12
|
-
{{{ t('consent.body', { app:
|
|
12
|
+
{{{ t('consent.body', { app: clientNameHtml }) }}}
|
|
13
13
|
</p>
|
|
14
|
+
@if(scopes && scopes.length)
|
|
15
|
+
<p class="mt-4 text-sm font-medium text-gray-900">{{ t('consent.scopes_title') }}</p>
|
|
16
|
+
<ul class="mt-2 space-y-1 text-sm text-gray-600 list-disc list-inside">
|
|
17
|
+
@each(scope in scopes)
|
|
18
|
+
<li>{{ scope.label }}</li>
|
|
19
|
+
@end
|
|
20
|
+
</ul>
|
|
21
|
+
@end
|
|
14
22
|
|
|
15
23
|
<button type="submit"
|
|
16
24
|
class="mt-6 w-full rounded-lg bg-gray-900 py-2.5 text-sm font-semibold text-white transition hover:opacity-90">
|
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
{{ t('mfa_challenge.intro') }}
|
|
13
13
|
</p>
|
|
14
14
|
|
|
15
|
+
@if(requiredMfaFactors > 2)
|
|
16
|
+
<p class="mt-3 text-sm text-gray-600" role="status">
|
|
17
|
+
{{ t('mfa_challenge.progress', { completed: 1 + (completedMfaMethods || []).length, required: requiredMfaFactors }) }}
|
|
18
|
+
</p>
|
|
19
|
+
@end
|
|
20
|
+
|
|
15
21
|
@if(error)
|
|
16
22
|
<p class="mt-4 text-sm text-red-600">{{ error }}</p>
|
|
17
23
|
@end
|
|
@@ -21,7 +27,7 @@
|
|
|
21
27
|
`totpAvailable === false` = a conta só tem passkey, sem app autenticador
|
|
22
28
|
confirmado: pedir um código de 6 dígitos seria pedir o impossível. Ausente
|
|
23
29
|
(stores que não reportam o estado) mantém o campo visível. --}}
|
|
24
|
-
@if(!noEnrollment && totpAvailable !== false)
|
|
30
|
+
@if(!noEnrollment && !otpLocked && totpAvailable !== false)
|
|
25
31
|
<div class="mt-6">
|
|
26
32
|
<label for="code" class="mb-1 block text-sm font-medium text-gray-700">{{ t('mfa_challenge.code_label') }}</label>
|
|
27
33
|
<input id="code" name="code" inputmode="numeric" autocomplete="one-time-code"
|
|
@@ -43,6 +49,39 @@
|
|
|
43
49
|
@end
|
|
44
50
|
</form>
|
|
45
51
|
|
|
52
|
+
@if(!noEnrollment)
|
|
53
|
+
@each(method in (customMfaMethods || []))
|
|
54
|
+
@if(!(completedMfaMethods || []).includes(method.id))
|
|
55
|
+
<section class="mt-6">
|
|
56
|
+
<h2 class="text-sm font-semibold text-gray-900">{{ method.label }}</h2>
|
|
57
|
+
@if(method.requiresBegin && !method.started)
|
|
58
|
+
<form method="POST" action="{{ method.beginUrl }}" class="mt-3">
|
|
59
|
+
<input type="hidden" name="_csrf" value="{{ csrfToken }}">
|
|
60
|
+
<button type="submit" class="w-full rounded-lg border border-gray-300 px-3 py-2.5 text-sm font-semibold text-gray-700 transition hover:bg-gray-50">
|
|
61
|
+
{{ t('mfa_challenge.start') }}
|
|
62
|
+
</button>
|
|
63
|
+
</form>
|
|
64
|
+
@else
|
|
65
|
+
<form method="POST" action="{{ method.verifyUrl }}" class="mt-3 space-y-3">
|
|
66
|
+
<input type="hidden" name="_csrf" value="{{ csrfToken }}">
|
|
67
|
+
@each(field in (method.fields || []))
|
|
68
|
+
<div>
|
|
69
|
+
<label for="custom-{{ method.id }}-{{ field.name }}" class="mb-1 block text-sm font-medium text-gray-700">{{ field.label }}</label>
|
|
70
|
+
<input id="custom-{{ method.id }}-{{ field.name }}" name="{{ field.name }}"
|
|
71
|
+
type="{{ field.type || 'text' }}" inputmode="{{ field.inputMode || 'text' }}" autocomplete="{{ field.autoComplete || 'off' }}"
|
|
72
|
+
class="w-full rounded-lg border border-gray-300 px-3 py-2 text-base outline-hidden focus:border-gray-900 focus:ring-2 focus:ring-gray-900">
|
|
73
|
+
</div>
|
|
74
|
+
@end
|
|
75
|
+
<button type="submit" class="w-full rounded-lg bg-gray-900 px-3 py-2.5 text-sm font-semibold text-white transition hover:opacity-90">
|
|
76
|
+
{{ t('mfa_challenge.submit') }}
|
|
77
|
+
</button>
|
|
78
|
+
</form>
|
|
79
|
+
@end
|
|
80
|
+
</section>
|
|
81
|
+
@end
|
|
82
|
+
@end
|
|
83
|
+
@end
|
|
84
|
+
|
|
46
85
|
@if(passkeyAvailable && !noEnrollment)
|
|
47
86
|
{{-- Passkey como alternativa ao código TOTP. O form é submetido por página
|
|
48
87
|
inteira (não fetch) para que o 303 de volta ao client navegue o browser. --}}
|
|
@@ -61,7 +100,7 @@
|
|
|
61
100
|
|
|
62
101
|
{{-- Códigos de recuperação só existem junto com o TOTP (são gerados no
|
|
63
102
|
enrollment); sem ele, a seção não tem o que receber. --}}
|
|
64
|
-
@if(!noEnrollment && totpAvailable !== false)
|
|
103
|
+
@if(!noEnrollment && !otpLocked && totpAvailable !== false)
|
|
65
104
|
<details class="mt-6 text-sm text-gray-600">
|
|
66
105
|
<summary class="cursor-pointer hover:underline">{{ t('mfa_challenge.recovery_summary') }}</summary>
|
|
67
106
|
<form method="POST" action="/auth/interaction/{{ uid }}/mfa" class="mt-3">
|
|
@@ -77,7 +116,7 @@
|
|
|
77
116
|
@end
|
|
78
117
|
</div>
|
|
79
118
|
|
|
80
|
-
@if(passkeyAvailable)
|
|
119
|
+
@if(passkeyAvailable && !noEnrollment)
|
|
81
120
|
{{-- M12: script inline sem nonce é bloqueado por CSP `script-src 'self'` — extraído
|
|
82
121
|
para asset same-origin (compartilhado com login.edge). --}}
|
|
83
122
|
<script type="module" src="/authkit/assets/passkey_button.js"></script>
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
{{-- Gerado por scripts/build_host_css.mjs — não editar à mão. --}}
|
|
2
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-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-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{.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-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)}.mb-1{margin-bottom:var(--spacing)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.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-sm{max-width:var(--container-sm)}.flex-1{flex:1}.cursor-pointer{cursor:pointer}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.items-center{align-items:center}.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-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-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-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.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))}.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-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>
|
|
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-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{.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}.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-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.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))}.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-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
|
@@ -10,6 +10,14 @@ export { appKeyEncrypter, lucidAccountStore } from './src/accounts/lucid_account
|
|
|
10
10
|
export { AuthOrganization, AuthOrganizationInvitation, AuthOrganizationMember, defaultOrganizationModels, } from './src/accounts/lucid_store/organization_models.js';
|
|
11
11
|
export type { LucidStoresModels, LucidStoresOptions, LucidStoresResult, } from './src/accounts/lucid_stores.js';
|
|
12
12
|
export { lucidStores } from './src/accounts/lucid_stores.js';
|
|
13
|
+
export type { PersonalAgentIdentity } from './src/agents/agent_identity.js';
|
|
14
|
+
export { PersonalAgentVerifier } from './src/agents/agent_identity.js';
|
|
15
|
+
export type { PersonalAgentDelegationConfigInput, PersonalAgentRegistration, PersonalAgentResolver, PersonalAgentsConfigInput, ResolvedPersonalAgentsConfig, } from './src/agents/config.js';
|
|
16
|
+
export type { DelegationContext, DelegationGrantSummary, DelegationReceipt, } from './src/agents/delegation_service.js';
|
|
17
|
+
export type { PersonalAgentAuthOptions, PersonalAgentRequestContext, } from './src/agents/middleware.js';
|
|
18
|
+
export { personalAgentAuth, personalAgentOf, personalAgentReceipt, personalAgentSecurity, personalAgentStepUp, } from './src/agents/middleware.js';
|
|
19
|
+
export type { BuiltinProtocolId, PactOptions, PersonalAgentProtocol, } from './src/agents/protocol.js';
|
|
20
|
+
export { pact, pactProtocol } from './src/agents/protocol.js';
|
|
13
21
|
export type { AuditEvent, AuditEventType, AuditPage, AuditSink, ListAuditParams, StoredAuditEvent, } from './src/audit/audit_sink.js';
|
|
14
22
|
export { lucidAuditSink } from './src/audit/lucid_audit_sink.js';
|
|
15
23
|
export type { AdminApiConfigInput, AdminConfigInput, AuthHostRenderer, AuthMethodsConfigInput, AuthServerConfigInput, AuthSocialConfig, DynamicRegistrationConfigInput, LoginConfigInput, OrganizationsConfigInput, PasswordlessConfigInput, RateLimitBucket, RateLimitConfigInput, RegistrationConfigInput, ResolvedAdminApiConfig, ResolvedAdminConfig, ResolvedAuthMethodsConfig, ResolvedDynamicRegistrationConfig, ResolvedLoginConfig, ResolvedNotificationsConfig, ResolvedOrganizationsConfig, ResolvedPasswordlessConfig, ResolvedRateLimitConfig, ResolvedRegistrationConfig, ResolvedServerConfig, ResolvedWebauthnConfig, WebauthnConfigInput, } from './src/define_config.js';
|
|
@@ -68,12 +76,16 @@ export { deriveLockedSettingKeys, isSettingLocked, lockedSettingKeys, POLICY_ROU
|
|
|
68
76
|
export { consoleLoginUrl, getAccountId, hasAccountSession, realAccountId, } from './src/host/console_session.js';
|
|
69
77
|
export type { AuthkitCsrfOptions } from './src/host/csrf.js';
|
|
70
78
|
export { authkitCsrfExceptions } from './src/host/csrf.js';
|
|
79
|
+
export type { CustomLoginIdentity, CustomLoginMethod, CustomLoginMethodBinding, CustomLoginMethodConstructor, CustomLoginMethods, } from './src/host/custom_login.js';
|
|
80
|
+
export { authenticateCustomLogin, beginCustomLogin, completeCustomLogin, } from './src/host/custom_login.js';
|
|
81
|
+
export type { CustomMfaContext, CustomMfaDescription, CustomMfaDescriptor, CustomMfaField, CustomMfaMethod, CustomMfaMethodBinding, CustomMfaMethodConstructor, CustomMfaMethods, } from './src/host/custom_mfa.js';
|
|
82
|
+
export { beginCustomMfa, customMfaViewProps, verifyCustomMfa } from './src/host/custom_mfa.js';
|
|
71
83
|
export { normalizeEmailIdentifier } from './src/host/email_identifier.js';
|
|
72
84
|
export type { ResolveGeo } from './src/host/geo.js';
|
|
73
85
|
export { GEO_RESOLVE_TIMEOUT_MS, resolveGeoSafe } from './src/host/geo.js';
|
|
74
86
|
export type { AuthMessages, I18nConfig } from './src/host/i18n.js';
|
|
75
87
|
export { BUILTIN_MESSAGES, DEFAULT_LOCALE, DEFAULT_MESSAGES, PT_BR_MESSAGES, resolveMessages, translate, } from './src/host/i18n.js';
|
|
76
|
-
export { ensureConsoleSession } from './src/host/idp_session_bridge.js';
|
|
88
|
+
export { ACCOUNT_IDP_SESSION_KEY, ensureConsoleSession, readIdpSession, } from './src/host/idp_session_bridge.js';
|
|
77
89
|
export type { ImpersonationClientLike, ImpersonationPanel } from './src/host/impersonation.js';
|
|
78
90
|
export { buildImpersonationPanel } from './src/host/impersonation.js';
|
|
79
91
|
export type { ImpersonationStartErrorCode, ImpersonationState, StartImpersonationParams, StopImpersonationOptions, TokenExchangeResult, } from './src/host/impersonation_session.js';
|
|
@@ -85,6 +97,8 @@ export { OidcBearerGuard, oidcBearerGuard } from './src/host/oidc_bearer_guard.j
|
|
|
85
97
|
export type { OidcRpGuardEvents, OidcRpGuardOptions } from './src/host/oidc_rp_guard.js';
|
|
86
98
|
export { OidcRpGuard, oidcRpGuard } from './src/host/oidc_rp_guard.js';
|
|
87
99
|
export { evaluateLoginOtp, generateOtpCode, OTP_LOGIN_DEFAULTS, type OtpLoginConfigInput, type OtpVerifyOutcome, type ResolvedOtpLoginConfig, resolveOtpLoginConfig, } from './src/host/otp_login.js';
|
|
100
|
+
export type { PersistentRpOptions } from './src/host/persistent_rp_session.js';
|
|
101
|
+
export { endRpSession } from './src/host/persistent_rp_session.js';
|
|
88
102
|
export type { AuthThrottles, ThrottleMiddleware } from './src/host/rate_limit.js';
|
|
89
103
|
export { createAuthThrottles } from './src/host/rate_limit.js';
|
|
90
104
|
export type { AccountScreensOptions, AuthHostOptions, AuthHostRouteMap, } from './src/host/register_auth_host.js';
|
|
@@ -109,6 +123,9 @@ export type { ParsedUserAgent } from './src/host/user_agent.js';
|
|
|
109
123
|
export { parseUserAgent } from './src/host/user_agent.js';
|
|
110
124
|
export type { ResolvedUserLoginMethods, UserLoginMethodKey, UserLoginMethods, } from './src/host/user_login_methods.js';
|
|
111
125
|
export { normalizeUserLoginMethods, parseUserLoginMethodsPayload, resolveEffectiveUserLoginMethods, USER_LOGIN_METHOD_KEYS, } from './src/host/user_login_methods.js';
|
|
126
|
+
export type { MetaWhatsappCodeSenderOptions, WhatsappCodeInput, WhatsappCodeSender, WhatsappCodeSenderBinding, WhatsappCodeSenderConstructor, WhatsmiauCodeSenderOptions, } from './src/host/whatsapp_code_sender.js';
|
|
127
|
+
export { MetaWhatsappCodeSender, resolveWhatsappCodeSender, WhatsmiauCodeSender, } from './src/host/whatsapp_code_sender.js';
|
|
128
|
+
export { MCP_CLIENT_REDIRECTS, MCP_RESOURCE_SCOPES, type McpOAuthConfigInput, type OAuthResourceRegistration, registeredOAuthResources, registerOAuthResource, } from './src/mcp/mcp_oauth.js';
|
|
112
129
|
export { withAuditLog } from './src/mixins/with_audit_log.js';
|
|
113
130
|
export { withAuthUser } from './src/mixins/with_auth_user.js';
|
|
114
131
|
export { withCredentials } from './src/mixins/with_credentials.js';
|
package/build/index.js
CHANGED
|
@@ -7,6 +7,9 @@ export { supportsAccountDeletion, supportsAccountEmailRewrite, supportsAccountIm
|
|
|
7
7
|
export { appKeyEncrypter, lucidAccountStore } from './src/accounts/lucid_account_store.js';
|
|
8
8
|
export { AuthOrganization, AuthOrganizationInvitation, AuthOrganizationMember, defaultOrganizationModels, } from './src/accounts/lucid_store/organization_models.js';
|
|
9
9
|
export { lucidStores } from './src/accounts/lucid_stores.js';
|
|
10
|
+
export { PersonalAgentVerifier } from './src/agents/agent_identity.js';
|
|
11
|
+
export { personalAgentAuth, personalAgentOf, personalAgentReceipt, personalAgentSecurity, personalAgentStepUp, } from './src/agents/middleware.js';
|
|
12
|
+
export { pact, pactProtocol } from './src/agents/protocol.js';
|
|
10
13
|
export { lucidAuditSink } from './src/audit/lucid_audit_sink.js';
|
|
11
14
|
export { adapters, defineConfig, resolveAdmin, resolveAdminApi, resolveAuthMethodsConfig, resolveDynamicRegistration, resolveLogin, resolveNotifications, resolveOrganizations, resolvePasswordless, resolveRateLimit, resolveRegistration, resolveWebauthn, toSeconds, } from './src/define_config.js';
|
|
12
15
|
export { buildWebhookBody, composeAuditSink, resolveEvents, signWebhookBody, } from './src/events/dispatcher.js';
|
|
@@ -51,10 +54,12 @@ export { assertClientMetadata, ClientMetadataError, redirectUriProblem, } from '
|
|
|
51
54
|
export { deriveLockedSettingKeys, isSettingLocked, lockedSettingKeys, POLICY_ROUTE_OPTIONS, resetLockedSettingKeys, SettingLockedError, setLockedSettingKeys, } from './src/host/config_locks.js';
|
|
52
55
|
export { consoleLoginUrl, getAccountId, hasAccountSession, realAccountId, } from './src/host/console_session.js';
|
|
53
56
|
export { authkitCsrfExceptions } from './src/host/csrf.js';
|
|
57
|
+
export { authenticateCustomLogin, beginCustomLogin, completeCustomLogin, } from './src/host/custom_login.js';
|
|
58
|
+
export { beginCustomMfa, customMfaViewProps, verifyCustomMfa } from './src/host/custom_mfa.js';
|
|
54
59
|
export { normalizeEmailIdentifier } from './src/host/email_identifier.js';
|
|
55
60
|
export { GEO_RESOLVE_TIMEOUT_MS, resolveGeoSafe } from './src/host/geo.js';
|
|
56
61
|
export { BUILTIN_MESSAGES, DEFAULT_LOCALE, DEFAULT_MESSAGES, PT_BR_MESSAGES, resolveMessages, translate, } from './src/host/i18n.js';
|
|
57
|
-
export { ensureConsoleSession } from './src/host/idp_session_bridge.js';
|
|
62
|
+
export { ACCOUNT_IDP_SESSION_KEY, ensureConsoleSession, readIdpSession, } from './src/host/idp_session_bridge.js';
|
|
58
63
|
export { buildImpersonationPanel } from './src/host/impersonation.js';
|
|
59
64
|
// Session impersonation — RP-side glue that routes through the IdP's RFC 8693
|
|
60
65
|
// token-exchange (the IdP validates the admin role + audits). See
|
|
@@ -74,6 +79,7 @@ export { OidcBearerGuard, oidcBearerGuard } from './src/host/oidc_bearer_guard.j
|
|
|
74
79
|
export { OidcRpGuard, oidcRpGuard } from './src/host/oidc_rp_guard.js';
|
|
75
80
|
// Login por OTP (código digitável): config + helpers puros.
|
|
76
81
|
export { evaluateLoginOtp, generateOtpCode, OTP_LOGIN_DEFAULTS, resolveOtpLoginConfig, } from './src/host/otp_login.js';
|
|
82
|
+
export { endRpSession } from './src/host/persistent_rp_session.js';
|
|
77
83
|
export { createAuthThrottles } from './src/host/rate_limit.js';
|
|
78
84
|
export { registerAuthHost } from './src/host/register_auth_host.js';
|
|
79
85
|
export { edgeRenderer } from './src/host/renderers/edge_renderer.js';
|
|
@@ -107,6 +113,8 @@ export { buildTrustedDevicePayload, isTrustedDeviceValid, resolveTrustedDevices,
|
|
|
107
113
|
export { parseUserAgent } from './src/host/user_agent.js';
|
|
108
114
|
// Tipos de login POR USUÁRIO (self-service no console de conta).
|
|
109
115
|
export { normalizeUserLoginMethods, parseUserLoginMethodsPayload, resolveEffectiveUserLoginMethods, USER_LOGIN_METHOD_KEYS, } from './src/host/user_login_methods.js';
|
|
116
|
+
export { MetaWhatsappCodeSender, resolveWhatsappCodeSender, WhatsmiauCodeSender, } from './src/host/whatsapp_code_sender.js';
|
|
117
|
+
export { MCP_CLIENT_REDIRECTS, MCP_RESOURCE_SCOPES, registeredOAuthResources, registerOAuthResource, } from './src/mcp/mcp_oauth.js';
|
|
110
118
|
export { withAuditLog } from './src/mixins/with_audit_log.js';
|
|
111
119
|
export { withAuthUser } from './src/mixins/with_auth_user.js';
|
|
112
120
|
export { withCredentials } from './src/mixins/with_credentials.js';
|