@dudousxd/adonis-authkit-server 0.4.0 → 0.5.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/build/index.d.ts +1 -1
- package/build/index.js +1 -1
- package/build/src/host/controllers/account_mfa_controller.js +6 -2
- package/build/src/host/controllers/interaction_controller.js +6 -2
- package/build/src/host/default_mailer.js +53 -19
- package/build/src/host/email_templates.d.ts +4 -0
- package/build/src/host/email_templates.js +5 -2
- package/build/src/host/i18n.d.ts +305 -11
- package/build/src/host/i18n.js +335 -12
- package/build/src/observability/metrics_controller.js +4 -4
- package/package.json +1 -1
package/build/index.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export { inertiaRenderer } from './src/host/renderers/inertia_renderer.js';
|
|
|
26
26
|
export { edgeRenderer } from './src/host/renderers/edge_renderer.js';
|
|
27
27
|
export { brandFor, isFirstParty } from './src/host/branding.js';
|
|
28
28
|
export type { BrandingConfig, ClientBrand } from './src/host/branding.js';
|
|
29
|
-
export { resolveMessages, translate, DEFAULT_MESSAGES, DEFAULT_LOCALE } from './src/host/i18n.js';
|
|
29
|
+
export { resolveMessages, translate, DEFAULT_MESSAGES, PT_BR_MESSAGES, BUILTIN_MESSAGES, DEFAULT_LOCALE, } from './src/host/i18n.js';
|
|
30
30
|
export type { I18nConfig, AuthMessages } from './src/host/i18n.js';
|
|
31
31
|
export type { AuthHostRenderer, AuthSocialConfig } from './src/define_config.js';
|
|
32
32
|
export { registerAuthHost } from './src/host/register_auth_host.js';
|
package/build/index.js
CHANGED
|
@@ -17,7 +17,7 @@ export { withAuditLog } from './src/mixins/with_audit_log.js';
|
|
|
17
17
|
export { inertiaRenderer } from './src/host/renderers/inertia_renderer.js';
|
|
18
18
|
export { edgeRenderer } from './src/host/renderers/edge_renderer.js';
|
|
19
19
|
export { brandFor, isFirstParty } from './src/host/branding.js';
|
|
20
|
-
export { resolveMessages, translate, DEFAULT_MESSAGES, DEFAULT_LOCALE } from './src/host/i18n.js';
|
|
20
|
+
export { resolveMessages, translate, DEFAULT_MESSAGES, PT_BR_MESSAGES, BUILTIN_MESSAGES, DEFAULT_LOCALE, } from './src/host/i18n.js';
|
|
21
21
|
export { registerAuthHost } from './src/host/register_auth_host.js';
|
|
22
22
|
export { resolveRateLimit, resolveNotifications } from './src/define_config.js';
|
|
23
23
|
export { createAuthThrottles } from './src/host/rate_limit.js';
|
|
@@ -39,7 +39,9 @@ export default class AccountMfaController {
|
|
|
39
39
|
const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
|
|
40
40
|
const generated = await cfg.accountStore.generatePasskeyRegistrationOptions?.(userId);
|
|
41
41
|
if (!generated) {
|
|
42
|
-
return ctx.response.notFound({
|
|
42
|
+
return ctx.response.notFound({
|
|
43
|
+
message: translate(cfg.messages, 'errors.passkeys_unavailable'),
|
|
44
|
+
});
|
|
43
45
|
}
|
|
44
46
|
ctx.session.put(PASSKEY_REG_CHALLENGE_KEY, generated.challenge);
|
|
45
47
|
return generated.options;
|
|
@@ -54,7 +56,9 @@ export default class AccountMfaController {
|
|
|
54
56
|
const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
|
|
55
57
|
const challenge = ctx.session.get(PASSKEY_REG_CHALLENGE_KEY);
|
|
56
58
|
if (!challenge) {
|
|
57
|
-
return ctx.response.badRequest({
|
|
59
|
+
return ctx.response.badRequest({
|
|
60
|
+
message: translate(cfg.messages, 'errors.challenge_expired'),
|
|
61
|
+
});
|
|
58
62
|
}
|
|
59
63
|
const body = ctx.request.input('response', ctx.request.body());
|
|
60
64
|
const ok = (await cfg.accountStore.verifyPasskeyRegistration?.(userId, body, challenge)) ?? false;
|
|
@@ -240,11 +240,15 @@ export default class AuthInteractionController {
|
|
|
240
240
|
const cfg = service.config;
|
|
241
241
|
const accountId = ctx.session.get(MFA_PENDING_KEY);
|
|
242
242
|
if (!accountId) {
|
|
243
|
-
return ctx.response.badRequest({
|
|
243
|
+
return ctx.response.badRequest({
|
|
244
|
+
message: translate(cfg.messages, 'errors.session_expired'),
|
|
245
|
+
});
|
|
244
246
|
}
|
|
245
247
|
const generated = await cfg.accountStore.generatePasskeyAuthenticationOptions?.(accountId);
|
|
246
248
|
if (!generated) {
|
|
247
|
-
return ctx.response.notFound({
|
|
249
|
+
return ctx.response.notFound({
|
|
250
|
+
message: translate(cfg.messages, 'errors.no_passkey_registered'),
|
|
251
|
+
});
|
|
248
252
|
}
|
|
249
253
|
ctx.session.put(PASSKEY_AUTH_CHALLENGE_KEY, generated.challenge);
|
|
250
254
|
return generated.options;
|
|
@@ -7,6 +7,7 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
7
7
|
return path;
|
|
8
8
|
};
|
|
9
9
|
import { renderTransactionalEmail } from './email_templates.js';
|
|
10
|
+
import { resolveMessages, translate, } from './i18n.js';
|
|
10
11
|
let mailServicePromise;
|
|
11
12
|
/**
|
|
12
13
|
* Importa o service de mail do HOST de forma preguiçosa e fail-safe.
|
|
@@ -78,6 +79,22 @@ function resolveBrand(ctx) {
|
|
|
78
79
|
}
|
|
79
80
|
return { appName: 'AuthKit' };
|
|
80
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Resolve o catálogo de mensagens i18n a partir do `config/authkit.ts` (i18n).
|
|
84
|
+
* Cai no default (`en`) se a config não for resolvível. Usado para localizar
|
|
85
|
+
* os e-mails transacionais (assunto/cabeçalho/corpo).
|
|
86
|
+
*/
|
|
87
|
+
function resolveMailMessages(ctx) {
|
|
88
|
+
try {
|
|
89
|
+
const resolver = ctx.containerResolver;
|
|
90
|
+
const i18n = resolver.app?.config?.get?.('authkit')?.i18n;
|
|
91
|
+
return { messages: resolveMessages(i18n), locale: i18n?.locale ?? 'en' };
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// sem config authkit resolvível — usa o default `en`.
|
|
95
|
+
return { messages: resolveMessages(), locale: 'en' };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
81
98
|
async function sendEmail(ctx, to, content) {
|
|
82
99
|
const mail = await loadMail();
|
|
83
100
|
if (!mail)
|
|
@@ -97,14 +114,17 @@ async function sendEmail(ctx, to, content) {
|
|
|
97
114
|
export async function sendPasswordResetEmail(ctx, data) {
|
|
98
115
|
try {
|
|
99
116
|
const brand = resolveBrand(ctx);
|
|
117
|
+
const { messages: t, locale } = resolveMailMessages(ctx);
|
|
100
118
|
const content = renderTransactionalEmail({
|
|
101
119
|
brand,
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
120
|
+
locale,
|
|
121
|
+
linkFallback: translate(t, 'mail.common.link_fallback'),
|
|
122
|
+
subject: translate(t, 'mail.reset.subject'),
|
|
123
|
+
heading: translate(t, 'mail.reset.heading'),
|
|
124
|
+
intro: translate(t, 'mail.reset.intro'),
|
|
125
|
+
ctaLabel: translate(t, 'mail.reset.cta'),
|
|
106
126
|
ctaUrl: data.resetUrl,
|
|
107
|
-
footnote:
|
|
127
|
+
footnote: translate(t, 'mail.reset.fallback'),
|
|
108
128
|
});
|
|
109
129
|
const sent = await sendEmail(ctx, data.email, content);
|
|
110
130
|
if (!sent) {
|
|
@@ -122,14 +142,17 @@ export async function sendPasswordResetEmail(ctx, data) {
|
|
|
122
142
|
export async function sendEmailChangeConfirmationEmail(ctx, data) {
|
|
123
143
|
try {
|
|
124
144
|
const brand = resolveBrand(ctx);
|
|
145
|
+
const { messages: t, locale } = resolveMailMessages(ctx);
|
|
125
146
|
const content = renderTransactionalEmail({
|
|
126
147
|
brand,
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
148
|
+
locale,
|
|
149
|
+
linkFallback: translate(t, 'mail.common.link_fallback'),
|
|
150
|
+
subject: translate(t, 'mail.email_change.subject'),
|
|
151
|
+
heading: translate(t, 'mail.email_change.heading'),
|
|
152
|
+
intro: translate(t, 'mail.email_change.intro'),
|
|
153
|
+
ctaLabel: translate(t, 'mail.email_change.cta'),
|
|
131
154
|
ctaUrl: data.confirmUrl,
|
|
132
|
-
footnote:
|
|
155
|
+
footnote: translate(t, 'mail.email_change.fallback'),
|
|
133
156
|
});
|
|
134
157
|
const sent = await sendEmail(ctx, data.email, content);
|
|
135
158
|
if (!sent) {
|
|
@@ -147,15 +170,23 @@ export async function sendEmailChangeConfirmationEmail(ctx, data) {
|
|
|
147
170
|
export async function sendNewLoginEmail(ctx, data) {
|
|
148
171
|
try {
|
|
149
172
|
const brand = resolveBrand(ctx);
|
|
173
|
+
const { messages: t, locale } = resolveMailMessages(ctx);
|
|
150
174
|
const origin = `${ctx.request.protocol()}://${ctx.request.host()}`;
|
|
175
|
+
const intro = [
|
|
176
|
+
translate(t, 'mail.new_login.intro'),
|
|
177
|
+
translate(t, 'mail.new_login.when', { date: data.when }),
|
|
178
|
+
translate(t, 'mail.new_login.ip', { ip: data.ip }),
|
|
179
|
+
].join(' ');
|
|
151
180
|
const content = renderTransactionalEmail({
|
|
152
181
|
brand,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
182
|
+
locale,
|
|
183
|
+
linkFallback: translate(t, 'mail.common.link_fallback'),
|
|
184
|
+
subject: translate(t, 'mail.new_login.subject'),
|
|
185
|
+
heading: translate(t, 'mail.new_login.heading'),
|
|
186
|
+
intro,
|
|
187
|
+
ctaLabel: translate(t, 'account.security.title'),
|
|
157
188
|
ctaUrl: `${origin}/account/security`,
|
|
158
|
-
footnote: '
|
|
189
|
+
footnote: translate(t, 'mail.new_login.fallback'),
|
|
159
190
|
});
|
|
160
191
|
const sent = await sendEmail(ctx, data.email, content);
|
|
161
192
|
if (!sent) {
|
|
@@ -173,12 +204,15 @@ export async function sendNewLoginEmail(ctx, data) {
|
|
|
173
204
|
export async function sendEmailVerificationEmail(ctx, data) {
|
|
174
205
|
try {
|
|
175
206
|
const brand = resolveBrand(ctx);
|
|
207
|
+
const { messages: t, locale } = resolveMailMessages(ctx);
|
|
176
208
|
const content = renderTransactionalEmail({
|
|
177
209
|
brand,
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
210
|
+
locale,
|
|
211
|
+
linkFallback: translate(t, 'mail.common.link_fallback'),
|
|
212
|
+
subject: translate(t, 'mail.verify.subject'),
|
|
213
|
+
heading: translate(t, 'mail.verify.heading'),
|
|
214
|
+
intro: translate(t, 'mail.verify.intro'),
|
|
215
|
+
ctaLabel: translate(t, 'mail.verify.cta'),
|
|
182
216
|
ctaUrl: data.verifyUrl,
|
|
183
217
|
});
|
|
184
218
|
const sent = await sendEmail(ctx, data.email, content);
|
|
@@ -30,6 +30,10 @@ interface EmailTemplateInput {
|
|
|
30
30
|
ctaUrl: string;
|
|
31
31
|
/** Linha auxiliar abaixo do botão (ex.: validade do link). */
|
|
32
32
|
footnote?: string;
|
|
33
|
+
/** Texto que precede o link de fallback (i18n). Default em inglês. */
|
|
34
|
+
linkFallback?: string;
|
|
35
|
+
/** Locale do documento HTML (atributo `lang`). Default: 'en'. */
|
|
36
|
+
locale?: string;
|
|
33
37
|
}
|
|
34
38
|
export declare function renderTransactionalEmail(input: EmailTemplateInput): EmailContent;
|
|
35
39
|
export {};
|
|
@@ -21,8 +21,11 @@ export function renderTransactionalEmail(input) {
|
|
|
21
21
|
const accent = input.brand.accent || FALLBACK_ACCENT;
|
|
22
22
|
const company = input.brand.company || appName;
|
|
23
23
|
const year = '©'; // ano resolvido fora (sem Date.* aqui); rodapé usa só o nome.
|
|
24
|
+
const lang = input.locale || 'en';
|
|
25
|
+
const linkFallback = input.linkFallback ||
|
|
26
|
+
'If the button does not work, copy and paste this link into your browser:';
|
|
24
27
|
const html = `<!doctype html>
|
|
25
|
-
<html lang="
|
|
28
|
+
<html lang="${esc(lang)}">
|
|
26
29
|
<head>
|
|
27
30
|
<meta charset="utf-8">
|
|
28
31
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
@@ -42,7 +45,7 @@ export function renderTransactionalEmail(input) {
|
|
|
42
45
|
<a href="${esc(input.ctaUrl)}" style="display:inline-block;padding:12px 24px;font-size:15px;font-weight:600;color:#ffffff;text-decoration:none;border-radius:8px;">${esc(input.ctaLabel)}</a>
|
|
43
46
|
</td></tr></table>
|
|
44
47
|
${input.footnote ? `<p style="margin:24px 0 0;font-size:13px;line-height:1.5;color:#6b7280;">${esc(input.footnote)}</p>` : ''}
|
|
45
|
-
<p style="margin:24px 0 0;font-size:13px;line-height:1.5;color:#6b7280;"
|
|
48
|
+
<p style="margin:24px 0 0;font-size:13px;line-height:1.5;color:#6b7280;">${esc(linkFallback)}<br><a href="${esc(input.ctaUrl)}" style="color:${esc(accent)};word-break:break-all;">${esc(input.ctaUrl)}</a></p>
|
|
46
49
|
</td></tr>
|
|
47
50
|
<tr><td style="padding:24px 28px 28px;border-top:1px solid #f3f4f6;">
|
|
48
51
|
<p style="margin:0;font-size:12px;line-height:1.5;color:#9ca3af;">${esc(company)} ${year}</p>
|
package/build/src/host/i18n.d.ts
CHANGED
|
@@ -3,26 +3,29 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Todas as strings visíveis ao usuário das views Edge (e as mensagens de
|
|
5
5
|
* flash/erro produzidas pelos controllers) vivem num catálogo achatado de
|
|
6
|
-
* chaves pontilhadas. O default embutido é
|
|
7
|
-
* funcionando SEM nenhuma configuração. O
|
|
8
|
-
*
|
|
6
|
+
* chaves pontilhadas. O default embutido é inglês (`en`) — os apps continuam
|
|
7
|
+
* funcionando SEM nenhuma configuração. O pt-BR é um locale embutido: basta
|
|
8
|
+
* `i18n: { locale: 'pt-BR' }`. O host também pode sobrescrever chaves pontuais
|
|
9
|
+
* ou fornecer locales inteiros (ex.: `fr`) via `I18nConfig`.
|
|
9
10
|
*/
|
|
10
11
|
/** Catálogo achatado de chaves de mensagem → strings. */
|
|
11
12
|
export type AuthMessages = Record<string, string>;
|
|
12
13
|
export interface I18nConfig {
|
|
13
|
-
/** Locale ativo. Default: 'pt-BR'. */
|
|
14
|
+
/** Locale ativo. Default: 'en'. Locale embutido extra: 'pt-BR'. */
|
|
14
15
|
locale?: string;
|
|
15
16
|
/**
|
|
16
17
|
* Locales adicionais e/ou overrides pontuais. As chaves do locale ativo são
|
|
17
|
-
* mescladas SOBRE o
|
|
18
|
-
*
|
|
18
|
+
* mescladas SOBRE o catálogo embutido do locale (ou sobre o default `en`
|
|
19
|
+
* quando o locale não é embutido) — então o host pode trocar só algumas
|
|
20
|
+
* chaves, complementar um locale embutido, ou trazer um locale novo por
|
|
21
|
+
* completo.
|
|
19
22
|
*/
|
|
20
23
|
messages?: Record<string, Partial<AuthMessages>>;
|
|
21
24
|
}
|
|
22
25
|
/** Locale default do host-kit. */
|
|
23
|
-
export declare const DEFAULT_LOCALE = "
|
|
26
|
+
export declare const DEFAULT_LOCALE = "en";
|
|
24
27
|
/**
|
|
25
|
-
* Catálogo default (
|
|
28
|
+
* Catálogo default (inglês) — cobre TODAS as strings visíveis ao usuário das
|
|
26
29
|
* views e as mensagens de flash/erro dos controllers. Chaves agrupadas por tela.
|
|
27
30
|
*/
|
|
28
31
|
export declare const DEFAULT_MESSAGES: {
|
|
@@ -244,11 +247,302 @@ export declare const DEFAULT_MESSAGES: {
|
|
|
244
247
|
'errors.signup_failed': string;
|
|
245
248
|
'errors.invalid_or_expired_token': string;
|
|
246
249
|
'errors.account_locked': string;
|
|
250
|
+
'errors.session_expired': string;
|
|
251
|
+
'errors.challenge_expired': string;
|
|
252
|
+
'errors.passkeys_unavailable': string;
|
|
253
|
+
'errors.no_passkey_registered': string;
|
|
254
|
+
'mail.common.link_fallback': string;
|
|
255
|
+
'mail.reset.subject': string;
|
|
256
|
+
'mail.reset.heading': string;
|
|
257
|
+
'mail.reset.intro': string;
|
|
258
|
+
'mail.reset.cta': string;
|
|
259
|
+
'mail.reset.fallback': string;
|
|
260
|
+
'mail.reset.expires': string;
|
|
261
|
+
'mail.verify.subject': string;
|
|
262
|
+
'mail.verify.heading': string;
|
|
263
|
+
'mail.verify.intro': string;
|
|
264
|
+
'mail.verify.cta': string;
|
|
265
|
+
'mail.verify.fallback': string;
|
|
266
|
+
'mail.verify.expires': string;
|
|
267
|
+
'mail.new_login.subject': string;
|
|
268
|
+
'mail.new_login.heading': string;
|
|
269
|
+
'mail.new_login.intro': string;
|
|
270
|
+
'mail.new_login.when': string;
|
|
271
|
+
'mail.new_login.ip': string;
|
|
272
|
+
'mail.new_login.device': string;
|
|
273
|
+
'mail.new_login.fallback': string;
|
|
274
|
+
'mail.email_change.subject': string;
|
|
275
|
+
'mail.email_change.heading': string;
|
|
276
|
+
'mail.email_change.intro': string;
|
|
277
|
+
'mail.email_change.cta': string;
|
|
278
|
+
'mail.email_change.fallback': string;
|
|
279
|
+
'mail.email_change.expires': string;
|
|
247
280
|
};
|
|
248
281
|
/**
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
|
|
282
|
+
* Catálogo embutido pt-BR. Espelha TODAS as chaves do default `en`. Ativado
|
|
283
|
+
* com `i18n: { locale: 'pt-BR' }` sem nenhuma config extra de mensagens.
|
|
284
|
+
*/
|
|
285
|
+
export declare const PT_BR_MESSAGES: {
|
|
286
|
+
'common.app_fallback': string;
|
|
287
|
+
'common.brand_eyebrow': string;
|
|
288
|
+
'login.page_title': string;
|
|
289
|
+
'login.title': string;
|
|
290
|
+
'login.identifier_intro': string;
|
|
291
|
+
'login.email_label': string;
|
|
292
|
+
'login.identifier_submit': string;
|
|
293
|
+
'login.create_account': string;
|
|
294
|
+
'login.forgot_password': string;
|
|
295
|
+
'login.divider_or': string;
|
|
296
|
+
'login.google': string;
|
|
297
|
+
'login.greeting': string;
|
|
298
|
+
'login.switch_account': string;
|
|
299
|
+
'login.password_label': string;
|
|
300
|
+
'login.submit': string;
|
|
301
|
+
'signup.page_title': string;
|
|
302
|
+
'signup.title': string;
|
|
303
|
+
'signup.intro': string;
|
|
304
|
+
'signup.name_label': string;
|
|
305
|
+
'signup.email_label': string;
|
|
306
|
+
'signup.password_label': string;
|
|
307
|
+
'signup.submit': string;
|
|
308
|
+
'signup.have_account': string;
|
|
309
|
+
'forgot.page_title': string;
|
|
310
|
+
'forgot.sent_title': string;
|
|
311
|
+
'forgot.sent_body': string;
|
|
312
|
+
'forgot.title': string;
|
|
313
|
+
'forgot.intro': string;
|
|
314
|
+
'forgot.email_label': string;
|
|
315
|
+
'forgot.submit': string;
|
|
316
|
+
'reset.page_title': string;
|
|
317
|
+
'reset.done_title': string;
|
|
318
|
+
'reset.done_body': string;
|
|
319
|
+
'reset.title': string;
|
|
320
|
+
'reset.intro': string;
|
|
321
|
+
'reset.password_label': string;
|
|
322
|
+
'reset.submit': string;
|
|
323
|
+
'verify_email.page_title': string;
|
|
324
|
+
'verify_email.verified_title': string;
|
|
325
|
+
'verify_email.verified_body': string;
|
|
326
|
+
'verify_email.invalid_title': string;
|
|
327
|
+
'verify_email.invalid_body': string;
|
|
328
|
+
'mfa_challenge.page_title': string;
|
|
329
|
+
'mfa_challenge.title': string;
|
|
330
|
+
'mfa_challenge.intro': string;
|
|
331
|
+
'mfa_challenge.code_label': string;
|
|
332
|
+
'mfa_challenge.submit': string;
|
|
333
|
+
'mfa_challenge.recovery_summary': string;
|
|
334
|
+
'mfa_challenge.recovery_submit': string;
|
|
335
|
+
'mfa_challenge.passkey_button': string;
|
|
336
|
+
'mfa_challenge.passkey_error': string;
|
|
337
|
+
'consent.page_title': string;
|
|
338
|
+
'consent.title': string;
|
|
339
|
+
'consent.body': string;
|
|
340
|
+
'consent.submit': string;
|
|
341
|
+
'account.login.page_title': string;
|
|
342
|
+
'account.login.title': string;
|
|
343
|
+
'account.login.intro': string;
|
|
344
|
+
'account.login.email_label': string;
|
|
345
|
+
'account.login.password_label': string;
|
|
346
|
+
'account.login.submit': string;
|
|
347
|
+
'account.tokens.page_title': string;
|
|
348
|
+
'account.tokens.title': string;
|
|
349
|
+
'account.tokens.logout': string;
|
|
350
|
+
'account.tokens.security': string;
|
|
351
|
+
'account.tokens.created_notice': string;
|
|
352
|
+
'account.tokens.name_placeholder': string;
|
|
353
|
+
'account.tokens.create': string;
|
|
354
|
+
'account.tokens.empty': string;
|
|
355
|
+
'account.tokens.created_at': string;
|
|
356
|
+
'account.tokens.last_used': string;
|
|
357
|
+
'account.tokens.never_used': string;
|
|
358
|
+
'account.tokens.scopes': string;
|
|
359
|
+
'account.tokens.audience': string;
|
|
360
|
+
'account.tokens.revoke': string;
|
|
361
|
+
'account.security.page_title': string;
|
|
362
|
+
'account.security.title': string;
|
|
363
|
+
'account.security.logout': string;
|
|
364
|
+
'account.security.current_email': string;
|
|
365
|
+
'account.security.not_supported': string;
|
|
366
|
+
'account.security.password_section': string;
|
|
367
|
+
'account.security.current_password_label': string;
|
|
368
|
+
'account.security.new_password_label': string;
|
|
369
|
+
'account.security.change_password_submit': string;
|
|
370
|
+
'account.security.password_changed': string;
|
|
371
|
+
'account.security.email_section': string;
|
|
372
|
+
'account.security.email_intro': string;
|
|
373
|
+
'account.security.new_email_label': string;
|
|
374
|
+
'account.security.email_password_label': string;
|
|
375
|
+
'account.security.change_email_submit': string;
|
|
376
|
+
'account.security.email_change_requested': string;
|
|
377
|
+
'account.security.email_changed': string;
|
|
378
|
+
'account.email_confirmed.page_title': string;
|
|
379
|
+
'account.email_confirmed.ok_title': string;
|
|
380
|
+
'account.email_confirmed.ok_body': string;
|
|
381
|
+
'account.email_confirmed.invalid_title': string;
|
|
382
|
+
'account.email_confirmed.invalid_body': string;
|
|
383
|
+
'account.mfa.page_title': string;
|
|
384
|
+
'account.mfa.title': string;
|
|
385
|
+
'account.mfa.logout': string;
|
|
386
|
+
'account.mfa.recovery_codes_notice': string;
|
|
387
|
+
'account.mfa.enroll_intro': string;
|
|
388
|
+
'account.mfa.qr_alt': string;
|
|
389
|
+
'account.mfa.manual_intro': string;
|
|
390
|
+
'account.mfa.confirm_code_label': string;
|
|
391
|
+
'account.mfa.activate': string;
|
|
392
|
+
'account.mfa.enabled_html': string;
|
|
393
|
+
'account.mfa.disable': string;
|
|
394
|
+
'account.mfa.disabled_intro': string;
|
|
395
|
+
'account.mfa.enable': string;
|
|
396
|
+
'mfa.passkey.section_title': string;
|
|
397
|
+
'mfa.passkey.section_intro': string;
|
|
398
|
+
'mfa.passkey.add': string;
|
|
399
|
+
'mfa.passkey.remove': string;
|
|
400
|
+
'mfa.passkey.empty': string;
|
|
401
|
+
'mfa.passkey.unnamed': string;
|
|
402
|
+
'mfa.passkey.created_at': string;
|
|
403
|
+
'mfa.passkey.register_error': string;
|
|
404
|
+
'mfa.passkey.unsupported': string;
|
|
405
|
+
'admin.nav.dashboard': string;
|
|
406
|
+
'admin.nav.users': string;
|
|
407
|
+
'admin.nav.clients': string;
|
|
408
|
+
'admin.nav.audit': string;
|
|
409
|
+
'admin.nav.logout': string;
|
|
410
|
+
'admin.dashboard.page_title': string;
|
|
411
|
+
'admin.dashboard.title': string;
|
|
412
|
+
'admin.dashboard.users_count': string;
|
|
413
|
+
'admin.dashboard.clients_count': string;
|
|
414
|
+
'admin.dashboard.audit_count': string;
|
|
415
|
+
'admin.dashboard.recent_title': string;
|
|
416
|
+
'admin.users.page_title': string;
|
|
417
|
+
'admin.users.title': string;
|
|
418
|
+
'admin.users.search_placeholder': string;
|
|
419
|
+
'admin.users.search': string;
|
|
420
|
+
'admin.users.empty': string;
|
|
421
|
+
'admin.users.roles_placeholder': string;
|
|
422
|
+
'admin.users.save_roles': string;
|
|
423
|
+
'admin.users.sessions': string;
|
|
424
|
+
'admin.sessions.page_title': string;
|
|
425
|
+
'admin.sessions.title': string;
|
|
426
|
+
'admin.sessions.account': string;
|
|
427
|
+
'admin.sessions.back': string;
|
|
428
|
+
'admin.sessions.not_supported': string;
|
|
429
|
+
'admin.sessions.revoked_notice': string;
|
|
430
|
+
'admin.sessions.sessions_section': string;
|
|
431
|
+
'admin.sessions.sessions_empty': string;
|
|
432
|
+
'admin.sessions.session_login_ts': string;
|
|
433
|
+
'admin.sessions.session_amr': string;
|
|
434
|
+
'admin.sessions.grants_section': string;
|
|
435
|
+
'admin.sessions.grants_empty': string;
|
|
436
|
+
'admin.sessions.grant_client': string;
|
|
437
|
+
'admin.sessions.grant_tokens': string;
|
|
438
|
+
'admin.sessions.revoke_all': string;
|
|
439
|
+
'admin.sessions.revoke_confirm': string;
|
|
440
|
+
'admin.clients.page_title': string;
|
|
441
|
+
'admin.clients.title': string;
|
|
442
|
+
'admin.clients.empty': string;
|
|
443
|
+
'admin.clients.confidential': string;
|
|
444
|
+
'admin.clients.public': string;
|
|
445
|
+
'admin.clients.grants': string;
|
|
446
|
+
'admin.clients.redirect_uris': string;
|
|
447
|
+
'admin.clients.dynamic_notice': string;
|
|
448
|
+
'admin.clients.static_section': string;
|
|
449
|
+
'admin.clients.dynamic_section': string;
|
|
450
|
+
'admin.clients.dynamic_empty': string;
|
|
451
|
+
'admin.clients.dynamic_not_supported': string;
|
|
452
|
+
'admin.clients.new': string;
|
|
453
|
+
'admin.clients.new_title': string;
|
|
454
|
+
'admin.clients.edit_title': string;
|
|
455
|
+
'admin.clients.edit': string;
|
|
456
|
+
'admin.clients.delete': string;
|
|
457
|
+
'admin.clients.delete_confirm': string;
|
|
458
|
+
'admin.clients.regenerate_secret': string;
|
|
459
|
+
'admin.clients.regenerate_confirm': string;
|
|
460
|
+
'admin.clients.back': string;
|
|
461
|
+
'admin.clients.cancel': string;
|
|
462
|
+
'admin.clients.save': string;
|
|
463
|
+
'admin.clients.create': string;
|
|
464
|
+
'admin.clients.secret_once_title': string;
|
|
465
|
+
'admin.clients.secret_once_notice': string;
|
|
466
|
+
'admin.clients.field_client_id': string;
|
|
467
|
+
'admin.clients.field_client_id_placeholder': string;
|
|
468
|
+
'admin.clients.field_client_id_help': string;
|
|
469
|
+
'admin.clients.field_redirect_uris': string;
|
|
470
|
+
'admin.clients.field_redirect_uris_help': string;
|
|
471
|
+
'admin.clients.field_post_logout_uris': string;
|
|
472
|
+
'admin.clients.field_post_logout_uris_help': string;
|
|
473
|
+
'admin.clients.field_grant_types': string;
|
|
474
|
+
'admin.clients.field_auth_method': string;
|
|
475
|
+
'admin.audit.page_title': string;
|
|
476
|
+
'admin.audit.title': string;
|
|
477
|
+
'admin.audit.type_placeholder': string;
|
|
478
|
+
'admin.audit.subject_placeholder': string;
|
|
479
|
+
'admin.audit.filter': string;
|
|
480
|
+
'admin.audit.empty': string;
|
|
481
|
+
'admin.audit.not_supported': string;
|
|
482
|
+
'admin.pagination.page': string;
|
|
483
|
+
'admin.pagination.prev': string;
|
|
484
|
+
'admin.pagination.next': string;
|
|
485
|
+
'device.input.title': string;
|
|
486
|
+
'device.input.intro': string;
|
|
487
|
+
'device.input.submit': string;
|
|
488
|
+
'device.input.error_invalid': string;
|
|
489
|
+
'device.input.error_aborted': string;
|
|
490
|
+
'device.input.error_generic': string;
|
|
491
|
+
'device.confirm.title': string;
|
|
492
|
+
'device.confirm.body': string;
|
|
493
|
+
'device.confirm.submit': string;
|
|
494
|
+
'device.confirm.abort': string;
|
|
495
|
+
'device.success.title': string;
|
|
496
|
+
'device.success.body': string;
|
|
497
|
+
'mfa_challenge.required_no_enrollment': string;
|
|
498
|
+
'errors.invalid_credentials': string;
|
|
499
|
+
'errors.invalid_code': string;
|
|
500
|
+
'errors.email_taken': string;
|
|
501
|
+
'errors.signup_failed': string;
|
|
502
|
+
'errors.invalid_or_expired_token': string;
|
|
503
|
+
'errors.account_locked': string;
|
|
504
|
+
'errors.session_expired': string;
|
|
505
|
+
'errors.challenge_expired': string;
|
|
506
|
+
'errors.passkeys_unavailable': string;
|
|
507
|
+
'errors.no_passkey_registered': string;
|
|
508
|
+
'mail.common.link_fallback': string;
|
|
509
|
+
'mail.reset.subject': string;
|
|
510
|
+
'mail.reset.heading': string;
|
|
511
|
+
'mail.reset.intro': string;
|
|
512
|
+
'mail.reset.cta': string;
|
|
513
|
+
'mail.reset.fallback': string;
|
|
514
|
+
'mail.reset.expires': string;
|
|
515
|
+
'mail.verify.subject': string;
|
|
516
|
+
'mail.verify.heading': string;
|
|
517
|
+
'mail.verify.intro': string;
|
|
518
|
+
'mail.verify.cta': string;
|
|
519
|
+
'mail.verify.fallback': string;
|
|
520
|
+
'mail.verify.expires': string;
|
|
521
|
+
'mail.new_login.subject': string;
|
|
522
|
+
'mail.new_login.heading': string;
|
|
523
|
+
'mail.new_login.intro': string;
|
|
524
|
+
'mail.new_login.when': string;
|
|
525
|
+
'mail.new_login.ip': string;
|
|
526
|
+
'mail.new_login.device': string;
|
|
527
|
+
'mail.new_login.fallback': string;
|
|
528
|
+
'mail.email_change.subject': string;
|
|
529
|
+
'mail.email_change.heading': string;
|
|
530
|
+
'mail.email_change.intro': string;
|
|
531
|
+
'mail.email_change.cta': string;
|
|
532
|
+
'mail.email_change.fallback': string;
|
|
533
|
+
'mail.email_change.expires': string;
|
|
534
|
+
};
|
|
535
|
+
/**
|
|
536
|
+
* Locales embutidos no host-kit. O `en` é o default; o `pt-BR` está disponível
|
|
537
|
+
* com `i18n: { locale: 'pt-BR' }` sem nenhuma config de mensagens extra. Os
|
|
538
|
+
* overrides/locales do host (via `I18nConfig.messages`) são mesclados por cima.
|
|
539
|
+
*/
|
|
540
|
+
export declare const BUILTIN_MESSAGES: Record<string, AuthMessages>;
|
|
541
|
+
/**
|
|
542
|
+
* Resolve o catálogo ativo. Começa do catálogo embutido do locale selecionado
|
|
543
|
+
* (ou do default `en` quando o locale não é embutido), depois mescla os
|
|
544
|
+
* overrides do host por cima. Sem config, retorna o default `en` intacto.
|
|
545
|
+
* Chaves omitidas caem no default `en` (fallback de cobertura).
|
|
252
546
|
*/
|
|
253
547
|
export declare function resolveMessages(i18n?: I18nConfig): AuthMessages;
|
|
254
548
|
/**
|
package/build/src/host/i18n.js
CHANGED
|
@@ -3,17 +3,299 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Todas as strings visíveis ao usuário das views Edge (e as mensagens de
|
|
5
5
|
* flash/erro produzidas pelos controllers) vivem num catálogo achatado de
|
|
6
|
-
* chaves pontilhadas. O default embutido é
|
|
7
|
-
* funcionando SEM nenhuma configuração. O
|
|
8
|
-
*
|
|
6
|
+
* chaves pontilhadas. O default embutido é inglês (`en`) — os apps continuam
|
|
7
|
+
* funcionando SEM nenhuma configuração. O pt-BR é um locale embutido: basta
|
|
8
|
+
* `i18n: { locale: 'pt-BR' }`. O host também pode sobrescrever chaves pontuais
|
|
9
|
+
* ou fornecer locales inteiros (ex.: `fr`) via `I18nConfig`.
|
|
9
10
|
*/
|
|
10
11
|
/** Locale default do host-kit. */
|
|
11
|
-
export const DEFAULT_LOCALE = '
|
|
12
|
+
export const DEFAULT_LOCALE = 'en';
|
|
12
13
|
/**
|
|
13
|
-
* Catálogo default (
|
|
14
|
+
* Catálogo default (inglês) — cobre TODAS as strings visíveis ao usuário das
|
|
14
15
|
* views e as mensagens de flash/erro dos controllers. Chaves agrupadas por tela.
|
|
15
16
|
*/
|
|
16
17
|
export const DEFAULT_MESSAGES = {
|
|
18
|
+
// Comum / fallback de marca.
|
|
19
|
+
'common.app_fallback': 'Auth',
|
|
20
|
+
'common.brand_eyebrow': 'Auth',
|
|
21
|
+
// Tela de login (interaction OIDC: identifier + password).
|
|
22
|
+
'login.page_title': 'Login',
|
|
23
|
+
'login.title': 'Login',
|
|
24
|
+
'login.identifier_intro': 'Enter your email to continue.',
|
|
25
|
+
'login.email_label': 'Email',
|
|
26
|
+
'login.identifier_submit': 'Continue',
|
|
27
|
+
'login.create_account': 'Create account',
|
|
28
|
+
'login.forgot_password': 'Forgot password',
|
|
29
|
+
'login.divider_or': 'or',
|
|
30
|
+
'login.google': 'Sign in with Google',
|
|
31
|
+
'login.greeting': 'Hi, {name}',
|
|
32
|
+
'login.switch_account': 'Switch account',
|
|
33
|
+
'login.password_label': 'Password',
|
|
34
|
+
'login.submit': 'Log in',
|
|
35
|
+
// Tela de cadastro (signup).
|
|
36
|
+
'signup.page_title': 'Create account',
|
|
37
|
+
'signup.title': 'Create account',
|
|
38
|
+
'signup.intro': 'Fill in your details to get started.',
|
|
39
|
+
'signup.name_label': 'Name',
|
|
40
|
+
'signup.email_label': 'Email',
|
|
41
|
+
'signup.password_label': 'Password',
|
|
42
|
+
'signup.submit': 'Create account',
|
|
43
|
+
'signup.have_account': 'I already have an account',
|
|
44
|
+
// Recuperação de senha (forgot).
|
|
45
|
+
'forgot.page_title': 'Reset password',
|
|
46
|
+
'forgot.sent_title': 'Email sent',
|
|
47
|
+
'forgot.sent_body': 'If the email exists, we will send reset instructions.',
|
|
48
|
+
'forgot.title': 'Reset password',
|
|
49
|
+
'forgot.intro': 'We will send you a link to reset your password.',
|
|
50
|
+
'forgot.email_label': 'Email',
|
|
51
|
+
'forgot.submit': 'Send link',
|
|
52
|
+
// Redefinição de senha (reset).
|
|
53
|
+
'reset.page_title': 'Reset password',
|
|
54
|
+
'reset.done_title': 'Password reset',
|
|
55
|
+
'reset.done_body': 'You can now log in with your new password.',
|
|
56
|
+
'reset.title': 'New password',
|
|
57
|
+
'reset.intro': 'Choose a new password for your account.',
|
|
58
|
+
'reset.password_label': 'Password',
|
|
59
|
+
'reset.submit': 'Reset',
|
|
60
|
+
// Verificação de e-mail (verify-email).
|
|
61
|
+
'verify_email.page_title': 'Verify email',
|
|
62
|
+
'verify_email.verified_title': 'Email verified',
|
|
63
|
+
'verify_email.verified_body': 'Your email was confirmed successfully.',
|
|
64
|
+
'verify_email.invalid_title': 'Invalid link',
|
|
65
|
+
'verify_email.invalid_body': 'The verification link is invalid or has already been used.',
|
|
66
|
+
// Desafio de MFA no fluxo de login (mfa-challenge).
|
|
67
|
+
'mfa_challenge.page_title': 'Two-factor verification',
|
|
68
|
+
'mfa_challenge.title': 'Two-factor verification',
|
|
69
|
+
'mfa_challenge.intro': 'Open your authenticator app and enter the 6-digit code.',
|
|
70
|
+
'mfa_challenge.code_label': 'Code',
|
|
71
|
+
'mfa_challenge.submit': 'Verify',
|
|
72
|
+
'mfa_challenge.recovery_summary': 'Use a recovery code',
|
|
73
|
+
'mfa_challenge.recovery_submit': 'Log in with a recovery code',
|
|
74
|
+
'mfa_challenge.passkey_button': 'Use passkey',
|
|
75
|
+
'mfa_challenge.passkey_error': 'Could not authenticate with the passkey. Please try again.',
|
|
76
|
+
// Consent (autorização de cliente OIDC).
|
|
77
|
+
'consent.page_title': 'Authorize',
|
|
78
|
+
'consent.title': 'Authorize access',
|
|
79
|
+
// `{app}` é interpolado com o nome do app já envolto em <strong> (renderizado
|
|
80
|
+
// raw na view). O nome vem do branding (config-trusted).
|
|
81
|
+
'consent.body': 'The app <strong>{app}</strong> wants to access your account.',
|
|
82
|
+
'consent.submit': 'Authorize',
|
|
83
|
+
// Console de conta — login (account/login).
|
|
84
|
+
'account.login.page_title': 'My account',
|
|
85
|
+
'account.login.title': 'My account',
|
|
86
|
+
'account.login.intro': 'Manage your access tokens.',
|
|
87
|
+
'account.login.email_label': 'Email',
|
|
88
|
+
'account.login.password_label': 'Password',
|
|
89
|
+
'account.login.submit': 'Log in',
|
|
90
|
+
// Console de conta — tokens (account/tokens).
|
|
91
|
+
'account.tokens.page_title': 'Access tokens',
|
|
92
|
+
'account.tokens.title': 'Access tokens',
|
|
93
|
+
'account.tokens.logout': 'Log out',
|
|
94
|
+
'account.tokens.security': 'Security',
|
|
95
|
+
'account.tokens.created_notice': 'Token created — copy it now, it will not be shown again:',
|
|
96
|
+
'account.tokens.name_placeholder': 'Token name (e.g. CI deploy)',
|
|
97
|
+
'account.tokens.create': 'Create',
|
|
98
|
+
'account.tokens.empty': 'No tokens yet.',
|
|
99
|
+
'account.tokens.created_at': 'Created on {date}',
|
|
100
|
+
'account.tokens.last_used': '· last used {date}',
|
|
101
|
+
'account.tokens.never_used': '· never used',
|
|
102
|
+
'account.tokens.scopes': 'Scopes: {scopes}',
|
|
103
|
+
'account.tokens.audience': 'Audience: {audience}',
|
|
104
|
+
'account.tokens.revoke': 'Revoke',
|
|
105
|
+
// Console de conta — segurança (account/security): senha + e-mail.
|
|
106
|
+
'account.security.page_title': 'Account security',
|
|
107
|
+
'account.security.title': 'Account security',
|
|
108
|
+
'account.security.logout': 'Log out',
|
|
109
|
+
'account.security.current_email': 'Current email: {email}',
|
|
110
|
+
'account.security.not_supported': 'Changing password and email is not available in this installation.',
|
|
111
|
+
'account.security.password_section': 'Change password',
|
|
112
|
+
'account.security.current_password_label': 'Current password',
|
|
113
|
+
'account.security.new_password_label': 'New password',
|
|
114
|
+
'account.security.change_password_submit': 'Change password',
|
|
115
|
+
'account.security.password_changed': 'Password changed successfully.',
|
|
116
|
+
'account.security.email_section': 'Change email',
|
|
117
|
+
'account.security.email_intro': 'We will send a confirmation link to the new address. The change only takes effect after confirmation.',
|
|
118
|
+
'account.security.new_email_label': 'New email',
|
|
119
|
+
'account.security.email_password_label': 'Current password',
|
|
120
|
+
'account.security.change_email_submit': 'Request email change',
|
|
121
|
+
'account.security.email_change_requested': 'We sent a confirmation link to {email}. Click it to complete the change.',
|
|
122
|
+
'account.security.email_changed': 'Email changed successfully.',
|
|
123
|
+
// Confirmação de troca de e-mail (account/email-confirmed).
|
|
124
|
+
'account.email_confirmed.page_title': 'Email confirmation',
|
|
125
|
+
'account.email_confirmed.ok_title': 'Email changed',
|
|
126
|
+
'account.email_confirmed.ok_body': 'Your new email has been confirmed and is now active.',
|
|
127
|
+
'account.email_confirmed.invalid_title': 'Invalid link',
|
|
128
|
+
'account.email_confirmed.invalid_body': 'The confirmation link is invalid or has already been used.',
|
|
129
|
+
// Console de conta — MFA (account/mfa).
|
|
130
|
+
'account.mfa.page_title': 'Two-factor verification',
|
|
131
|
+
'account.mfa.title': 'Two-factor verification',
|
|
132
|
+
'account.mfa.logout': 'Log out',
|
|
133
|
+
'account.mfa.recovery_codes_notice': 'Save your recovery codes — they will not be shown again:',
|
|
134
|
+
'account.mfa.enroll_intro': 'Scan the QR code with your authenticator app (Google Authenticator, 1Password, etc.).',
|
|
135
|
+
'account.mfa.qr_alt': 'TOTP QR code',
|
|
136
|
+
'account.mfa.manual_intro': 'Or enter it manually:',
|
|
137
|
+
'account.mfa.confirm_code_label': 'Confirmation code',
|
|
138
|
+
'account.mfa.activate': 'Enable two-factor verification',
|
|
139
|
+
'account.mfa.enabled_html': 'Two-factor verification is <span class="font-semibold text-emerald-700">enabled</span> on this account.',
|
|
140
|
+
'account.mfa.disable': 'Disable',
|
|
141
|
+
'account.mfa.disabled_intro': 'Two-factor verification is disabled. Enable it to protect your account with an authenticator app.',
|
|
142
|
+
'account.mfa.enable': 'Enable two-factor verification',
|
|
143
|
+
// Console de conta — passkeys (WebAuthn) na tela de MFA.
|
|
144
|
+
'mfa.passkey.section_title': 'Passkeys',
|
|
145
|
+
'mfa.passkey.section_intro': 'Use a passkey (biometrics, device PIN, or security key) as a second factor, without typing codes.',
|
|
146
|
+
'mfa.passkey.add': 'Add passkey',
|
|
147
|
+
'mfa.passkey.remove': 'Remove',
|
|
148
|
+
'mfa.passkey.empty': 'No passkeys registered.',
|
|
149
|
+
'mfa.passkey.unnamed': 'Passkey',
|
|
150
|
+
'mfa.passkey.created_at': 'Created on {date}',
|
|
151
|
+
'mfa.passkey.register_error': 'Could not register the passkey. Please try again.',
|
|
152
|
+
'mfa.passkey.unsupported': 'Your browser does not support passkeys.',
|
|
153
|
+
// Console admin (B6) — navegação compartilhada.
|
|
154
|
+
'admin.nav.dashboard': 'Dashboard',
|
|
155
|
+
'admin.nav.users': 'Users',
|
|
156
|
+
'admin.nav.clients': 'Clients',
|
|
157
|
+
'admin.nav.audit': 'Audit',
|
|
158
|
+
'admin.nav.logout': 'Log out',
|
|
159
|
+
// Console admin — dashboard.
|
|
160
|
+
'admin.dashboard.page_title': 'Admin dashboard',
|
|
161
|
+
'admin.dashboard.title': 'Admin dashboard',
|
|
162
|
+
'admin.dashboard.users_count': 'Users',
|
|
163
|
+
'admin.dashboard.clients_count': 'Clients',
|
|
164
|
+
'admin.dashboard.audit_count': 'Audit events',
|
|
165
|
+
'admin.dashboard.recent_title': 'Recent events',
|
|
166
|
+
// Console admin — usuários.
|
|
167
|
+
'admin.users.page_title': 'Users',
|
|
168
|
+
'admin.users.title': 'Users',
|
|
169
|
+
'admin.users.search_placeholder': 'Search by email',
|
|
170
|
+
'admin.users.search': 'Search',
|
|
171
|
+
'admin.users.empty': 'No users found.',
|
|
172
|
+
'admin.users.roles_placeholder': 'Roles (comma-separated)',
|
|
173
|
+
'admin.users.save_roles': 'Save roles',
|
|
174
|
+
'admin.users.sessions': 'Sessions',
|
|
175
|
+
// Console admin — sessões/grants ativos de uma conta.
|
|
176
|
+
'admin.sessions.page_title': 'Active sessions',
|
|
177
|
+
'admin.sessions.title': 'Active sessions',
|
|
178
|
+
'admin.sessions.account': 'Account: {email}',
|
|
179
|
+
'admin.sessions.back': 'Back to users',
|
|
180
|
+
'admin.sessions.not_supported': 'The configured OIDC adapter does not support enumeration — session inspection is unavailable.',
|
|
181
|
+
'admin.sessions.revoked_notice': 'Revoked: {sessions} session(s), {grants} grant(s), {accessTokens} access token(s), {refreshTokens} refresh token(s).',
|
|
182
|
+
'admin.sessions.sessions_section': 'Sessions (IdP login)',
|
|
183
|
+
'admin.sessions.sessions_empty': 'No active sessions.',
|
|
184
|
+
'admin.sessions.session_login_ts': 'Login: {date}',
|
|
185
|
+
'admin.sessions.session_amr': 'Methods: {amr}',
|
|
186
|
+
'admin.sessions.grants_section': 'Grants (per-client authorizations)',
|
|
187
|
+
'admin.sessions.grants_empty': 'No active grants.',
|
|
188
|
+
'admin.sessions.grant_client': 'Client: {clientId}',
|
|
189
|
+
'admin.sessions.grant_tokens': '{accessTokens} access · {refreshTokens} refresh',
|
|
190
|
+
'admin.sessions.revoke_all': 'Revoke all sessions and grants',
|
|
191
|
+
'admin.sessions.revoke_confirm': 'Revoke all sessions and grants for this account? The user will need to log in again and issued tokens will stop working.',
|
|
192
|
+
// Console admin — clients.
|
|
193
|
+
'admin.clients.page_title': 'OAuth clients',
|
|
194
|
+
'admin.clients.title': 'OAuth clients',
|
|
195
|
+
'admin.clients.empty': 'No clients configured.',
|
|
196
|
+
'admin.clients.confidential': 'Confidential',
|
|
197
|
+
'admin.clients.public': 'Public',
|
|
198
|
+
'admin.clients.grants': 'Grants: {grants}',
|
|
199
|
+
'admin.clients.redirect_uris': 'Redirects: {uris}',
|
|
200
|
+
'admin.clients.dynamic_notice': 'Dynamic client registration (RFC 7591) is on — clients registered via /reg are persisted in the adapter and appear in the dynamic section below.',
|
|
201
|
+
'admin.clients.static_section': 'Static clients (config)',
|
|
202
|
+
'admin.clients.dynamic_section': 'Dynamic clients (adapter)',
|
|
203
|
+
'admin.clients.dynamic_empty': 'No dynamic clients persisted.',
|
|
204
|
+
'admin.clients.dynamic_not_supported': 'The configured OIDC adapter does not support client enumeration — dynamic management is unavailable.',
|
|
205
|
+
'admin.clients.new': 'New client',
|
|
206
|
+
'admin.clients.new_title': 'New OIDC client',
|
|
207
|
+
'admin.clients.edit_title': 'Edit OIDC client',
|
|
208
|
+
'admin.clients.edit': 'Edit',
|
|
209
|
+
'admin.clients.delete': 'Delete',
|
|
210
|
+
'admin.clients.delete_confirm': 'Delete this client? This action cannot be undone.',
|
|
211
|
+
'admin.clients.regenerate_secret': 'Regenerate secret',
|
|
212
|
+
'admin.clients.regenerate_confirm': 'Regenerate the secret? The current secret will stop working immediately.',
|
|
213
|
+
'admin.clients.back': 'Back',
|
|
214
|
+
'admin.clients.cancel': 'Cancel',
|
|
215
|
+
'admin.clients.save': 'Save',
|
|
216
|
+
'admin.clients.create': 'Create client',
|
|
217
|
+
'admin.clients.secret_once_title': 'Save the client_secret now',
|
|
218
|
+
'admin.clients.secret_once_notice': 'This is the only time the secret is shown. Copy it now — it cannot be retrieved later.',
|
|
219
|
+
'admin.clients.field_client_id': 'Client ID',
|
|
220
|
+
'admin.clients.field_client_id_placeholder': 'leave blank to generate automatically',
|
|
221
|
+
'admin.clients.field_client_id_help': 'Optional. If empty, a random identifier will be generated.',
|
|
222
|
+
'admin.clients.field_redirect_uris': 'Redirect URIs',
|
|
223
|
+
'admin.clients.field_redirect_uris_help': 'One URI per line.',
|
|
224
|
+
'admin.clients.field_post_logout_uris': 'Post-logout redirect URIs',
|
|
225
|
+
'admin.clients.field_post_logout_uris_help': 'One URI per line (optional).',
|
|
226
|
+
'admin.clients.field_grant_types': 'Grant types',
|
|
227
|
+
'admin.clients.field_auth_method': 'Token endpoint auth method',
|
|
228
|
+
// Console admin — auditoria.
|
|
229
|
+
'admin.audit.page_title': 'Audit',
|
|
230
|
+
'admin.audit.title': 'Audit log',
|
|
231
|
+
'admin.audit.type_placeholder': 'Filter by type',
|
|
232
|
+
'admin.audit.subject_placeholder': 'Filter by subject (accountId)',
|
|
233
|
+
'admin.audit.filter': 'Filter',
|
|
234
|
+
'admin.audit.empty': 'No events found.',
|
|
235
|
+
'admin.audit.not_supported': 'The configured audit sink does not support querying.',
|
|
236
|
+
// Console admin — paginação compartilhada.
|
|
237
|
+
'admin.pagination.page': 'Page {page} of {total}',
|
|
238
|
+
'admin.pagination.prev': 'Previous',
|
|
239
|
+
'admin.pagination.next': 'Next',
|
|
240
|
+
// Device Authorization Grant (RFC 8628) — telas servidas pelo oidc-provider.
|
|
241
|
+
'device.input.title': 'Sign in to the device',
|
|
242
|
+
'device.input.intro': 'Enter the code shown on your device.',
|
|
243
|
+
'device.input.submit': 'Continue',
|
|
244
|
+
'device.input.error_invalid': 'The code you entered is incorrect. Please try again.',
|
|
245
|
+
'device.input.error_aborted': 'The login request was aborted.',
|
|
246
|
+
'device.input.error_generic': 'An error occurred while processing your request.',
|
|
247
|
+
'device.confirm.title': 'Confirm device',
|
|
248
|
+
'device.confirm.body': 'The code below should be displayed on your device. Confirm only if you recognize it.',
|
|
249
|
+
'device.confirm.submit': 'Continue',
|
|
250
|
+
'device.confirm.abort': 'Cancel',
|
|
251
|
+
'device.success.title': 'Login complete',
|
|
252
|
+
'device.success.body': 'You are signed in. You can return to your device.',
|
|
253
|
+
// Step-up auth (acr_values): cliente exige MFA mas a conta não tem MFA enrolado.
|
|
254
|
+
'mfa_challenge.required_no_enrollment': 'This client requires two-factor verification. Set up MFA in your account console to continue.',
|
|
255
|
+
// Mensagens de erro/flash produzidas pelos controllers.
|
|
256
|
+
'errors.invalid_credentials': 'Invalid credentials',
|
|
257
|
+
'errors.invalid_code': 'Invalid code',
|
|
258
|
+
'errors.email_taken': 'Email already registered',
|
|
259
|
+
'errors.signup_failed': 'Could not create the account',
|
|
260
|
+
'errors.invalid_or_expired_token': 'Invalid or expired token',
|
|
261
|
+
'errors.account_locked': 'Account temporarily locked due to too many attempts. Try again in {seconds}s.',
|
|
262
|
+
'errors.session_expired': 'Session expired',
|
|
263
|
+
'errors.challenge_expired': 'Challenge expired',
|
|
264
|
+
'errors.passkeys_unavailable': 'Passkeys unavailable',
|
|
265
|
+
'errors.no_passkey_registered': 'No passkey registered',
|
|
266
|
+
// Assuntos/corpos de e-mail transacional (default_mailer).
|
|
267
|
+
'mail.common.link_fallback': "If the button does not work, copy and paste this link into your browser:",
|
|
268
|
+
'mail.reset.subject': 'Reset your password',
|
|
269
|
+
'mail.reset.heading': 'Reset your password',
|
|
270
|
+
'mail.reset.intro': 'We received a request to reset the password for your account.',
|
|
271
|
+
'mail.reset.cta': 'Reset password',
|
|
272
|
+
'mail.reset.fallback': 'If you did not request this, you can ignore this email.',
|
|
273
|
+
'mail.reset.expires': 'This link expires in {minutes} minutes.',
|
|
274
|
+
'mail.verify.subject': 'Verify your email',
|
|
275
|
+
'mail.verify.heading': 'Verify your email',
|
|
276
|
+
'mail.verify.intro': 'Confirm your email address to finish setting up your account.',
|
|
277
|
+
'mail.verify.cta': 'Verify email',
|
|
278
|
+
'mail.verify.fallback': 'If you did not create this account, you can ignore this email.',
|
|
279
|
+
'mail.verify.expires': 'This link expires in {minutes} minutes.',
|
|
280
|
+
'mail.new_login.subject': 'New login to your account',
|
|
281
|
+
'mail.new_login.heading': 'New login detected',
|
|
282
|
+
'mail.new_login.intro': 'We detected a new login to your account.',
|
|
283
|
+
'mail.new_login.when': 'When: {date}',
|
|
284
|
+
'mail.new_login.ip': 'IP address: {ip}',
|
|
285
|
+
'mail.new_login.device': 'Device: {device}',
|
|
286
|
+
'mail.new_login.fallback': 'If this was you, no action is needed. If not, reset your password right away.',
|
|
287
|
+
'mail.email_change.subject': 'Confirm your new email',
|
|
288
|
+
'mail.email_change.heading': 'Confirm your new email',
|
|
289
|
+
'mail.email_change.intro': 'We received a request to change the email address on your account. Confirm the new address below.',
|
|
290
|
+
'mail.email_change.cta': 'Confirm new email',
|
|
291
|
+
'mail.email_change.fallback': 'If you did not request this, you can ignore this email.',
|
|
292
|
+
'mail.email_change.expires': 'This link expires in {minutes} minutes.',
|
|
293
|
+
};
|
|
294
|
+
/**
|
|
295
|
+
* Catálogo embutido pt-BR. Espelha TODAS as chaves do default `en`. Ativado
|
|
296
|
+
* com `i18n: { locale: 'pt-BR' }` sem nenhuma config extra de mensagens.
|
|
297
|
+
*/
|
|
298
|
+
export const PT_BR_MESSAGES = {
|
|
17
299
|
// Comum / fallback de marca.
|
|
18
300
|
'common.app_fallback': 'Auth',
|
|
19
301
|
'common.brand_eyebrow': 'Auth',
|
|
@@ -75,8 +357,6 @@ export const DEFAULT_MESSAGES = {
|
|
|
75
357
|
// Consent (autorização de cliente OIDC).
|
|
76
358
|
'consent.page_title': 'Autorizar',
|
|
77
359
|
'consent.title': 'Autorizar acesso',
|
|
78
|
-
// `{app}` é interpolado com o nome do app já envolto em <strong> (renderizado
|
|
79
|
-
// raw na view). O nome vem do branding (config-trusted).
|
|
80
360
|
'consent.body': 'O app <strong>{app}</strong> quer acessar sua conta.',
|
|
81
361
|
'consent.submit': 'Autorizar',
|
|
82
362
|
// Console de conta — login (account/login).
|
|
@@ -258,20 +538,63 @@ export const DEFAULT_MESSAGES = {
|
|
|
258
538
|
'errors.signup_failed': 'Não foi possível criar a conta',
|
|
259
539
|
'errors.invalid_or_expired_token': 'Token inválido ou expirado',
|
|
260
540
|
'errors.account_locked': 'Conta temporariamente bloqueada por excesso de tentativas. Tente novamente em {seconds}s.',
|
|
541
|
+
'errors.session_expired': 'Sessão expirada',
|
|
542
|
+
'errors.challenge_expired': 'Desafio expirado',
|
|
543
|
+
'errors.passkeys_unavailable': 'Passkeys indisponíveis',
|
|
544
|
+
'errors.no_passkey_registered': 'Nenhuma passkey registrada',
|
|
545
|
+
// Assuntos/corpos de e-mail transacional (default_mailer).
|
|
546
|
+
'mail.common.link_fallback': 'Se o botão não funcionar, copie e cole este link no navegador:',
|
|
547
|
+
'mail.reset.subject': 'Redefinição de senha',
|
|
548
|
+
'mail.reset.heading': 'Redefinição de senha',
|
|
549
|
+
'mail.reset.intro': 'Recebemos um pedido para redefinir a senha da sua conta.',
|
|
550
|
+
'mail.reset.cta': 'Redefinir senha',
|
|
551
|
+
'mail.reset.fallback': 'Se você não solicitou isso, pode ignorar este e-mail.',
|
|
552
|
+
'mail.reset.expires': 'Este link expira em {minutes} minutos.',
|
|
553
|
+
'mail.verify.subject': 'Verifique seu e-mail',
|
|
554
|
+
'mail.verify.heading': 'Verifique seu e-mail',
|
|
555
|
+
'mail.verify.intro': 'Confirme seu endereço de e-mail para concluir a configuração da conta.',
|
|
556
|
+
'mail.verify.cta': 'Verificar e-mail',
|
|
557
|
+
'mail.verify.fallback': 'Se você não criou esta conta, pode ignorar este e-mail.',
|
|
558
|
+
'mail.verify.expires': 'Este link expira em {minutes} minutos.',
|
|
559
|
+
'mail.new_login.subject': 'Novo login na sua conta',
|
|
560
|
+
'mail.new_login.heading': 'Novo login detectado',
|
|
561
|
+
'mail.new_login.intro': 'Detectamos um novo login na sua conta.',
|
|
562
|
+
'mail.new_login.when': 'Quando: {date}',
|
|
563
|
+
'mail.new_login.ip': 'Endereço IP: {ip}',
|
|
564
|
+
'mail.new_login.device': 'Dispositivo: {device}',
|
|
565
|
+
'mail.new_login.fallback': 'Se foi você, nenhuma ação é necessária. Caso contrário, redefina sua senha imediatamente.',
|
|
566
|
+
'mail.email_change.subject': 'Confirme seu novo e-mail',
|
|
567
|
+
'mail.email_change.heading': 'Confirme seu novo e-mail',
|
|
568
|
+
'mail.email_change.intro': 'Recebemos um pedido para trocar o e-mail da sua conta. Confirme o novo endereço abaixo.',
|
|
569
|
+
'mail.email_change.cta': 'Confirmar novo e-mail',
|
|
570
|
+
'mail.email_change.fallback': 'Se você não solicitou isso, pode ignorar este e-mail.',
|
|
571
|
+
'mail.email_change.expires': 'Este link expira em {minutes} minutos.',
|
|
572
|
+
};
|
|
573
|
+
/**
|
|
574
|
+
* Locales embutidos no host-kit. O `en` é o default; o `pt-BR` está disponível
|
|
575
|
+
* com `i18n: { locale: 'pt-BR' }` sem nenhuma config de mensagens extra. Os
|
|
576
|
+
* overrides/locales do host (via `I18nConfig.messages`) são mesclados por cima.
|
|
577
|
+
*/
|
|
578
|
+
export const BUILTIN_MESSAGES = {
|
|
579
|
+
en: DEFAULT_MESSAGES,
|
|
580
|
+
'pt-BR': PT_BR_MESSAGES,
|
|
261
581
|
};
|
|
262
582
|
/**
|
|
263
|
-
* Resolve o catálogo ativo
|
|
264
|
-
* default
|
|
265
|
-
*
|
|
583
|
+
* Resolve o catálogo ativo. Começa do catálogo embutido do locale selecionado
|
|
584
|
+
* (ou do default `en` quando o locale não é embutido), depois mescla os
|
|
585
|
+
* overrides do host por cima. Sem config, retorna o default `en` intacto.
|
|
586
|
+
* Chaves omitidas caem no default `en` (fallback de cobertura).
|
|
266
587
|
*/
|
|
267
588
|
export function resolveMessages(i18n) {
|
|
268
|
-
const base = { ...DEFAULT_MESSAGES };
|
|
269
589
|
const locale = i18n?.locale ?? DEFAULT_LOCALE;
|
|
590
|
+
// Base: sempre o default `en` para garantir cobertura total das chaves; o
|
|
591
|
+
// catálogo embutido do locale (ex.: pt-BR) é mesclado por cima.
|
|
592
|
+
const base = { ...DEFAULT_MESSAGES, ...(BUILTIN_MESSAGES[locale] ?? {}) };
|
|
270
593
|
const overrides = i18n?.messages?.[locale];
|
|
271
594
|
if (!overrides)
|
|
272
595
|
return base;
|
|
273
596
|
// Mescla só valores definidos (o `Partial` permite undefined); chaves omitidas
|
|
274
|
-
// seguem caindo no
|
|
597
|
+
// seguem caindo no catálogo base.
|
|
275
598
|
for (const [key, value] of Object.entries(overrides)) {
|
|
276
599
|
if (value !== undefined)
|
|
277
600
|
base[key] = value;
|
|
@@ -5,11 +5,11 @@ function renderDashboardHtml(snapshot) {
|
|
|
5
5
|
const histograms = Object.entries(snapshot.histograms)
|
|
6
6
|
.map(([k, h]) => `<tr><td>${k}</td><td>${h.count}</td><td>${h.sum}</td><td>${h.min}</td><td>${h.max}</td></tr>`)
|
|
7
7
|
.join('');
|
|
8
|
-
return `<!doctype html><html lang="
|
|
8
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="refresh" content="5"><title>AuthKit — Metrics</title>
|
|
9
9
|
<style>body{font-family:system-ui,sans-serif;margin:2rem;color:#111}h1{font-size:1.2rem}table{border-collapse:collapse;margin:1rem 0;width:100%}th,td{border:1px solid #ddd;padding:.4rem .6rem;text-align:left;font-size:.9rem}th{background:#f5f5f5}</style>
|
|
10
|
-
</head><body><h1>AuthKit —
|
|
11
|
-
<h2>Counters</h2><table><thead><tr><th>
|
|
12
|
-
<h2>Histograms</h2><table><thead><tr><th>
|
|
10
|
+
</head><body><h1>AuthKit — Metrics</h1><p>Updated: ${snapshot.updatedAt ? new Date(snapshot.updatedAt).toISOString() : '—'}</p>
|
|
11
|
+
<h2>Counters</h2><table><thead><tr><th>Metric</th><th>Total</th></tr></thead><tbody>${counters || '<tr><td colspan="2">—</td></tr>'}</tbody></table>
|
|
12
|
+
<h2>Histograms</h2><table><thead><tr><th>Metric</th><th>Count</th><th>Sum</th><th>Min</th><th>Max</th></tr></thead><tbody>${histograms || '<tr><td colspan="5">—</td></tr>'}</tbody></table>
|
|
13
13
|
</body></html>`;
|
|
14
14
|
}
|
|
15
15
|
export default class MetricsController {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dudousxd/adonis-authkit-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
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
5
|
"license": "MIT",
|
|
6
6
|
"author": "dudousxd",
|