@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.
- package/LICENSE +21 -0
- package/README.md +137 -0
- package/build/assets/grafana/authkit-dashboard.json +118 -0
- package/build/commands/commands.json +30 -0
- package/build/commands/configure.d.ts +2 -0
- package/build/commands/configure.js +42 -0
- package/build/commands/eject.d.ts +11 -0
- package/build/commands/eject.js +96 -0
- package/build/commands/main.d.ts +12 -0
- package/build/commands/main.js +38 -0
- package/build/commands/ui_preset.d.ts +4 -0
- package/build/commands/ui_preset.js +32 -0
- package/build/database/migrations/make_authkit_oidc_table.d.ts +6 -0
- package/build/database/migrations/make_authkit_oidc_table.js +19 -0
- package/build/host/views/account/login.edge +29 -0
- package/build/host/views/account/mfa.edge +151 -0
- package/build/host/views/account/tokens.edge +70 -0
- package/build/host/views/admin/audit.edge +72 -0
- package/build/host/views/admin/clients.edge +51 -0
- package/build/host/views/admin/dashboard.edge +58 -0
- package/build/host/views/admin/users.edge +76 -0
- package/build/host/views/consent.edge +19 -0
- package/build/host/views/forgot.edge +30 -0
- package/build/host/views/login.edge +91 -0
- package/build/host/views/mfa-challenge.edge +88 -0
- package/build/host/views/reset.edge +29 -0
- package/build/host/views/signup.edge +44 -0
- package/build/host/views/verify-email.edge +16 -0
- package/build/index.d.ts +42 -0
- package/build/index.js +28 -0
- package/build/providers/authkit_server_provider.d.ts +19 -0
- package/build/providers/authkit_server_provider.js +81 -0
- package/build/src/accounts/account_store.d.ts +136 -0
- package/build/src/accounts/account_store.js +1 -0
- package/build/src/accounts/lucid_account_store.d.ts +75 -0
- package/build/src/accounts/lucid_account_store.js +396 -0
- package/build/src/adapters/adapter_contract.d.ts +18 -0
- package/build/src/adapters/adapter_contract.js +1 -0
- package/build/src/adapters/database_adapter.d.ts +15 -0
- package/build/src/adapters/database_adapter.js +63 -0
- package/build/src/adapters/factory.d.ts +30 -0
- package/build/src/adapters/factory.js +43 -0
- package/build/src/adapters/redis_adapter.d.ts +16 -0
- package/build/src/adapters/redis_adapter.js +95 -0
- package/build/src/audit/audit_sink.d.ts +54 -0
- package/build/src/audit/audit_sink.js +1 -0
- package/build/src/audit/lucid_audit_sink.d.ts +10 -0
- package/build/src/audit/lucid_audit_sink.js +60 -0
- package/build/src/controllers/oidc_callback_controller.d.ts +22 -0
- package/build/src/controllers/oidc_callback_controller.js +33 -0
- package/build/src/define_config.d.ts +261 -0
- package/build/src/define_config.js +115 -0
- package/build/src/host/account_lockout.d.ts +86 -0
- package/build/src/host/account_lockout.js +185 -0
- package/build/src/host/augmentations.d.ts +1 -0
- package/build/src/host/augmentations.js +1 -0
- package/build/src/host/branding.d.ts +17 -0
- package/build/src/host/branding.js +8 -0
- package/build/src/host/controllers/account_mfa_controller.d.ts +30 -0
- package/build/src/host/controllers/account_mfa_controller.js +157 -0
- package/build/src/host/controllers/account_session_controller.d.ts +7 -0
- package/build/src/host/controllers/account_session_controller.js +50 -0
- package/build/src/host/controllers/account_tokens_controller.d.ts +7 -0
- package/build/src/host/controllers/account_tokens_controller.js +55 -0
- package/build/src/host/controllers/admin/admin_audit_controller.d.ts +10 -0
- package/build/src/host/controllers/admin/admin_audit_controller.js +56 -0
- package/build/src/host/controllers/admin/admin_clients_controller.d.ts +10 -0
- package/build/src/host/controllers/admin/admin_clients_controller.js +24 -0
- package/build/src/host/controllers/admin/admin_dashboard_controller.d.ts +10 -0
- package/build/src/host/controllers/admin/admin_dashboard_controller.js +27 -0
- package/build/src/host/controllers/admin/admin_users_controller.d.ts +11 -0
- package/build/src/host/controllers/admin/admin_users_controller.js +53 -0
- package/build/src/host/controllers/interaction_controller.d.ts +44 -0
- package/build/src/host/controllers/interaction_controller.js +304 -0
- package/build/src/host/controllers/pat_introspection_controller.d.ts +22 -0
- package/build/src/host/controllers/pat_introspection_controller.js +46 -0
- package/build/src/host/controllers/registration_controller.d.ts +18 -0
- package/build/src/host/controllers/registration_controller.js +169 -0
- package/build/src/host/controllers/social_controller.d.ts +8 -0
- package/build/src/host/controllers/social_controller.js +82 -0
- package/build/src/host/default_mailer.d.ts +39 -0
- package/build/src/host/default_mailer.js +141 -0
- package/build/src/host/email_templates.d.ts +35 -0
- package/build/src/host/email_templates.js +66 -0
- package/build/src/host/i18n.d.ts +178 -0
- package/build/src/host/i18n.js +208 -0
- package/build/src/host/middleware/account_auth.d.ts +7 -0
- package/build/src/host/middleware/account_auth.js +11 -0
- package/build/src/host/rate_limit.d.ts +32 -0
- package/build/src/host/rate_limit.js +87 -0
- package/build/src/host/register_auth_host.d.ts +41 -0
- package/build/src/host/register_auth_host.js +133 -0
- package/build/src/host/renderers/edge_renderer.d.ts +3 -0
- package/build/src/host/renderers/edge_renderer.js +29 -0
- package/build/src/host/renderers/inertia_renderer.d.ts +5 -0
- package/build/src/host/renderers/inertia_renderer.js +26 -0
- package/build/src/host/validators.d.ts +39 -0
- package/build/src/host/validators.js +13 -0
- package/build/src/keys/jwks_manager.d.ts +6 -0
- package/build/src/keys/jwks_manager.js +11 -0
- package/build/src/mixins/with_audit_log.d.ts +19 -0
- package/build/src/mixins/with_audit_log.js +41 -0
- package/build/src/mixins/with_auth_user.d.ts +18 -0
- package/build/src/mixins/with_auth_user.js +39 -0
- package/build/src/mixins/with_credentials.d.ts +20 -0
- package/build/src/mixins/with_credentials.js +29 -0
- package/build/src/mixins/with_mfa.d.ts +31 -0
- package/build/src/mixins/with_mfa.js +39 -0
- package/build/src/mixins/with_personal_access_token.d.ts +19 -0
- package/build/src/mixins/with_personal_access_token.js +44 -0
- package/build/src/mixins/with_provider_identity.d.ts +20 -0
- package/build/src/mixins/with_provider_identity.js +32 -0
- package/build/src/mixins/with_webauthn_credential.d.ts +37 -0
- package/build/src/mixins/with_webauthn_credential.js +49 -0
- package/build/src/observability/metrics_controller.d.ts +5 -0
- package/build/src/observability/metrics_controller.js +24 -0
- package/build/src/observability/metrics_service.d.ts +2 -0
- package/build/src/observability/metrics_service.js +7 -0
- package/build/src/observability/otel_recorder.d.ts +10 -0
- package/build/src/observability/otel_recorder.js +59 -0
- package/build/src/observability/wire_provider_events.d.ts +12 -0
- package/build/src/observability/wire_provider_events.js +19 -0
- package/build/src/pat/lucid_pat_store.d.ts +6 -0
- package/build/src/pat/lucid_pat_store.js +62 -0
- package/build/src/pat/pat_store.d.ts +31 -0
- package/build/src/pat/pat_store.js +1 -0
- package/build/src/pat/pat_tokens.d.ts +4 -0
- package/build/src/pat/pat_tokens.js +9 -0
- package/build/src/provider/build_provider.d.ts +8 -0
- package/build/src/provider/build_provider.js +101 -0
- package/build/src/provider/interaction_actions.d.ts +21 -0
- package/build/src/provider/interaction_actions.js +32 -0
- package/build/src/provider/oidc_service.d.ts +17 -0
- package/build/src/provider/oidc_service.js +84 -0
- package/build/src/provider/token_exchange.d.ts +15 -0
- package/build/src/provider/token_exchange.js +72 -0
- package/build/src/register_routes.d.ts +16 -0
- package/build/src/register_routes.js +21 -0
- package/build/stubs/config/authkit.stub +29 -0
- package/build/stubs/main.d.ts +1 -0
- package/build/stubs/main.js +2 -0
- package/build/stubs/models/auth_user.stub +13 -0
- package/build/stubs/ui/edge/views/consent.edge +13 -0
- package/build/stubs/ui/edge/views/login.edge +19 -0
- package/build/stubs/ui/react/components/auth_shell.tsx +67 -0
- package/build/stubs/ui/react/pages/account/login.tsx +56 -0
- package/build/stubs/ui/react/pages/account/mfa.tsx +132 -0
- package/build/stubs/ui/react/pages/account/tokens.tsx +88 -0
- package/build/stubs/ui/react/pages/consent.tsx +39 -0
- package/build/stubs/ui/react/pages/forgot.tsx +44 -0
- package/build/stubs/ui/react/pages/login.tsx +171 -0
- package/build/stubs/ui/react/pages/mfa-challenge.tsx +72 -0
- package/build/stubs/ui/react/pages/reset.tsx +58 -0
- package/build/stubs/ui/react/pages/signup.tsx +78 -0
- package/build/stubs/ui/react/pages/verify-email.tsx +24 -0
- package/build/types.d.ts +7 -0
- package/build/types.js +1 -0
- package/package.json +108 -0
- package/stubs/config/authkit.stub +29 -0
- package/stubs/main.ts +2 -0
- package/stubs/models/auth_user.stub +13 -0
- package/stubs/ui/edge/views/consent.edge +13 -0
- package/stubs/ui/edge/views/login.edge +19 -0
- package/stubs/ui/react/components/auth_shell.tsx +67 -0
- package/stubs/ui/react/pages/account/login.tsx +56 -0
- package/stubs/ui/react/pages/account/mfa.tsx +132 -0
- package/stubs/ui/react/pages/account/tokens.tsx +88 -0
- package/stubs/ui/react/pages/consent.tsx +39 -0
- package/stubs/ui/react/pages/forgot.tsx +44 -0
- package/stubs/ui/react/pages/login.tsx +171 -0
- package/stubs/ui/react/pages/mfa-challenge.tsx +72 -0
- package/stubs/ui/react/pages/reset.tsx +58 -0
- package/stubs/ui/react/pages/signup.tsx +78 -0
- package/stubs/ui/react/pages/verify-email.tsx +24 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.makePath('inertia/pages/authkit/account/login.tsx') })
|
|
3
|
+
}}}
|
|
4
|
+
interface Props {
|
|
5
|
+
csrfToken: string
|
|
6
|
+
error?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default function AccountLogin({ csrfToken, error }: Props) {
|
|
10
|
+
return (
|
|
11
|
+
<div className="min-h-screen flex items-center justify-center bg-gray-100 p-4">
|
|
12
|
+
<form
|
|
13
|
+
method="POST"
|
|
14
|
+
action="/account/login"
|
|
15
|
+
className="w-full max-w-sm rounded-2xl bg-white p-8 shadow-xl ring-1 ring-black/5"
|
|
16
|
+
>
|
|
17
|
+
<input type="hidden" name="_csrf" value={csrfToken} />
|
|
18
|
+
<div className="text-xs font-semibold uppercase tracking-[0.2em] text-gray-400">educ(a)ção</div>
|
|
19
|
+
<h1 className="mt-2 text-xl font-semibold text-gray-900">Minha conta</h1>
|
|
20
|
+
<p className="mt-1 text-sm text-gray-500">Gerencie seus tokens de acesso.</p>
|
|
21
|
+
|
|
22
|
+
{error && <p className="mt-4 text-sm text-red-600">{error}</p>}
|
|
23
|
+
|
|
24
|
+
<label htmlFor="email" className="mt-6 mb-1 block text-sm font-medium text-gray-700">
|
|
25
|
+
E-mail
|
|
26
|
+
</label>
|
|
27
|
+
<input
|
|
28
|
+
id="email"
|
|
29
|
+
name="email"
|
|
30
|
+
type="email"
|
|
31
|
+
required
|
|
32
|
+
autoFocus
|
|
33
|
+
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-gray-900"
|
|
34
|
+
/>
|
|
35
|
+
|
|
36
|
+
<label htmlFor="password" className="mt-4 mb-1 block text-sm font-medium text-gray-700">
|
|
37
|
+
Senha
|
|
38
|
+
</label>
|
|
39
|
+
<input
|
|
40
|
+
id="password"
|
|
41
|
+
name="password"
|
|
42
|
+
type="password"
|
|
43
|
+
required
|
|
44
|
+
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-gray-900"
|
|
45
|
+
/>
|
|
46
|
+
|
|
47
|
+
<button
|
|
48
|
+
type="submit"
|
|
49
|
+
className="mt-6 w-full rounded-lg bg-gray-900 py-2.5 text-sm font-semibold text-white transition hover:opacity-90"
|
|
50
|
+
>
|
|
51
|
+
Entrar
|
|
52
|
+
</button>
|
|
53
|
+
</form>
|
|
54
|
+
</div>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.makePath('inertia/pages/authkit/account/mfa.tsx') })
|
|
3
|
+
}}}
|
|
4
|
+
interface Props {
|
|
5
|
+
csrfToken: string
|
|
6
|
+
enabled: boolean
|
|
7
|
+
enrolling?: boolean
|
|
8
|
+
secret?: string | null
|
|
9
|
+
qrDataUrl?: string | null
|
|
10
|
+
error?: string
|
|
11
|
+
recoveryCodes?: string[] | null
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default function AccountMfa({
|
|
15
|
+
csrfToken,
|
|
16
|
+
enabled,
|
|
17
|
+
enrolling,
|
|
18
|
+
secret,
|
|
19
|
+
qrDataUrl,
|
|
20
|
+
error,
|
|
21
|
+
recoveryCodes,
|
|
22
|
+
}: Props) {
|
|
23
|
+
return (
|
|
24
|
+
<div className="min-h-screen bg-gray-100 p-4">
|
|
25
|
+
<div className="mx-auto max-w-2xl">
|
|
26
|
+
<div className="flex items-center justify-between py-6">
|
|
27
|
+
<div>
|
|
28
|
+
<div className="text-xs font-semibold uppercase tracking-[0.2em] text-gray-400">educ(a)ção</div>
|
|
29
|
+
<h1 className="text-xl font-semibold text-gray-900">Verificação em duas etapas</h1>
|
|
30
|
+
</div>
|
|
31
|
+
<form method="POST" action="/account/logout">
|
|
32
|
+
<input type="hidden" name="_csrf" value={csrfToken} />
|
|
33
|
+
<button type="submit" className="text-sm text-gray-500 hover:underline">
|
|
34
|
+
Sair
|
|
35
|
+
</button>
|
|
36
|
+
</form>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
{error && <p className="mb-4 text-sm text-red-600">{error}</p>}
|
|
40
|
+
|
|
41
|
+
{recoveryCodes && recoveryCodes.length > 0 && (
|
|
42
|
+
<div className="mb-6 rounded-lg border border-emerald-300 bg-emerald-50 p-4">
|
|
43
|
+
<p className="text-sm font-medium text-emerald-900">
|
|
44
|
+
Guarde seus códigos de recuperação — eles não serão mostrados de novo:
|
|
45
|
+
</p>
|
|
46
|
+
<ul className="mt-3 grid grid-cols-2 gap-2">
|
|
47
|
+
{recoveryCodes.map((rc) => (
|
|
48
|
+
<li key={rc}>
|
|
49
|
+
<code className="block rounded bg-white px-3 py-2 text-sm text-emerald-800 ring-1 ring-emerald-200">
|
|
50
|
+
{rc}
|
|
51
|
+
</code>
|
|
52
|
+
</li>
|
|
53
|
+
))}
|
|
54
|
+
</ul>
|
|
55
|
+
</div>
|
|
56
|
+
)}
|
|
57
|
+
|
|
58
|
+
{enrolling ? (
|
|
59
|
+
<div className="rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
|
|
60
|
+
<p className="text-sm text-gray-600">
|
|
61
|
+
Escaneie o QR code com seu app autenticador (Google Authenticator, 1Password, etc.).
|
|
62
|
+
</p>
|
|
63
|
+
{qrDataUrl && (
|
|
64
|
+
<img src={qrDataUrl} alt="QR code TOTP" className="mx-auto my-4 h-48 w-48" />
|
|
65
|
+
)}
|
|
66
|
+
{secret && (
|
|
67
|
+
<>
|
|
68
|
+
<p className="text-center text-xs text-gray-500">Ou informe manualmente:</p>
|
|
69
|
+
<code className="mx-auto mt-1 block w-fit break-all rounded bg-gray-100 px-3 py-2 text-sm text-gray-800">
|
|
70
|
+
{secret}
|
|
71
|
+
</code>
|
|
72
|
+
</>
|
|
73
|
+
)}
|
|
74
|
+
<form method="POST" action="/account/mfa/confirm" className="mt-6">
|
|
75
|
+
<input type="hidden" name="_csrf" value={csrfToken} />
|
|
76
|
+
<label htmlFor="code" className="mb-1 block text-sm font-medium text-gray-700">
|
|
77
|
+
Código de confirmação
|
|
78
|
+
</label>
|
|
79
|
+
<input
|
|
80
|
+
id="code"
|
|
81
|
+
name="code"
|
|
82
|
+
inputMode="numeric"
|
|
83
|
+
pattern="[0-9]*"
|
|
84
|
+
maxLength={6}
|
|
85
|
+
autoFocus
|
|
86
|
+
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-center text-lg tracking-[0.4em] outline-none focus:border-gray-900"
|
|
87
|
+
/>
|
|
88
|
+
<button
|
|
89
|
+
type="submit"
|
|
90
|
+
className="mt-4 w-full rounded-lg bg-gray-900 py-2.5 text-sm font-semibold text-white"
|
|
91
|
+
>
|
|
92
|
+
Ativar verificação em duas etapas
|
|
93
|
+
</button>
|
|
94
|
+
</form>
|
|
95
|
+
</div>
|
|
96
|
+
) : enabled ? (
|
|
97
|
+
<div className="rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
|
|
98
|
+
<p className="text-sm text-gray-700">
|
|
99
|
+
A verificação em duas etapas está{' '}
|
|
100
|
+
<span className="font-semibold text-emerald-700">ativa</span> nesta conta.
|
|
101
|
+
</p>
|
|
102
|
+
<form method="POST" action="/account/mfa/disable" className="mt-4">
|
|
103
|
+
<input type="hidden" name="_csrf" value={csrfToken} />
|
|
104
|
+
<button
|
|
105
|
+
type="submit"
|
|
106
|
+
className="rounded-lg border border-red-300 px-4 py-2 text-sm font-semibold text-red-600 hover:bg-red-50"
|
|
107
|
+
>
|
|
108
|
+
Desativar
|
|
109
|
+
</button>
|
|
110
|
+
</form>
|
|
111
|
+
</div>
|
|
112
|
+
) : (
|
|
113
|
+
<div className="rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
|
|
114
|
+
<p className="text-sm text-gray-700">
|
|
115
|
+
A verificação em duas etapas está desativada. Ative-a para proteger sua conta com um app
|
|
116
|
+
autenticador.
|
|
117
|
+
</p>
|
|
118
|
+
<form method="POST" action="/account/mfa/enroll" className="mt-4">
|
|
119
|
+
<input type="hidden" name="_csrf" value={csrfToken} />
|
|
120
|
+
<button
|
|
121
|
+
type="submit"
|
|
122
|
+
className="rounded-lg bg-gray-900 px-4 py-2 text-sm font-semibold text-white"
|
|
123
|
+
>
|
|
124
|
+
Ativar verificação em duas etapas
|
|
125
|
+
</button>
|
|
126
|
+
</form>
|
|
127
|
+
</div>
|
|
128
|
+
)}
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.makePath('inertia/pages/authkit/account/tokens.tsx') })
|
|
3
|
+
}}}
|
|
4
|
+
interface TokenRow {
|
|
5
|
+
id: string
|
|
6
|
+
name: string
|
|
7
|
+
scopes: string[]
|
|
8
|
+
audience: string | null
|
|
9
|
+
lastUsedAt: string | null
|
|
10
|
+
createdAt: string
|
|
11
|
+
}
|
|
12
|
+
interface Props {
|
|
13
|
+
csrfToken: string
|
|
14
|
+
createdToken: string | null
|
|
15
|
+
tokens: TokenRow[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default function AccountTokens({ csrfToken, createdToken, tokens }: Props) {
|
|
19
|
+
return (
|
|
20
|
+
<div className="min-h-screen bg-gray-100 p-4">
|
|
21
|
+
<div className="mx-auto max-w-2xl">
|
|
22
|
+
<div className="flex items-center justify-between py-6">
|
|
23
|
+
<div>
|
|
24
|
+
<div className="text-xs font-semibold uppercase tracking-[0.2em] text-gray-400">educ(a)ção</div>
|
|
25
|
+
<h1 className="text-xl font-semibold text-gray-900">Tokens de acesso</h1>
|
|
26
|
+
</div>
|
|
27
|
+
<form method="POST" action="/account/logout">
|
|
28
|
+
<input type="hidden" name="_csrf" value={csrfToken} />
|
|
29
|
+
<button type="submit" className="text-sm text-gray-500 hover:underline">
|
|
30
|
+
Sair
|
|
31
|
+
</button>
|
|
32
|
+
</form>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
{createdToken && (
|
|
36
|
+
<div className="mb-6 rounded-lg border border-emerald-300 bg-emerald-50 p-4">
|
|
37
|
+
<p className="text-sm font-medium text-emerald-900">
|
|
38
|
+
Token criado — copie agora, não será mostrado de novo:
|
|
39
|
+
</p>
|
|
40
|
+
<code className="mt-2 block break-all rounded bg-white px-3 py-2 text-sm text-emerald-800 ring-1 ring-emerald-200">
|
|
41
|
+
{createdToken}
|
|
42
|
+
</code>
|
|
43
|
+
</div>
|
|
44
|
+
)}
|
|
45
|
+
|
|
46
|
+
<form
|
|
47
|
+
method="POST"
|
|
48
|
+
action="/account/tokens"
|
|
49
|
+
className="mb-6 flex gap-2 rounded-xl bg-white p-4 shadow-sm ring-1 ring-black/5"
|
|
50
|
+
>
|
|
51
|
+
<input type="hidden" name="_csrf" value={csrfToken} />
|
|
52
|
+
<input
|
|
53
|
+
name="name"
|
|
54
|
+
placeholder="Nome do token (ex.: CI deploy)"
|
|
55
|
+
className="flex-1 rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-gray-900"
|
|
56
|
+
/>
|
|
57
|
+
<button type="submit" className="rounded-lg bg-gray-900 px-4 text-sm font-semibold text-white">
|
|
58
|
+
Criar
|
|
59
|
+
</button>
|
|
60
|
+
</form>
|
|
61
|
+
|
|
62
|
+
<div className="overflow-hidden rounded-xl bg-white shadow-sm ring-1 ring-black/5">
|
|
63
|
+
{tokens.length === 0 ? (
|
|
64
|
+
<p className="p-6 text-sm text-gray-500">Nenhum token ainda.</p>
|
|
65
|
+
) : (
|
|
66
|
+
tokens.map((t) => (
|
|
67
|
+
<div key={t.id} className="flex items-center justify-between border-b border-gray-100 p-4 last:border-0">
|
|
68
|
+
<div>
|
|
69
|
+
<p className="text-sm font-medium text-gray-900">{t.name}</p>
|
|
70
|
+
<p className="text-xs text-gray-500">
|
|
71
|
+
Criado em {new Date(t.createdAt).toLocaleDateString('pt-BR')}
|
|
72
|
+
{t.lastUsedAt ? ` · último uso ${new Date(t.lastUsedAt).toLocaleDateString('pt-BR')}` : ' · nunca usado'}
|
|
73
|
+
</p>
|
|
74
|
+
</div>
|
|
75
|
+
<form method="POST" action={`/account/tokens/${t.id}/revoke`}>
|
|
76
|
+
<input type="hidden" name="_csrf" value={csrfToken} />
|
|
77
|
+
<button type="submit" className="text-sm text-red-600 hover:underline">
|
|
78
|
+
Revogar
|
|
79
|
+
</button>
|
|
80
|
+
</form>
|
|
81
|
+
</div>
|
|
82
|
+
))
|
|
83
|
+
)}
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.makePath('inertia/pages/authkit/consent.tsx') })
|
|
3
|
+
}}}
|
|
4
|
+
import AuthShell, { type AuthBrand } from '../../components/auth_shell'
|
|
5
|
+
|
|
6
|
+
export default function AuthkitConsent({
|
|
7
|
+
uid,
|
|
8
|
+
params,
|
|
9
|
+
csrfToken,
|
|
10
|
+
brand,
|
|
11
|
+
}: {
|
|
12
|
+
uid: string
|
|
13
|
+
params: { client_id: string }
|
|
14
|
+
csrfToken: string
|
|
15
|
+
brand?: AuthBrand
|
|
16
|
+
}) {
|
|
17
|
+
const accent = brand?.accent ?? '#111827'
|
|
18
|
+
const appName = brand?.appName ?? params.client_id
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<AuthShell brand={brand}>
|
|
22
|
+
<form method="POST" action={'/auth/interaction/' + uid + '/consent'}>
|
|
23
|
+
<input type="hidden" name="_csrf" value={csrfToken} />
|
|
24
|
+
<h1 className="text-xl font-semibold text-gray-900">Autorizar acesso</h1>
|
|
25
|
+
<p className="mt-2 text-sm text-gray-600">
|
|
26
|
+
O app <strong>{appName}</strong> quer acessar sua conta.
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
<button
|
|
30
|
+
type="submit"
|
|
31
|
+
className="mt-6 w-full rounded-lg py-2.5 text-sm font-semibold text-white transition hover:opacity-90"
|
|
32
|
+
style={{ backgroundColor: accent }}
|
|
33
|
+
>
|
|
34
|
+
Autorizar
|
|
35
|
+
</button>
|
|
36
|
+
</form>
|
|
37
|
+
</AuthShell>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.makePath('inertia/pages/authkit/forgot.tsx') })
|
|
3
|
+
}}}
|
|
4
|
+
import AuthShell from '../../components/auth_shell'
|
|
5
|
+
|
|
6
|
+
const inputClass =
|
|
7
|
+
'w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none transition focus:border-transparent focus:ring-2 focus:ring-gray-800'
|
|
8
|
+
|
|
9
|
+
export default function AuthkitForgot({ csrfToken, sent }: { csrfToken: string; sent?: boolean }) {
|
|
10
|
+
if (sent) {
|
|
11
|
+
return (
|
|
12
|
+
<AuthShell>
|
|
13
|
+
<h1 className="text-xl font-semibold text-gray-900">E-mail enviado</h1>
|
|
14
|
+
<p className="mt-2 text-sm text-gray-600">
|
|
15
|
+
Se o e-mail existir, enviaremos instruções de redefinição.
|
|
16
|
+
</p>
|
|
17
|
+
</AuthShell>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<AuthShell>
|
|
23
|
+
<form method="POST" action="/auth/forgot-password">
|
|
24
|
+
<input type="hidden" name="_csrf" value={csrfToken} />
|
|
25
|
+
<h1 className="text-xl font-semibold text-gray-900">Recuperar senha</h1>
|
|
26
|
+
<p className="mt-1 text-sm text-gray-500">Enviaremos um link para redefinir sua senha.</p>
|
|
27
|
+
|
|
28
|
+
<div className="mt-6">
|
|
29
|
+
<label htmlFor="email" className="mb-1 block text-sm font-medium text-gray-700">
|
|
30
|
+
E-mail
|
|
31
|
+
</label>
|
|
32
|
+
<input id="email" name="email" type="email" required className={inputClass} />
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<button
|
|
36
|
+
type="submit"
|
|
37
|
+
className="mt-6 w-full rounded-lg bg-gray-900 py-2.5 text-sm font-semibold text-white transition hover:opacity-90"
|
|
38
|
+
>
|
|
39
|
+
Enviar link
|
|
40
|
+
</button>
|
|
41
|
+
</form>
|
|
42
|
+
</AuthShell>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.makePath('inertia/pages/authkit/login.tsx') })
|
|
3
|
+
}}}
|
|
4
|
+
import AuthShell, { type AuthBrand } from '../../components/auth_shell'
|
|
5
|
+
|
|
6
|
+
const inputClass =
|
|
7
|
+
'w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none transition focus:border-transparent focus:ring-2'
|
|
8
|
+
|
|
9
|
+
export default function AuthkitLogin({
|
|
10
|
+
uid,
|
|
11
|
+
csrfToken,
|
|
12
|
+
step = 'identifier',
|
|
13
|
+
email,
|
|
14
|
+
account,
|
|
15
|
+
error,
|
|
16
|
+
brand,
|
|
17
|
+
}: {
|
|
18
|
+
uid: string
|
|
19
|
+
csrfToken: string
|
|
20
|
+
step?: 'identifier' | 'password'
|
|
21
|
+
email?: string
|
|
22
|
+
account?: { fullName: string | null; globalRoles: string[] } | null
|
|
23
|
+
error?: string
|
|
24
|
+
brand?: AuthBrand
|
|
25
|
+
}) {
|
|
26
|
+
const accent = brand?.accent ?? '#111827'
|
|
27
|
+
const focusStyle = { ['--tw-ring-color' as any]: accent }
|
|
28
|
+
|
|
29
|
+
if (step === 'identifier') {
|
|
30
|
+
return (
|
|
31
|
+
<AuthShell brand={brand}>
|
|
32
|
+
<form method="POST" action={'/auth/interaction/' + uid + '/identifier'}>
|
|
33
|
+
<input type="hidden" name="_csrf" value={csrfToken} />
|
|
34
|
+
<h1 className="text-xl font-semibold text-gray-900">Entrar</h1>
|
|
35
|
+
<p className="mt-1 text-sm text-gray-500">Informe seu e-mail para continuar.</p>
|
|
36
|
+
|
|
37
|
+
{error && <p className="mt-4 text-sm text-red-600">{error}</p>}
|
|
38
|
+
|
|
39
|
+
<div className="mt-6">
|
|
40
|
+
<label htmlFor="email" className="mb-1 block text-sm font-medium text-gray-700">
|
|
41
|
+
E-mail
|
|
42
|
+
</label>
|
|
43
|
+
<input
|
|
44
|
+
id="email"
|
|
45
|
+
name="email"
|
|
46
|
+
type="email"
|
|
47
|
+
required
|
|
48
|
+
autoFocus
|
|
49
|
+
className={inputClass}
|
|
50
|
+
style={focusStyle}
|
|
51
|
+
/>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<button
|
|
55
|
+
type="submit"
|
|
56
|
+
className="mt-6 w-full rounded-lg py-2.5 text-sm font-semibold text-white transition hover:opacity-90"
|
|
57
|
+
style={{ backgroundColor: accent }}
|
|
58
|
+
>
|
|
59
|
+
Continuar
|
|
60
|
+
</button>
|
|
61
|
+
|
|
62
|
+
<div className="mt-4 flex justify-between text-sm text-gray-600">
|
|
63
|
+
<a href={'/auth/interaction/' + uid + '/signup'} className="hover:underline">
|
|
64
|
+
Criar conta
|
|
65
|
+
</a>
|
|
66
|
+
<a href="/auth/forgot-password" className="hover:underline">
|
|
67
|
+
Esqueci a senha
|
|
68
|
+
</a>
|
|
69
|
+
</div>
|
|
70
|
+
</form>
|
|
71
|
+
|
|
72
|
+
<div className="my-6 flex items-center gap-3 text-xs text-gray-400">
|
|
73
|
+
<span className="h-px flex-1 bg-gray-200" />
|
|
74
|
+
ou
|
|
75
|
+
<span className="h-px flex-1 bg-gray-200" />
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<a
|
|
79
|
+
href={'/auth/google/redirect/' + uid}
|
|
80
|
+
className="flex w-full items-center justify-center gap-2 rounded-lg border border-gray-300 bg-white py-2.5 text-sm font-medium text-gray-700 transition hover:bg-gray-50"
|
|
81
|
+
>
|
|
82
|
+
<svg className="h-4 w-4" viewBox="0 0 24 24" aria-hidden="true">
|
|
83
|
+
<path
|
|
84
|
+
fill="#4285F4"
|
|
85
|
+
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.27-4.74 3.27-8.1Z"
|
|
86
|
+
/>
|
|
87
|
+
<path
|
|
88
|
+
fill="#34A853"
|
|
89
|
+
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84A11 11 0 0 0 12 23Z"
|
|
90
|
+
/>
|
|
91
|
+
<path
|
|
92
|
+
fill="#FBBC05"
|
|
93
|
+
d="M5.84 14.1a6.6 6.6 0 0 1 0-4.2V7.06H2.18a11 11 0 0 0 0 9.88l3.66-2.84Z"
|
|
94
|
+
/>
|
|
95
|
+
<path
|
|
96
|
+
fill="#EA4335"
|
|
97
|
+
d="M12 4.75c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 1.46 14.97.5 12 .5A11 11 0 0 0 2.18 7.06l3.66 2.84C6.71 7.3 9.14 4.75 12 4.75Z"
|
|
98
|
+
/>
|
|
99
|
+
</svg>
|
|
100
|
+
Entrar com Google
|
|
101
|
+
</a>
|
|
102
|
+
</AuthShell>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// step === 'password'
|
|
107
|
+
const isAdmin = account?.globalRoles?.includes('ADMIN') ?? false
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<AuthShell brand={brand}>
|
|
111
|
+
<form method="POST" action={'/auth/interaction/' + uid + '/login'}>
|
|
112
|
+
<input type="hidden" name="_csrf" value={csrfToken} />
|
|
113
|
+
|
|
114
|
+
{account?.fullName ? (
|
|
115
|
+
<h1 className="text-xl font-semibold text-gray-900">
|
|
116
|
+
Olá, {account.fullName}
|
|
117
|
+
{isAdmin && (
|
|
118
|
+
<span className="ml-2 inline-block rounded bg-amber-100 px-1.5 py-0.5 align-middle text-xs font-semibold uppercase text-amber-800">
|
|
119
|
+
ADMIN
|
|
120
|
+
</span>
|
|
121
|
+
)}
|
|
122
|
+
</h1>
|
|
123
|
+
) : (
|
|
124
|
+
<h1 className="text-xl font-semibold text-gray-900">Entrar</h1>
|
|
125
|
+
)}
|
|
126
|
+
|
|
127
|
+
<div className="mt-1 flex items-center gap-2">
|
|
128
|
+
<span className="text-sm text-gray-500">{email}</span>
|
|
129
|
+
<a
|
|
130
|
+
href={'/auth/interaction/' + uid + '/switch'}
|
|
131
|
+
className="text-xs font-medium hover:underline"
|
|
132
|
+
style={{ color: accent }}
|
|
133
|
+
>
|
|
134
|
+
Trocar de conta
|
|
135
|
+
</a>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
{error && <p className="mt-4 text-sm text-red-600">{error}</p>}
|
|
139
|
+
|
|
140
|
+
<div className="mt-6">
|
|
141
|
+
<label htmlFor="password" className="mb-1 block text-sm font-medium text-gray-700">
|
|
142
|
+
Senha
|
|
143
|
+
</label>
|
|
144
|
+
<input
|
|
145
|
+
id="password"
|
|
146
|
+
name="password"
|
|
147
|
+
type="password"
|
|
148
|
+
required
|
|
149
|
+
autoFocus
|
|
150
|
+
className={inputClass}
|
|
151
|
+
style={focusStyle}
|
|
152
|
+
/>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<button
|
|
156
|
+
type="submit"
|
|
157
|
+
className="mt-6 w-full rounded-lg py-2.5 text-sm font-semibold text-white transition hover:opacity-90"
|
|
158
|
+
style={{ backgroundColor: accent }}
|
|
159
|
+
>
|
|
160
|
+
Entrar
|
|
161
|
+
</button>
|
|
162
|
+
|
|
163
|
+
<div className="mt-4 flex justify-end text-sm text-gray-600">
|
|
164
|
+
<a href="/auth/forgot-password" className="hover:underline">
|
|
165
|
+
Esqueci a senha
|
|
166
|
+
</a>
|
|
167
|
+
</div>
|
|
168
|
+
</form>
|
|
169
|
+
</AuthShell>
|
|
170
|
+
)
|
|
171
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.makePath('inertia/pages/authkit/mfa-challenge.tsx') })
|
|
3
|
+
}}}
|
|
4
|
+
import AuthShell from '../components/auth_shell'
|
|
5
|
+
|
|
6
|
+
const inputClass =
|
|
7
|
+
'w-full rounded-lg border border-gray-300 px-3 py-2 text-center text-lg tracking-[0.4em] outline-none transition focus:border-transparent focus:ring-2 focus:ring-gray-800'
|
|
8
|
+
|
|
9
|
+
export default function AuthkitMfaChallenge({
|
|
10
|
+
uid,
|
|
11
|
+
csrfToken,
|
|
12
|
+
error,
|
|
13
|
+
}: {
|
|
14
|
+
uid: string
|
|
15
|
+
csrfToken: string
|
|
16
|
+
error?: string
|
|
17
|
+
}) {
|
|
18
|
+
return (
|
|
19
|
+
<AuthShell>
|
|
20
|
+
<form method="POST" action={`/auth/interaction/${uid}/mfa`}>
|
|
21
|
+
<input type="hidden" name="_csrf" value={csrfToken} />
|
|
22
|
+
<h1 className="text-xl font-semibold text-gray-900">Verificação em duas etapas</h1>
|
|
23
|
+
<p className="mt-1 text-sm text-gray-500">
|
|
24
|
+
Abra seu app autenticador e informe o código de 6 dígitos.
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
{error && <p className="mt-4 text-sm text-red-600">{error}</p>}
|
|
28
|
+
|
|
29
|
+
<div className="mt-6">
|
|
30
|
+
<label htmlFor="code" className="mb-1 block text-sm font-medium text-gray-700">
|
|
31
|
+
Código
|
|
32
|
+
</label>
|
|
33
|
+
<input
|
|
34
|
+
id="code"
|
|
35
|
+
name="code"
|
|
36
|
+
inputMode="numeric"
|
|
37
|
+
autoComplete="one-time-code"
|
|
38
|
+
pattern="[0-9]*"
|
|
39
|
+
maxLength={6}
|
|
40
|
+
autoFocus
|
|
41
|
+
className={inputClass}
|
|
42
|
+
/>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<button
|
|
46
|
+
type="submit"
|
|
47
|
+
className="mt-6 w-full rounded-lg bg-gray-900 py-2.5 text-sm font-semibold text-white transition hover:opacity-90"
|
|
48
|
+
>
|
|
49
|
+
Verificar
|
|
50
|
+
</button>
|
|
51
|
+
</form>
|
|
52
|
+
|
|
53
|
+
<details className="mt-6 text-sm text-gray-600">
|
|
54
|
+
<summary className="cursor-pointer hover:underline">Usar um código de recuperação</summary>
|
|
55
|
+
<form method="POST" action={`/auth/interaction/${uid}/mfa`} className="mt-3">
|
|
56
|
+
<input type="hidden" name="_csrf" value={csrfToken} />
|
|
57
|
+
<input
|
|
58
|
+
name="recoveryCode"
|
|
59
|
+
placeholder="xxxxx-xxxxx"
|
|
60
|
+
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-gray-900"
|
|
61
|
+
/>
|
|
62
|
+
<button
|
|
63
|
+
type="submit"
|
|
64
|
+
className="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"
|
|
65
|
+
>
|
|
66
|
+
Entrar com código de recuperação
|
|
67
|
+
</button>
|
|
68
|
+
</form>
|
|
69
|
+
</details>
|
|
70
|
+
</AuthShell>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.makePath('inertia/pages/authkit/reset.tsx') })
|
|
3
|
+
}}}
|
|
4
|
+
import AuthShell from '../../components/auth_shell'
|
|
5
|
+
|
|
6
|
+
const inputClass =
|
|
7
|
+
'w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none transition focus:border-transparent focus:ring-2 focus:ring-gray-800'
|
|
8
|
+
|
|
9
|
+
export default function AuthkitReset({
|
|
10
|
+
token,
|
|
11
|
+
csrfToken,
|
|
12
|
+
done,
|
|
13
|
+
}: {
|
|
14
|
+
token: string
|
|
15
|
+
csrfToken: string
|
|
16
|
+
done?: boolean
|
|
17
|
+
}) {
|
|
18
|
+
if (done) {
|
|
19
|
+
return (
|
|
20
|
+
<AuthShell>
|
|
21
|
+
<h1 className="text-xl font-semibold text-gray-900">Senha redefinida</h1>
|
|
22
|
+
<p className="mt-2 text-sm text-gray-600">Você já pode entrar com a nova senha.</p>
|
|
23
|
+
</AuthShell>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<AuthShell>
|
|
29
|
+
<form method="POST" action="/auth/reset-password">
|
|
30
|
+
<input type="hidden" name="_csrf" value={csrfToken} />
|
|
31
|
+
<input type="hidden" name="token" value={token} />
|
|
32
|
+
<h1 className="text-xl font-semibold text-gray-900">Nova senha</h1>
|
|
33
|
+
<p className="mt-1 text-sm text-gray-500">Escolha uma nova senha para sua conta.</p>
|
|
34
|
+
|
|
35
|
+
<div className="mt-6">
|
|
36
|
+
<label htmlFor="password" className="mb-1 block text-sm font-medium text-gray-700">
|
|
37
|
+
Senha
|
|
38
|
+
</label>
|
|
39
|
+
<input
|
|
40
|
+
id="password"
|
|
41
|
+
name="password"
|
|
42
|
+
type="password"
|
|
43
|
+
required
|
|
44
|
+
minLength={8}
|
|
45
|
+
className={inputClass}
|
|
46
|
+
/>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<button
|
|
50
|
+
type="submit"
|
|
51
|
+
className="mt-6 w-full rounded-lg bg-gray-900 py-2.5 text-sm font-semibold text-white transition hover:opacity-90"
|
|
52
|
+
>
|
|
53
|
+
Redefinir
|
|
54
|
+
</button>
|
|
55
|
+
</form>
|
|
56
|
+
</AuthShell>
|
|
57
|
+
)
|
|
58
|
+
}
|