@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,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
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.makePath('inertia/pages/authkit/signup.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 AuthkitSignup({
|
|
10
|
+
uid,
|
|
11
|
+
csrfToken,
|
|
12
|
+
error,
|
|
13
|
+
brand,
|
|
14
|
+
}: {
|
|
15
|
+
uid: string
|
|
16
|
+
csrfToken: string
|
|
17
|
+
error?: string
|
|
18
|
+
brand?: AuthBrand
|
|
19
|
+
}) {
|
|
20
|
+
const accent = brand?.accent ?? '#111827'
|
|
21
|
+
const focusStyle = { ['--tw-ring-color' as any]: accent }
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<AuthShell brand={brand}>
|
|
25
|
+
<form method="POST" action={'/auth/interaction/' + uid + '/signup'}>
|
|
26
|
+
<input type="hidden" name="_csrf" value={csrfToken} />
|
|
27
|
+
<h1 className="text-xl font-semibold text-gray-900">Criar conta</h1>
|
|
28
|
+
<p className="mt-1 text-sm text-gray-500">Preencha seus dados para começar.</p>
|
|
29
|
+
|
|
30
|
+
{error && <p className="mt-4 text-sm text-red-600">{error}</p>}
|
|
31
|
+
|
|
32
|
+
<div className="mt-6">
|
|
33
|
+
<label htmlFor="fullName" className="mb-1 block text-sm font-medium text-gray-700">
|
|
34
|
+
Nome
|
|
35
|
+
</label>
|
|
36
|
+
<input id="fullName" name="fullName" type="text" required className={inputClass} style={focusStyle} />
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div className="mt-4">
|
|
40
|
+
<label htmlFor="email" className="mb-1 block text-sm font-medium text-gray-700">
|
|
41
|
+
E-mail
|
|
42
|
+
</label>
|
|
43
|
+
<input id="email" name="email" type="email" required className={inputClass} style={focusStyle} />
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div className="mt-4">
|
|
47
|
+
<label htmlFor="password" className="mb-1 block text-sm font-medium text-gray-700">
|
|
48
|
+
Senha
|
|
49
|
+
</label>
|
|
50
|
+
<input
|
|
51
|
+
id="password"
|
|
52
|
+
name="password"
|
|
53
|
+
type="password"
|
|
54
|
+
required
|
|
55
|
+
minLength={8}
|
|
56
|
+
className={inputClass}
|
|
57
|
+
style={focusStyle}
|
|
58
|
+
/>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<button
|
|
62
|
+
type="submit"
|
|
63
|
+
className="mt-6 w-full rounded-lg py-2.5 text-sm font-semibold text-white transition hover:opacity-90"
|
|
64
|
+
style={{ backgroundColor: accent }}
|
|
65
|
+
>
|
|
66
|
+
Criar conta
|
|
67
|
+
</button>
|
|
68
|
+
|
|
69
|
+
<a
|
|
70
|
+
href={'/auth/interaction/' + uid}
|
|
71
|
+
className="mt-4 block text-center text-sm text-gray-600 hover:underline"
|
|
72
|
+
>
|
|
73
|
+
Já tenho conta
|
|
74
|
+
</a>
|
|
75
|
+
</form>
|
|
76
|
+
</AuthShell>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.makePath('inertia/pages/authkit/verify-email.tsx') })
|
|
3
|
+
}}}
|
|
4
|
+
import AuthShell from '../../components/auth_shell'
|
|
5
|
+
|
|
6
|
+
export default function AuthkitVerifyEmail({ verified }: { verified?: boolean }) {
|
|
7
|
+
if (verified) {
|
|
8
|
+
return (
|
|
9
|
+
<AuthShell>
|
|
10
|
+
<h1 className="text-xl font-semibold text-gray-900">E-mail verificado</h1>
|
|
11
|
+
<p className="mt-2 text-sm text-gray-600">Seu e-mail foi confirmado com sucesso.</p>
|
|
12
|
+
</AuthShell>
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<AuthShell>
|
|
18
|
+
<h1 className="text-xl font-semibold text-gray-900">Link inválido</h1>
|
|
19
|
+
<p className="mt-2 text-sm text-gray-600">
|
|
20
|
+
O link de verificação é inválido ou já foi utilizado.
|
|
21
|
+
</p>
|
|
22
|
+
</AuthShell>
|
|
23
|
+
)
|
|
24
|
+
}
|
package/build/types.d.ts
ADDED
package/build/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dudousxd/adonis-authkit-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AdonisJS OIDC/OAuth2 provider (Identity Provider) toolkit: ejectable auth server with sessions, rate-limiting, MFA/TOTP, audit log, federated logout and OpenTelemetry metrics.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "dudousxd",
|
|
7
|
+
"homepage": "https://github.com/DavideCarvalho/streaming-educacao/tree/main/packages/authkit-server#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/DavideCarvalho/streaming-educacao.git",
|
|
11
|
+
"directory": "packages/authkit-server"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/DavideCarvalho/streaming-educacao/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"adonisjs",
|
|
18
|
+
"oidc",
|
|
19
|
+
"oauth2",
|
|
20
|
+
"openid-connect",
|
|
21
|
+
"authentication",
|
|
22
|
+
"authkit",
|
|
23
|
+
"identity-provider",
|
|
24
|
+
"rate-limiting",
|
|
25
|
+
"mfa",
|
|
26
|
+
"totp"
|
|
27
|
+
],
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"type": "module",
|
|
32
|
+
"main": "./build/index.js",
|
|
33
|
+
"types": "./build/index.d.ts",
|
|
34
|
+
"files": [
|
|
35
|
+
"build",
|
|
36
|
+
"stubs"
|
|
37
|
+
],
|
|
38
|
+
"exports": {
|
|
39
|
+
".": "./build/index.js",
|
|
40
|
+
"./types": "./build/index.js",
|
|
41
|
+
"./authkit_server_provider": "./build/providers/authkit_server_provider.js",
|
|
42
|
+
"./commands": "./build/commands/main.js",
|
|
43
|
+
"./commands/configure": "./build/commands/configure.js"
|
|
44
|
+
},
|
|
45
|
+
"adonisjs": {
|
|
46
|
+
"types": "@dudousxd/adonis-authkit-server",
|
|
47
|
+
"providers": [
|
|
48
|
+
"@dudousxd/adonis-authkit-server/authkit_server_provider"
|
|
49
|
+
],
|
|
50
|
+
"commands": [
|
|
51
|
+
"@dudousxd/adonis-authkit-server/commands"
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"@adonisjs/ally": "6.3.0",
|
|
56
|
+
"@adonisjs/core": "7.3.3",
|
|
57
|
+
"@adonisjs/lucid": "22.4.2",
|
|
58
|
+
"@adonisjs/session": "8.1.0",
|
|
59
|
+
"@adonisjs/shield": "9.0.0",
|
|
60
|
+
"edge.js": "6.5.1"
|
|
61
|
+
},
|
|
62
|
+
"peerDependenciesMeta": {
|
|
63
|
+
"edge.js": {
|
|
64
|
+
"optional": true
|
|
65
|
+
},
|
|
66
|
+
"@adonisjs/ally": {
|
|
67
|
+
"optional": true
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"dependencies": {
|
|
71
|
+
"@simplewebauthn/server": "^13.3.1",
|
|
72
|
+
"@vinejs/vine": "4.4.0",
|
|
73
|
+
"jose": "^5.9.0",
|
|
74
|
+
"koa": "^3.2.1",
|
|
75
|
+
"koa-mount": "^4.2.0",
|
|
76
|
+
"oidc-provider": "^9.8.4",
|
|
77
|
+
"otplib": "^12.0.1",
|
|
78
|
+
"qrcode": "^1.5.4",
|
|
79
|
+
"@dudousxd/adonis-authkit-core": "0.1.0"
|
|
80
|
+
},
|
|
81
|
+
"devDependencies": {
|
|
82
|
+
"@adonisjs/ally": "6.3.0",
|
|
83
|
+
"@adonisjs/core": "7.3.3",
|
|
84
|
+
"@adonisjs/lucid": "22.4.2",
|
|
85
|
+
"@adonisjs/session": "8.1.0",
|
|
86
|
+
"@adonisjs/shield": "9.0.0",
|
|
87
|
+
"@adonisjs/tsconfig": "2.0.0",
|
|
88
|
+
"@japa/assert": "4.2.0",
|
|
89
|
+
"@japa/runner": "5.3.0",
|
|
90
|
+
"@poppinss/ts-exec": "1.4.4",
|
|
91
|
+
"@types/koa": "^2.15.2",
|
|
92
|
+
"@types/koa-mount": "^4.0.5",
|
|
93
|
+
"@types/luxon": "3.7.1",
|
|
94
|
+
"@types/node": "25.9.1",
|
|
95
|
+
"@types/qrcode": "^1.5.5",
|
|
96
|
+
"better-sqlite3": "^11.0.0",
|
|
97
|
+
"edge.js": "6.5.1",
|
|
98
|
+
"ioredis": "5.4.2",
|
|
99
|
+
"ioredis-mock": "^8.9.0",
|
|
100
|
+
"luxon": "3.7.2",
|
|
101
|
+
"typescript": "6.0.3"
|
|
102
|
+
},
|
|
103
|
+
"scripts": {
|
|
104
|
+
"build": "tsc && node -e \"require('node:fs').cpSync('stubs','build/stubs',{recursive:true,filter:(s)=>!s.endsWith('.ts')})\" && node -e \"const fs=require('node:fs');if(fs.existsSync('assets'))fs.cpSync('assets','build/assets',{recursive:true})\" && node -e \"require('node:fs').cpSync('src/host/views','build/host/views',{recursive:true})\" && node -e \"require('node:fs').copyFileSync('commands/commands.json','build/commands/commands.json')\"",
|
|
105
|
+
"typecheck": "tsc --noEmit",
|
|
106
|
+
"test": "node --import=@poppinss/ts-exec bin/test.ts"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.configPath('authkit.ts') })
|
|
3
|
+
}}}
|
|
4
|
+
import env from '#start/env'
|
|
5
|
+
import AuthUser from '#models/auth_user'
|
|
6
|
+
import { defineConfig, adapters } from '@authkit/server'
|
|
7
|
+
|
|
8
|
+
const authServerConfig = defineConfig({
|
|
9
|
+
issuer: env.get('AUTHKIT_ISSUER'),
|
|
10
|
+
adapter: adapters.redis({ connection: 'main' }),
|
|
11
|
+
jwks: { source: 'managed', algorithm: 'RS256' },
|
|
12
|
+
clients: [
|
|
13
|
+
// registre seus clients OIDC aqui
|
|
14
|
+
],
|
|
15
|
+
ttl: { accessToken: '15m', refreshToken: '30d' },
|
|
16
|
+
globalRolesClaim: 'roles',
|
|
17
|
+
findAccount: async (sub) => {
|
|
18
|
+
const user = await AuthUser.find(sub)
|
|
19
|
+
if (!user) return null
|
|
20
|
+
return { id: user.id, email: user.email, globalRoles: user.globalRoles }
|
|
21
|
+
},
|
|
22
|
+
verifyCredentials: async (email, password) => {
|
|
23
|
+
const user = await AuthUser.query().where('email', email).first()
|
|
24
|
+
if (!user || !(await user.verifyPassword(password))) return null
|
|
25
|
+
return { id: user.id }
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
export default authServerConfig
|
package/stubs/main.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.makePath('app/models/auth_user.ts') })
|
|
3
|
+
}}}
|
|
4
|
+
import { BaseModel, column } from '@adonisjs/lucid/orm'
|
|
5
|
+
import { compose } from '@adonisjs/core/helpers'
|
|
6
|
+
import { withAuthUser, withCredentials } from '@authkit/server'
|
|
7
|
+
|
|
8
|
+
export default class AuthUser extends compose(BaseModel, withAuthUser(), withCredentials()) {
|
|
9
|
+
static connection = 'auth'
|
|
10
|
+
|
|
11
|
+
@column({ isPrimary: true })
|
|
12
|
+
declare id: string
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.makePath('resources/views/authkit/consent.edge') })
|
|
3
|
+
}}}
|
|
4
|
+
<!doctype html>
|
|
5
|
+
<html lang="pt-br"><head><meta charset="utf-8"><title>Autorizar</title>
|
|
6
|
+
<script src="https://cdn.tailwindcss.com"></script></head>
|
|
7
|
+
<body class="min-h-screen flex items-center justify-center bg-gray-50">
|
|
8
|
+
<form method="POST" action="/auth/interaction/{{ uid }}/consent" class="w-full max-w-sm bg-white p-6 rounded-lg shadow">
|
|
9
|
+
<h1 class="text-lg font-semibold mb-2">Autorizar acesso</h1>
|
|
10
|
+
<p class="text-sm text-gray-600 mb-4">O app <strong>{{ params.client_id }}</strong> quer acessar sua conta.</p>
|
|
11
|
+
<button class="w-full bg-black text-white rounded py-2">Autorizar</button>
|
|
12
|
+
</form>
|
|
13
|
+
</body></html>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.makePath('resources/views/authkit/login.edge') })
|
|
3
|
+
}}}
|
|
4
|
+
<!doctype html>
|
|
5
|
+
<html lang="pt-br"><head><meta charset="utf-8"><title>Entrar</title>
|
|
6
|
+
<script src="https://cdn.tailwindcss.com"></script></head>
|
|
7
|
+
<body class="min-h-screen flex items-center justify-center bg-gray-50">
|
|
8
|
+
<form method="POST" action="/auth/interaction/{{ uid }}/login" class="w-full max-w-sm bg-white p-6 rounded-lg shadow">
|
|
9
|
+
<h1 class="text-lg font-semibold mb-4">Entrar</h1>
|
|
10
|
+
@if(error)
|
|
11
|
+
<p class="text-red-600 text-sm mb-3">{{ error }}</p>
|
|
12
|
+
@end
|
|
13
|
+
<label class="block text-sm mb-1">E-mail</label>
|
|
14
|
+
<input name="email" type="email" required class="w-full border rounded px-3 py-2 mb-3" />
|
|
15
|
+
<label class="block text-sm mb-1">Senha</label>
|
|
16
|
+
<input name="password" type="password" required class="w-full border rounded px-3 py-2 mb-4" />
|
|
17
|
+
<button class="w-full bg-black text-white rounded py-2">Entrar</button>
|
|
18
|
+
</form>
|
|
19
|
+
</body></html>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.makePath('inertia/components/auth_shell.tsx') })
|
|
3
|
+
}}}
|
|
4
|
+
import type { ReactNode } from 'react'
|
|
5
|
+
|
|
6
|
+
export interface AuthBrand {
|
|
7
|
+
appName: string
|
|
8
|
+
accent: string
|
|
9
|
+
accentSoft?: string
|
|
10
|
+
company?: string
|
|
11
|
+
tagline?: string
|
|
12
|
+
audienceLabel?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const COMPANY_FALLBACK = 'educ(a)ção'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Layout de duas colunas para as telas de autenticacao do IdP.
|
|
19
|
+
* Painel esquerdo: marca (empresa guarda-chuva + produto). Painel direito: formulario.
|
|
20
|
+
*/
|
|
21
|
+
export default function AuthShell({
|
|
22
|
+
brand,
|
|
23
|
+
children,
|
|
24
|
+
}: {
|
|
25
|
+
brand?: AuthBrand
|
|
26
|
+
children: ReactNode
|
|
27
|
+
}) {
|
|
28
|
+
const accent = brand?.accent ?? '#111827'
|
|
29
|
+
const accentSoft = brand?.accentSoft ?? accent
|
|
30
|
+
const company = brand?.company ?? COMPANY_FALLBACK
|
|
31
|
+
const appName = brand?.appName ?? 'Sua conta'
|
|
32
|
+
const tagline = brand?.tagline ?? 'Acesso unificado'
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className="min-h-screen flex items-center justify-center bg-gray-100 p-4">
|
|
36
|
+
<div className="w-full max-w-4xl overflow-hidden rounded-2xl bg-white shadow-xl ring-1 ring-black/5 grid md:grid-cols-2">
|
|
37
|
+
{/* Painel da marca */}
|
|
38
|
+
<div
|
|
39
|
+
className="relative flex flex-col justify-between p-8 text-white md:p-10"
|
|
40
|
+
style={{ backgroundImage: `linear-gradient(135deg, ${accent} 0%, ${accentSoft} 100%)` }}
|
|
41
|
+
>
|
|
42
|
+
<div
|
|
43
|
+
className="text-xs font-semibold uppercase tracking-[0.2em] text-white/80"
|
|
44
|
+
aria-label="Empresa"
|
|
45
|
+
>
|
|
46
|
+
{company}
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div className="mt-10 md:mt-0">
|
|
50
|
+
<h2 className="text-2xl font-bold leading-tight md:text-3xl">{appName}</h2>
|
|
51
|
+
<p className="mt-2 text-sm text-white/85">{tagline}</p>
|
|
52
|
+
{brand?.audienceLabel && (
|
|
53
|
+
<span className="mt-4 inline-block rounded-full bg-white/20 px-3 py-1 text-xs font-medium uppercase tracking-wide text-white ring-1 ring-white/30">
|
|
54
|
+
{brand.audienceLabel}
|
|
55
|
+
</span>
|
|
56
|
+
)}
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<div className="mt-10 text-xs text-white/60">© {company}</div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
{/* Painel do formulario */}
|
|
63
|
+
<div className="p-8 md:p-10">{children}</div>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
)
|
|
67
|
+
}
|