pgkiln 0.63.0 → 0.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/SECURITY.md +2 -0
  2. package/db/migrations/132_app_user_mfa.sql +58 -0
  3. package/db/migrations/133_app_user_passkeys_stepup.sql +54 -0
  4. package/docs/apex-feature-parity.md +6 -1
  5. package/docs/guide/08-security.md +81 -5
  6. package/docs/guide/09-reference.md +5 -1
  7. package/docs/guide/11-from-apex.md +2 -1
  8. package/docs/guide/12-development.md +13 -1
  9. package/docs/guide/13-rest-api.md +4 -0
  10. package/package.json +1 -1
  11. package/public/app.css +10 -0
  12. package/public/app.js +63 -0
  13. package/public/builder.js +1 -62
  14. package/src/account-mfa.ts +184 -0
  15. package/src/api.ts +9 -2
  16. package/src/builder/advisor.ts +8 -0
  17. package/src/builder/api.ts +3 -1
  18. package/src/builder/components.ts +1 -0
  19. package/src/builder/i18n/de.ts +6 -0
  20. package/src/builder/i18n/en.ts +6 -0
  21. package/src/builder/i18n/es.ts +6 -0
  22. package/src/builder/i18n/fr.ts +6 -0
  23. package/src/builder/i18n/nl.ts +6 -0
  24. package/src/builder/i18n/pt.ts +6 -0
  25. package/src/builder/login-page.ts +32 -3
  26. package/src/builder/rest-modules.ts +1 -1
  27. package/src/builder/security.ts +3 -19
  28. package/src/builder/shared.ts +5 -3
  29. package/src/builder/users.ts +34 -3
  30. package/src/i18n/ar.ts +65 -0
  31. package/src/i18n/cs.ts +65 -0
  32. package/src/i18n/da.ts +65 -0
  33. package/src/i18n/de.ts +65 -0
  34. package/src/i18n/el.ts +65 -0
  35. package/src/i18n/es.ts +65 -0
  36. package/src/i18n/fi.ts +65 -0
  37. package/src/i18n/fr.ts +65 -0
  38. package/src/i18n/he.ts +65 -0
  39. package/src/i18n/it.ts +65 -0
  40. package/src/i18n/ja.ts +65 -0
  41. package/src/i18n/ko.ts +65 -0
  42. package/src/i18n/nb.ts +65 -0
  43. package/src/i18n/pl.ts +65 -0
  44. package/src/i18n/pt.ts +65 -0
  45. package/src/i18n/ru.ts +65 -0
  46. package/src/i18n/sv.ts +65 -0
  47. package/src/i18n/tr.ts +65 -0
  48. package/src/i18n/uk.ts +65 -0
  49. package/src/i18n/zh.ts +65 -0
  50. package/src/i18n.ts +130 -0
  51. package/src/metadata.ts +11 -2
  52. package/src/mfa.ts +94 -65
  53. package/src/remember.ts +1 -1
  54. package/src/runtime/account.ts +7 -1
  55. package/src/runtime/authz.ts +23 -4
  56. package/src/runtime/routes.ts +21 -7
  57. package/src/runtime/two-step.ts +702 -0
  58. package/src/sharedlogin.ts +1 -1
  59. package/src/webauthn.ts +20 -0
package/src/i18n/sv.ts CHANGED
@@ -805,4 +805,69 @@ export const sv: Record<MessageKey, string> = {
805
805
  'ai_filter.per_group': "{what} per grupp",
806
806
  'ai_filter.needs_number': "{fn} kräver en kolumn med tal.",
807
807
  'ai_filter.invalid': "Den ändringen går inte: {why}",
808
+ // (sprint 130) two-step sign-in
809
+ "mfa.step_title": "Inloggning i två steg",
810
+ "mfa.step_intro": "Ange den 6-siffriga koden från din autentiseringsapp, eller en av dina återställningskoder.",
811
+ "mfa.code": "Kod",
812
+ "mfa.verify": "Verifiera",
813
+ "mfa.wrong": "Koden är fel eller har redan använts.",
814
+ "mfa.expired": "Inloggningen tog för lång tid. Logga in igen.",
815
+ "mfa.enrol_title": "Konfigurera inloggning i två steg",
816
+ "mfa.enrol_intro": "Det här programmet ber om en kod från en autentiseringsapp när du loggar in. Lägg till en nu för att fortsätta.",
817
+ "mfa.scan": "Skanna QR-koden med en autentiseringsapp, till exempel Google Authenticator, Microsoft Authenticator eller 1Password.",
818
+ "mfa.or_enter": "Eller ange den här nyckeln:",
819
+ "mfa.enter_code": "Ange den 6-siffriga kod som appen visar.",
820
+ "mfa.qr_label": "QR-kod för en autentiseringsapp",
821
+ "mfa.codes_title": "Återställningskoder",
822
+ "mfa.codes_once": "Förvara koderna på ett säkert ställe. Var och en loggar in dig en gång när du inte har telefonen. De visas bara nu.",
823
+ "mfa.codes_saved": "Jag har sparat koderna",
824
+ "mfa.continue": "Fortsätt",
825
+ "mfa.no_key": "Inloggning i två steg är inte konfigurerad på den här servern än. Kontakta administratören.",
826
+ "mfa.app_name": "Namn",
827
+ "mfa.app_default": "Autentiseringsapp",
828
+ "mfa.on": "När du loggar in anger du också en kod från din autentiseringsapp.",
829
+ "mfa.off": "Lägg till en autentiseringsapp för att skydda ditt konto med ett andra steg när du loggar in.",
830
+ "mfa.required_you": "Det här programmet kräver inloggning i två steg för dig: lägg till en autentiseringsapp.",
831
+ "mfa.add_app": "Lägg till en autentiseringsapp",
832
+ "mfa.confirm_app": "Lägg till app",
833
+ "mfa.cancel": "Avbryt",
834
+ "mfa.remove": "Ta bort",
835
+ "mfa.remove_code": "En aktuell kod eller en återställningskod",
836
+ "mfa.removed": "Autentiseringsappen togs bort.",
837
+ "mfa.keep_one": "Det här programmet kräver inloggning i två steg för dig: lägg till en annan app innan du tar bort den här.",
838
+ "mfa.added": "Autentiseringsappen lades till.",
839
+ "mfa.codes_left": "Återstående återställningskoder: {n}",
840
+ "mfa.codes_new": "Nya återställningskoder",
841
+ "mfa.recovery_used": "Du loggade in med en återställningskod. Återstående återställningskoder: {n}.",
842
+ // (sprint 131) passkeys, e-mail codes, step-up, API tokens
843
+ "mfa.on_any": "När du loggar in anger du också en kod eller använder en nyckel (passkey).",
844
+ "mfa.use_passkey": "Använd en passkey",
845
+ "mfa.passkey_failed": "Passkey fungerade inte. Försök igen eller ange en kod.",
846
+ "mfa.email_send": "Skicka en kod via e-post i stället",
847
+ "mfa.email_sent": "Vi har skickat en kod till din e-postadress. Den gäller i {minutes} minuter.",
848
+ "mfa.email_failed": "Ingen kod kunde skickas just nu. Använd din autentiseringsapp, en passkey eller en återställningskod.",
849
+ "mfa.email_subject": "Din inloggningskod för {app}",
850
+ "mfa.email_body": "Din inloggningskod för {app} är {code}. Den gäller i {minutes} minuter.\n\nOm du inte försökte logga in, byt lösenord.",
851
+ "mfa.stepup_title": "Bekräfta att det är du",
852
+ "mfa.stepup_intro": "Den här sidan ber om ditt andra steg igen. Ange en kod från din autentiseringsapp eller en återställningskod, eller använd en passkey.",
853
+ "mfa.stepup_none": "Den här sidan kräver inloggning i två steg. Lägg först till en autentiseringsapp eller en passkey under Mitt konto.",
854
+ "mfa.my_account": "Mitt konto",
855
+ "mfa.passkey_default": "Passkey",
856
+ "mfa.add_passkey": "Lägg till en passkey",
857
+ "mfa.passkey_intro": "Med en passkey loggar du in med fingeravtryck, ansikte, skärmlås eller en säkerhetsnyckel, utan att skriva en kod.",
858
+ "mfa.passkey_added": "Passkey har lagts till.",
859
+ "mfa.passkey_removed": "Passkey har tagits bort.",
860
+ "mfa.passkey_known": "Den här passkey har redan lagts till.",
861
+ "mfa.passkey_needs_host": "Passkeys kräver att programmets adress är ett värdnamn, inte en IP-adress.",
862
+ "mfa.passkey_needs_js": "Att lägga till en passkey kräver JavaScript.",
863
+ "mfa.or_passkey": "Eller lägg till en passkey i stället:",
864
+ "mfa.kind_passkey": "Passkey",
865
+ "mfa.kind_app": "Autentiseringsapp",
866
+ "token.title": "REST API-token",
867
+ "token.intro": "Med en token kan ett skript använda programmets REST API som du, tills den går ut. Den visas bara en gång.",
868
+ "token.needs_step": "Du anger först ditt andra steg igen.",
869
+ "token.hours": "Giltig i (timmar)",
870
+ "token.create": "Skapa token",
871
+ "token.shown_once": "Din token (visas en gång, giltig i {hours} h):",
872
+ "token.step_first": "Bekräfta att det är du innan du skapar en token.",
808
873
  };
package/src/i18n/tr.ts CHANGED
@@ -805,4 +805,69 @@ export const tr: Record<MessageKey, string> = {
805
805
  'ai_filter.per_group': "grup başına {what}",
806
806
  'ai_filter.needs_number': "{fn} sayı içeren bir sütun gerektirir.",
807
807
  'ai_filter.invalid': "Bu değişiklik yapılamaz: {why}",
808
+ // (sprint 130) two-step sign-in
809
+ "mfa.step_title": "İki adımlı oturum açma",
810
+ "mfa.step_intro": "Kimlik doğrulama uygulamanızdaki 6 haneli kodu veya kurtarma kodlarınızdan birini girin.",
811
+ "mfa.code": "Kod",
812
+ "mfa.verify": "Doğrula",
813
+ "mfa.wrong": "Kod yanlış veya daha önce kullanılmış.",
814
+ "mfa.expired": "Oturum açma çok uzun sürdü. Lütfen yeniden oturum açın.",
815
+ "mfa.enrol_title": "İki adımlı oturum açmayı ayarlayın",
816
+ "mfa.enrol_intro": "Bu uygulama oturum açarken bir kimlik doğrulama uygulamasından kod ister. Devam etmek için şimdi bir tane ekleyin.",
817
+ "mfa.scan": "QR kodunu Google Authenticator, Microsoft Authenticator veya 1Password gibi bir kimlik doğrulama uygulamasıyla tarayın.",
818
+ "mfa.or_enter": "Veya bu anahtarı girin:",
819
+ "mfa.enter_code": "Uygulamanın gösterdiği 6 haneli kodu girin.",
820
+ "mfa.qr_label": "Kimlik doğrulama uygulaması için QR kodu",
821
+ "mfa.codes_title": "Kurtarma kodları",
822
+ "mfa.codes_once": "Bu kodları güvenli bir yerde saklayın. Telefonunuz yanınızda olmadığında her biri bir kez oturum açmanızı sağlar. Yalnızca şimdi gösterilirler.",
823
+ "mfa.codes_saved": "Bu kodları kaydettim",
824
+ "mfa.continue": "Devam",
825
+ "mfa.no_key": "İki adımlı oturum açma bu sunucuda henüz ayarlanmadı. Lütfen yöneticiye başvurun.",
826
+ "mfa.app_name": "Ad",
827
+ "mfa.app_default": "Kimlik doğrulama uygulaması",
828
+ "mfa.on": "Oturum açarken kimlik doğrulama uygulamanızdan bir kod da girersiniz.",
829
+ "mfa.off": "Oturum açarken hesabınızı ikinci bir adımla korumak için bir kimlik doğrulama uygulaması ekleyin.",
830
+ "mfa.required_you": "Bu uygulama sizin için iki adımlı oturum açmayı zorunlu kılıyor: bir kimlik doğrulama uygulaması ekleyin.",
831
+ "mfa.add_app": "Kimlik doğrulama uygulaması ekle",
832
+ "mfa.confirm_app": "Uygulama ekle",
833
+ "mfa.cancel": "İptal",
834
+ "mfa.remove": "Kaldır",
835
+ "mfa.remove_code": "Geçerli bir kod veya kurtarma kodu",
836
+ "mfa.removed": "Kimlik doğrulama uygulaması kaldırıldı.",
837
+ "mfa.keep_one": "Bu uygulama sizin için iki adımlı oturum açmayı zorunlu kılıyor: bunu kaldırmadan önce başka bir uygulama ekleyin.",
838
+ "mfa.added": "Kimlik doğrulama uygulaması eklendi.",
839
+ "mfa.codes_left": "Kalan kurtarma kodları: {n}",
840
+ "mfa.codes_new": "Yeni kurtarma kodları",
841
+ "mfa.recovery_used": "Kurtarma koduyla oturum açtınız. Kalan kurtarma kodları: {n}.",
842
+ // (sprint 131) passkeys, e-mail codes, step-up, API tokens
843
+ "mfa.on_any": "Oturum açarken ayrıca bir kod girer veya bir geçiş anahtarı kullanırsınız.",
844
+ "mfa.use_passkey": "Geçiş anahtarı kullan",
845
+ "mfa.passkey_failed": "Geçiş anahtarı çalışmadı. Tekrar deneyin veya bir kod girin.",
846
+ "mfa.email_send": "Bunun yerine bana e-postayla kod gönder",
847
+ "mfa.email_sent": "E-posta adresinize bir kod gönderdik. {minutes} dakika geçerlidir.",
848
+ "mfa.email_failed": "Şu anda kod gönderilemedi. Kimlik doğrulama uygulamanızı, bir geçiş anahtarını veya bir kurtarma kodunu kullanın.",
849
+ "mfa.email_subject": "{app} için oturum açma kodunuz",
850
+ "mfa.email_body": "{app} için oturum açma kodunuz {code}. {minutes} dakika geçerlidir.\n\nOturum açmaya çalışmadıysanız parolanızı değiştirin.",
851
+ "mfa.stepup_title": "Siz olduğunuzu doğrulayın",
852
+ "mfa.stepup_intro": "Bu sayfa ikinci adımınızı yeniden istiyor. Kimlik doğrulama uygulamanızdan bir kod veya bir kurtarma kodu girin ya da bir geçiş anahtarı kullanın.",
853
+ "mfa.stepup_none": "Bu sayfa iki adımlı oturum açma gerektirir. Önce Hesabım bölümünde bir kimlik doğrulama uygulaması veya geçiş anahtarı ekleyin.",
854
+ "mfa.my_account": "Hesabım",
855
+ "mfa.passkey_default": "Geçiş anahtarı",
856
+ "mfa.add_passkey": "Geçiş anahtarı ekle",
857
+ "mfa.passkey_intro": "Geçiş anahtarıyla kod yazmadan parmak iziniz, yüzünüz, ekran kilidiniz veya bir güvenlik anahtarıyla oturum açarsınız.",
858
+ "mfa.passkey_added": "Geçiş anahtarı eklendi.",
859
+ "mfa.passkey_removed": "Geçiş anahtarı kaldırıldı.",
860
+ "mfa.passkey_known": "Bu geçiş anahtarı zaten eklenmiş.",
861
+ "mfa.passkey_needs_host": "Geçiş anahtarları, uygulamanın adresinin IP adresi değil bir ana bilgisayar adı olmasını gerektirir.",
862
+ "mfa.passkey_needs_js": "Geçiş anahtarı eklemek için JavaScript gerekir.",
863
+ "mfa.or_passkey": "Ya da bunun yerine bir geçiş anahtarı ekleyin:",
864
+ "mfa.kind_passkey": "Geçiş anahtarı",
865
+ "mfa.kind_app": "Kimlik doğrulama uygulaması",
866
+ "token.title": "REST API belirteci",
867
+ "token.intro": "Belirteç, bir betiğin süresi dolana kadar bu uygulamanın REST API'sini sizin adınıza kullanmasını sağlar. Yalnızca bir kez gösterilir.",
868
+ "token.needs_step": "Önce ikinci adımınızı yeniden verirsiniz.",
869
+ "token.hours": "Geçerlilik (saat)",
870
+ "token.create": "Belirteç oluştur",
871
+ "token.shown_once": "Belirteciniz (bir kez gösterilir, {hours} sa geçerli):",
872
+ "token.step_first": "Belirteç oluşturmadan önce siz olduğunuzu doğrulayın.",
808
873
  };
package/src/i18n/uk.ts CHANGED
@@ -805,4 +805,69 @@ export const uk: Record<MessageKey, string> = {
805
805
  'ai_filter.per_group': "{what} за групами",
806
806
  'ai_filter.needs_number': "Для {fn} потрібен стовпець із числами.",
807
807
  'ai_filter.invalid': "Ця зміна неможлива: {why}",
808
+ // (sprint 130) two-step sign-in
809
+ "mfa.step_title": "Двоетапний вхід",
810
+ "mfa.step_intro": "Введіть 6-значний код із застосунку-автентифікатора або один із кодів відновлення.",
811
+ "mfa.code": "Код",
812
+ "mfa.verify": "Підтвердити",
813
+ "mfa.wrong": "Код неправильний або вже був використаний.",
814
+ "mfa.expired": "Вхід тривав надто довго. Увійдіть знову.",
815
+ "mfa.enrol_title": "Налаштування двоетапного входу",
816
+ "mfa.enrol_intro": "Цей застосунок під час входу запитує код із застосунку-автентифікатора. Додайте його зараз, щоб продовжити.",
817
+ "mfa.scan": "Відскануйте QR-код застосунком-автентифікатором, наприклад Google Authenticator, Microsoft Authenticator або 1Password.",
818
+ "mfa.or_enter": "Або введіть цей ключ:",
819
+ "mfa.enter_code": "Введіть 6-значний код, який показує застосунок.",
820
+ "mfa.qr_label": "QR-код для застосунку-автентифікатора",
821
+ "mfa.codes_title": "Коди відновлення",
822
+ "mfa.codes_once": "Зберігайте ці коди в надійному місці. Кожен із них дає змогу увійти один раз, коли у вас немає телефона. Вони показуються лише зараз.",
823
+ "mfa.codes_saved": "Я зберіг ці коди",
824
+ "mfa.continue": "Продовжити",
825
+ "mfa.no_key": "Двоетапний вхід на цьому сервері ще не налаштовано. Зверніться до адміністратора.",
826
+ "mfa.app_name": "Назва",
827
+ "mfa.app_default": "Застосунок-автентифікатор",
828
+ "mfa.on": "Під час входу ви також вводите код із застосунку-автентифікатора.",
829
+ "mfa.off": "Додайте застосунок-автентифікатор, щоб захистити обліковий запис другим кроком під час входу.",
830
+ "mfa.required_you": "Цей застосунок вимагає від вас двоетапного входу: додайте застосунок-автентифікатор.",
831
+ "mfa.add_app": "Додати застосунок-автентифікатор",
832
+ "mfa.confirm_app": "Додати застосунок",
833
+ "mfa.cancel": "Скасувати",
834
+ "mfa.remove": "Вилучити",
835
+ "mfa.remove_code": "Поточний код або код відновлення",
836
+ "mfa.removed": "Застосунок-автентифікатор вилучено.",
837
+ "mfa.keep_one": "Цей застосунок вимагає від вас двоетапного входу: перш ніж вилучити цей застосунок, додайте інший.",
838
+ "mfa.added": "Застосунок-автентифікатор додано.",
839
+ "mfa.codes_left": "Залишилося кодів відновлення: {n}",
840
+ "mfa.codes_new": "Нові коди відновлення",
841
+ "mfa.recovery_used": "Ви увійшли з кодом відновлення. Залишилося кодів відновлення: {n}.",
842
+ // (sprint 131) passkeys, e-mail codes, step-up, API tokens
843
+ "mfa.on_any": "Під час входу ви також вводите код або використовуєте ключ доступу.",
844
+ "mfa.use_passkey": "Використати ключ доступу",
845
+ "mfa.passkey_failed": "Ключ доступу не спрацював. Спробуйте ще раз або введіть код.",
846
+ "mfa.email_send": "Краще надішліть мені код електронною поштою",
847
+ "mfa.email_sent": "Ми надіслали код на вашу адресу електронної пошти. Він дійсний {minutes} хв.",
848
+ "mfa.email_failed": "Зараз не вдалося надіслати код. Використайте застосунок-автентифікатор, ключ доступу або резервний код.",
849
+ "mfa.email_subject": "Ваш код входу до {app}",
850
+ "mfa.email_body": "Ваш код входу до {app}: {code}. Він дійсний {minutes} хв.\n\nЯкщо ви не намагалися увійти, змініть пароль.",
851
+ "mfa.stepup_title": "Підтвердьте, що це ви",
852
+ "mfa.stepup_intro": "Ця сторінка знову запитує другий крок. Введіть код із застосунку-автентифікатора або резервний код чи використайте ключ доступу.",
853
+ "mfa.stepup_none": "Для цієї сторінки потрібен вхід у два кроки. Спершу додайте застосунок-автентифікатор або ключ доступу в розділі «Мій обліковий запис».",
854
+ "mfa.my_account": "Мій обліковий запис",
855
+ "mfa.passkey_default": "Ключ доступу",
856
+ "mfa.add_passkey": "Додати ключ доступу",
857
+ "mfa.passkey_intro": "Ключ доступу дає змогу входити за відбитком пальця, обличчям, блокуванням екрана або ключем безпеки, не вводячи код.",
858
+ "mfa.passkey_added": "Ключ доступу додано.",
859
+ "mfa.passkey_removed": "Ключ доступу видалено.",
860
+ "mfa.passkey_known": "Цей ключ доступу вже додано.",
861
+ "mfa.passkey_needs_host": "Для ключів доступу адреса застосунку має бути іменем хоста, а не IP-адресою.",
862
+ "mfa.passkey_needs_js": "Щоб додати ключ доступу, потрібен JavaScript.",
863
+ "mfa.or_passkey": "Або натомість додайте ключ доступу:",
864
+ "mfa.kind_passkey": "Ключ доступу",
865
+ "mfa.kind_app": "Застосунок-автентифікатор",
866
+ "token.title": "Токен REST API",
867
+ "token.intro": "Токен дає змогу скрипту використовувати REST API цього застосунку від вашого імені, доки не мине строк. Він показується лише один раз.",
868
+ "token.needs_step": "Спершу ви знову проходите другий крок.",
869
+ "token.hours": "Дійсний (години)",
870
+ "token.create": "Створити токен",
871
+ "token.shown_once": "Ваш токен (показується один раз, дійсний {hours} год):",
872
+ "token.step_first": "Підтвердьте, що це ви, перш ніж створювати токен.",
808
873
  };
package/src/i18n/zh.ts CHANGED
@@ -805,4 +805,69 @@ export const zh: Record<MessageKey, string> = {
805
805
  'ai_filter.per_group': "每组 {what}",
806
806
  'ai_filter.needs_number': "{fn} 需要数字列。",
807
807
  'ai_filter.invalid': "无法进行此更改:{why}",
808
+ // (sprint 130) two-step sign-in
809
+ "mfa.step_title": "两步登录",
810
+ "mfa.step_intro": "请输入身份验证器应用中的 6 位代码,或你的一个恢复代码。",
811
+ "mfa.code": "代码",
812
+ "mfa.verify": "验证",
813
+ "mfa.wrong": "代码不正确,或已被使用。",
814
+ "mfa.expired": "登录耗时过长。请重新登录。",
815
+ "mfa.enrol_title": "设置两步登录",
816
+ "mfa.enrol_intro": "此应用程序在登录时需要身份验证器应用中的代码。请立即添加一个以继续。",
817
+ "mfa.scan": "使用身份验证器应用(如 Google Authenticator、Microsoft Authenticator 或 1Password)扫描二维码。",
818
+ "mfa.or_enter": "或输入此密钥:",
819
+ "mfa.enter_code": "输入应用显示的 6 位代码。",
820
+ "mfa.qr_label": "用于身份验证器应用的二维码",
821
+ "mfa.codes_title": "恢复代码",
822
+ "mfa.codes_once": "请将这些代码保存在安全的地方。没有手机时,每个代码可让你登录一次。它们只会显示这一次。",
823
+ "mfa.codes_saved": "我已保存这些代码",
824
+ "mfa.continue": "继续",
825
+ "mfa.no_key": "此服务器尚未设置两步登录。请联系管理员。",
826
+ "mfa.app_name": "名称",
827
+ "mfa.app_default": "身份验证器应用",
828
+ "mfa.on": "登录时,你还需要输入身份验证器应用中的代码。",
829
+ "mfa.off": "添加身份验证器应用,在登录时用第二步保护你的账户。",
830
+ "mfa.required_you": "此应用程序要求你使用两步登录:请添加身份验证器应用。",
831
+ "mfa.add_app": "添加身份验证器应用",
832
+ "mfa.confirm_app": "添加应用",
833
+ "mfa.cancel": "取消",
834
+ "mfa.remove": "移除",
835
+ "mfa.remove_code": "当前代码或恢复代码",
836
+ "mfa.removed": "已移除身份验证器应用。",
837
+ "mfa.keep_one": "此应用程序要求你使用两步登录:请先添加另一个应用,再移除此应用。",
838
+ "mfa.added": "已添加身份验证器应用。",
839
+ "mfa.codes_left": "剩余恢复代码:{n}",
840
+ "mfa.codes_new": "新的恢复代码",
841
+ "mfa.recovery_used": "你使用恢复代码登录了。剩余恢复代码:{n}。",
842
+ // (sprint 131) passkeys, e-mail codes, step-up, API tokens
843
+ "mfa.on_any": "登录时,您还需要输入代码或使用通行密钥。",
844
+ "mfa.use_passkey": "使用通行密钥",
845
+ "mfa.passkey_failed": "通行密钥无效。请重试或输入代码。",
846
+ "mfa.email_send": "改为通过电子邮件发送代码",
847
+ "mfa.email_sent": "我们已向您的电子邮件地址发送了代码,有效期为 {minutes} 分钟。",
848
+ "mfa.email_failed": "目前无法发送代码。请使用身份验证器应用、通行密钥或恢复代码。",
849
+ "mfa.email_subject": "您的 {app} 登录代码",
850
+ "mfa.email_body": "您的 {app} 登录代码是 {code},有效期为 {minutes} 分钟。\n\n如果您没有尝试登录,请更改密码。",
851
+ "mfa.stepup_title": "确认是您本人",
852
+ "mfa.stepup_intro": "此页面再次要求您完成第二步。请输入身份验证器应用中的代码或恢复代码,或使用通行密钥。",
853
+ "mfa.stepup_none": "此页面需要两步登录。请先在“我的帐户”中添加身份验证器应用或通行密钥。",
854
+ "mfa.my_account": "我的帐户",
855
+ "mfa.passkey_default": "通行密钥",
856
+ "mfa.add_passkey": "添加通行密钥",
857
+ "mfa.passkey_intro": "使用通行密钥,您可以通过指纹、面容、屏幕锁或安全密钥登录,无需输入代码。",
858
+ "mfa.passkey_added": "已添加通行密钥。",
859
+ "mfa.passkey_removed": "已删除通行密钥。",
860
+ "mfa.passkey_known": "此通行密钥已添加过。",
861
+ "mfa.passkey_needs_host": "通行密钥要求应用程序的地址是主机名,而不是 IP 地址。",
862
+ "mfa.passkey_needs_js": "添加通行密钥需要 JavaScript。",
863
+ "mfa.or_passkey": "或者改为添加通行密钥:",
864
+ "mfa.kind_passkey": "通行密钥",
865
+ "mfa.kind_app": "身份验证器应用",
866
+ "token.title": "REST API 令牌",
867
+ "token.intro": "令牌允许脚本以您的身份使用此应用程序的 REST API,直到过期。它只显示一次。",
868
+ "token.needs_step": "您需要先再次完成第二步。",
869
+ "token.hours": "有效期(小时)",
870
+ "token.create": "创建令牌",
871
+ "token.shown_once": "您的令牌(仅显示一次,有效期 {hours} 小时):",
872
+ "token.step_first": "创建令牌前,请确认是您本人。",
808
873
  };
package/src/i18n.ts CHANGED
@@ -832,6 +832,71 @@ const en = {
832
832
  'ai_filter.per_group': "{what} per group",
833
833
  'ai_filter.needs_number': "{fn} needs a column with numbers.",
834
834
  'ai_filter.invalid': "That change is not possible: {why}",
835
+ // (sprint 130) two-factor sign-in
836
+ "mfa.step_title": "Two-factor sign-in",
837
+ "mfa.step_intro": "Enter the 6-digit code from your authenticator app, or one of your recovery codes.",
838
+ "mfa.code": "Code",
839
+ "mfa.verify": "Verify",
840
+ "mfa.wrong": "That code is not right, or it was used already.",
841
+ "mfa.expired": "Signing in took too long. Please sign in again.",
842
+ "mfa.enrol_title": "Set up two-factor sign-in",
843
+ "mfa.enrol_intro": "This application asks for a code from an authenticator app when you sign in. Add one now to continue.",
844
+ "mfa.scan": "Scan the QR code with an authenticator app, such as Google Authenticator, Microsoft Authenticator or 1Password.",
845
+ "mfa.or_enter": "Or enter this key:",
846
+ "mfa.enter_code": "Enter the 6-digit code the app shows.",
847
+ "mfa.qr_label": "QR code for an authenticator app",
848
+ "mfa.codes_title": "Recovery codes",
849
+ "mfa.codes_once": "Keep these codes in a safe place. Each one signs you in once when you don't have your phone. They are shown only now.",
850
+ "mfa.codes_saved": "I have saved these codes",
851
+ "mfa.continue": "Continue",
852
+ "mfa.no_key": "Two-factor sign-in is not set up on this server yet. Please ask the administrator.",
853
+ "mfa.app_name": "Name",
854
+ "mfa.app_default": "Authenticator app",
855
+ "mfa.on": "When you sign in, you also give a code from your authenticator app.",
856
+ "mfa.off": "Add an authenticator app to protect your account with a second step when you sign in.",
857
+ "mfa.required_you": "This application requires two-factor sign-in for you: add an authenticator app.",
858
+ "mfa.add_app": "Add an authenticator app",
859
+ "mfa.confirm_app": "Add app",
860
+ "mfa.cancel": "Cancel",
861
+ "mfa.remove": "Remove",
862
+ "mfa.remove_code": "A current code or a recovery code",
863
+ "mfa.removed": "The authenticator app was removed.",
864
+ "mfa.keep_one": "This application requires two-factor sign-in for you: add another app before you remove this one.",
865
+ "mfa.added": "The authenticator app was added.",
866
+ "mfa.codes_left": "Recovery codes left: {n}",
867
+ "mfa.codes_new": "New recovery codes",
868
+ "mfa.recovery_used": "You signed in with a recovery code. Recovery codes left: {n}.",
869
+ // (sprint 131) passkeys, e-mail codes, step-up, API tokens
870
+ "mfa.on_any": "When you sign in, you also give a code or use a passkey.",
871
+ "mfa.use_passkey": "Use a passkey",
872
+ "mfa.passkey_failed": "The passkey did not work. Try again, or enter a code.",
873
+ "mfa.email_send": "E-mail me a code instead",
874
+ "mfa.email_sent": "We sent a code to your e-mail address. It is valid for {minutes} minutes.",
875
+ "mfa.email_failed": "No code could be sent right now. Use your authenticator app, a passkey or a recovery code.",
876
+ "mfa.email_subject": "Your sign-in code for {app}",
877
+ "mfa.email_body": "Your sign-in code for {app} is {code}. It is valid for {minutes} minutes.\n\nIf you did not try to sign in, change your password.",
878
+ "mfa.stepup_title": "Confirm it's you",
879
+ "mfa.stepup_intro": "This page asks for your second step again. Enter a code from your authenticator app or a recovery code, or use a passkey.",
880
+ "mfa.stepup_none": "This page needs two-factor sign-in. Add an authenticator app or a passkey on My account first.",
881
+ "mfa.my_account": "My account",
882
+ "mfa.passkey_default": "Passkey",
883
+ "mfa.add_passkey": "Add a passkey",
884
+ "mfa.passkey_intro": "A passkey signs you in with your fingerprint, face, screen lock or a security key, without typing a code.",
885
+ "mfa.passkey_added": "The passkey was added.",
886
+ "mfa.passkey_removed": "The passkey was removed.",
887
+ "mfa.passkey_known": "This passkey was added already.",
888
+ "mfa.passkey_needs_host": "Passkeys need the application's address to be a host name, not an IP address.",
889
+ "mfa.passkey_needs_js": "Adding a passkey needs JavaScript.",
890
+ "mfa.or_passkey": "Or add a passkey instead:",
891
+ "mfa.kind_passkey": "Passkey",
892
+ "mfa.kind_app": "Authenticator app",
893
+ "token.title": "REST API token",
894
+ "token.intro": "A token lets a script use this application's REST API as you, until it expires. It is shown only once.",
895
+ "token.needs_step": "You give your second step again first.",
896
+ "token.hours": "Valid for (hours)",
897
+ "token.create": "Create token",
898
+ "token.shown_once": "Your token (shown once, valid {hours} h):",
899
+ "token.step_first": "Confirm it's you before you create a token.",
835
900
  };
836
901
 
837
902
  export type MessageKey = keyof typeof en;
@@ -1633,6 +1698,71 @@ const nl: Record<MessageKey, string> = {
1633
1698
  'ai_filter.per_group': "{what} per groep",
1634
1699
  'ai_filter.needs_number': "{fn} vraagt een kolom met getallen.",
1635
1700
  'ai_filter.invalid': "Die wijziging kan niet: {why}",
1701
+ // (sprint 130) two-factor sign-in
1702
+ "mfa.step_title": "Aanmelden in twee stappen",
1703
+ "mfa.step_intro": "Voer de 6-cijferige code uit je authenticator-app in, of een van je herstelcodes.",
1704
+ "mfa.code": "Code",
1705
+ "mfa.verify": "Controleren",
1706
+ "mfa.wrong": "Die code klopt niet, of is al gebruikt.",
1707
+ "mfa.expired": "Het aanmelden duurde te lang. Meld je opnieuw aan.",
1708
+ "mfa.enrol_title": "Aanmelden in twee stappen instellen",
1709
+ "mfa.enrol_intro": "Deze applicatie vraagt bij het aanmelden om een code uit een authenticator-app. Voeg er nu een toe om verder te gaan.",
1710
+ "mfa.scan": "Scan de QR-code met een authenticator-app, zoals Google Authenticator, Microsoft Authenticator of 1Password.",
1711
+ "mfa.or_enter": "Of voer deze sleutel in:",
1712
+ "mfa.enter_code": "Voer de 6-cijferige code in die de app toont.",
1713
+ "mfa.qr_label": "QR-code voor een authenticator-app",
1714
+ "mfa.codes_title": "Herstelcodes",
1715
+ "mfa.codes_once": "Bewaar deze codes op een veilige plek. Met elke code meld je je één keer aan als je je telefoon niet bij je hebt. Ze worden alleen nu getoond.",
1716
+ "mfa.codes_saved": "Ik heb deze codes bewaard",
1717
+ "mfa.continue": "Doorgaan",
1718
+ "mfa.no_key": "Aanmelden in twee stappen is op deze server nog niet ingesteld. Vraag het de beheerder.",
1719
+ "mfa.app_name": "Naam",
1720
+ "mfa.app_default": "Authenticator-app",
1721
+ "mfa.on": "Bij het aanmelden geef je ook een code uit je authenticator-app.",
1722
+ "mfa.off": "Voeg een authenticator-app toe om je account bij het aanmelden met een tweede stap te beschermen.",
1723
+ "mfa.required_you": "Deze applicatie vereist voor jou aanmelden in twee stappen: voeg een authenticator-app toe.",
1724
+ "mfa.add_app": "Authenticator-app toevoegen",
1725
+ "mfa.confirm_app": "App toevoegen",
1726
+ "mfa.cancel": "Annuleren",
1727
+ "mfa.remove": "Verwijderen",
1728
+ "mfa.remove_code": "Een actuele code of een herstelcode",
1729
+ "mfa.removed": "De authenticator-app is verwijderd.",
1730
+ "mfa.keep_one": "Deze applicatie vereist voor jou aanmelden in twee stappen: voeg eerst een andere app toe voordat je deze verwijdert.",
1731
+ "mfa.added": "De authenticator-app is toegevoegd.",
1732
+ "mfa.codes_left": "Resterende herstelcodes: {n}",
1733
+ "mfa.codes_new": "Nieuwe herstelcodes",
1734
+ "mfa.recovery_used": "Je hebt je aangemeld met een herstelcode. Resterende herstelcodes: {n}.",
1735
+ // (sprint 131) passkeys, e-mail codes, step-up, API tokens
1736
+ "mfa.on_any": "Bij het aanmelden geef je ook een code of gebruik je een passkey.",
1737
+ "mfa.use_passkey": "Passkey gebruiken",
1738
+ "mfa.passkey_failed": "De passkey werkte niet. Probeer het opnieuw of voer een code in.",
1739
+ "mfa.email_send": "Stuur me liever een code per e-mail",
1740
+ "mfa.email_sent": "We hebben een code naar je e-mailadres gestuurd. Die is {minutes} minuten geldig.",
1741
+ "mfa.email_failed": "Er kon nu geen code worden verstuurd. Gebruik je authenticator-app, een passkey of een herstelcode.",
1742
+ "mfa.email_subject": "Je aanmeldcode voor {app}",
1743
+ "mfa.email_body": "Je aanmeldcode voor {app} is {code}. Die is {minutes} minuten geldig.\n\nProbeerde je niet aan te melden? Wijzig dan je wachtwoord.",
1744
+ "mfa.stepup_title": "Bevestig dat jij het bent",
1745
+ "mfa.stepup_intro": "Deze pagina vraagt opnieuw om je tweede stap. Voer een code uit je authenticator-app of een herstelcode in, of gebruik een passkey.",
1746
+ "mfa.stepup_none": "Voor deze pagina is aanmelden in twee stappen nodig. Voeg eerst een authenticator-app of een passkey toe bij Mijn account.",
1747
+ "mfa.my_account": "Mijn account",
1748
+ "mfa.passkey_default": "Passkey",
1749
+ "mfa.add_passkey": "Passkey toevoegen",
1750
+ "mfa.passkey_intro": "Met een passkey meld je je aan met je vingerafdruk, gezicht, schermvergrendeling of een beveiligingssleutel, zonder een code te typen.",
1751
+ "mfa.passkey_added": "De passkey is toegevoegd.",
1752
+ "mfa.passkey_removed": "De passkey is verwijderd.",
1753
+ "mfa.passkey_known": "Deze passkey was al toegevoegd.",
1754
+ "mfa.passkey_needs_host": "Passkeys werken alleen als het adres van de applicatie een hostnaam is, geen IP-adres.",
1755
+ "mfa.passkey_needs_js": "Voor het toevoegen van een passkey is JavaScript nodig.",
1756
+ "mfa.or_passkey": "Of voeg in plaats daarvan een passkey toe:",
1757
+ "mfa.kind_passkey": "Passkey",
1758
+ "mfa.kind_app": "Authenticator-app",
1759
+ "token.title": "REST API-token",
1760
+ "token.intro": "Met een token gebruikt een script de REST API van deze applicatie als jou, tot het verloopt. Het wordt maar één keer getoond.",
1761
+ "token.needs_step": "Je geeft eerst opnieuw je tweede stap.",
1762
+ "token.hours": "Geldig (uren)",
1763
+ "token.create": "Token maken",
1764
+ "token.shown_once": "Je token (één keer getoond, {hours} uur geldig):",
1765
+ "token.step_first": "Bevestig dat jij het bent voordat je een token maakt.",
1636
1766
  };
1637
1767
 
1638
1768
  const BUILTIN: Record<string, Record<string, string>> = { en, nl, de, fr, es, it, pt, pl, sv, da, nb, cs, ja, zh, fi, tr, el, ru, uk, ko, ar, he };
package/src/metadata.ts CHANGED
@@ -6,6 +6,8 @@ export interface AuthzScheme {
6
6
  type: 'role' | 'sql';
7
7
  value: string;
8
8
  error_message: string;
9
+ /** (133) step-up: passes only when the session's last second step is at most this many minutes old */
10
+ mfa_minutes?: number | null;
9
11
  }
10
12
 
11
13
  export interface NavEntry {
@@ -78,6 +80,13 @@ export interface App {
78
80
  password_reset: boolean;
79
81
  /** (067) session sharing group (src/sharedlogin.ts) */
80
82
  session_group: string | null;
83
+ /** (132) two-step sign-in for the application's users (src/account-mfa.ts) */
84
+ mfa: 'off' | 'optional' | 'required' | 'roles';
85
+ mfa_roles: string[];
86
+ /** (133) a code by e-mail at the second step, for users with another factor */
87
+ mfa_email: boolean;
88
+ /** (133) users create their own REST API token on My account */
89
+ api_self_tokens: boolean;
81
90
  /** LDAP directories the password form checks after local accounts */
82
91
  ldap_directories: string[];
83
92
  /** Progressive Web App: installable, offline pages, offline form queue */
@@ -369,7 +378,7 @@ const agg = (table: string, fk: string, parent: string, appId: string) =>
369
378
  // No caching on purpose: edits made in the builder show up on the next request.
370
379
  export async function loadApp(alias: string) {
371
380
  return runtime.one<App>(
372
- `select a.id, a.alias, a.name, a.version, a.home_page, a.authentication, a.access_control, a.sso_providers, a.local_login, a.remember_me_days, a.login_page, a.password_reset, a.session_group, a.ldap_directories, a.header_name, a.header_auto_create, a.logout_url, a.db_auth_roles, a.db_auth_member_of, a.custom_auth_function, a.custom_auth_code, a.custom_auth_post_code, a.nav_list, a.navbar_list, a.pwa, a.pwa_short_name, a.pwa_icon is not null as pwa_has_icon, a.pwa_offline_pages, a.pwa_offline_submit, a.pwa_push, a.db_role, a.debug, a.debug_level, a.theme,
381
+ `select a.id, a.alias, a.name, a.version, a.home_page, a.authentication, a.access_control, a.sso_providers, a.local_login, a.remember_me_days, a.login_page, a.password_reset, a.session_group, a.mfa, a.mfa_roles, a.mfa_email, a.api_self_tokens, a.ldap_directories, a.header_name, a.header_auto_create, a.logout_url, a.db_auth_roles, a.db_auth_member_of, a.custom_auth_function, a.custom_auth_code, a.custom_auth_post_code, a.nav_list, a.navbar_list, a.pwa, a.pwa_short_name, a.pwa_icon is not null as pwa_has_icon, a.pwa_offline_pages, a.pwa_offline_submit, a.pwa_push, a.db_role, a.debug, a.debug_level, a.theme,
373
382
  a.availability, a.availability_message, a.availability_users, a.feedback, a.error_function, a.session_warning_seconds, a.component_settings,
374
383
  a.language, a.languages, a.language_from, a.date_format, a.timestamp_format, a.time_zone, a.time_zone_auto, a.currency,
375
384
  coalesce((select jsonb_agg(jsonb_build_object('name', l.name, 'query', l.query, 'rest_source', l.rest_source)) from meta.lov l where l.app_id = a.id), '[]') as lovs,
@@ -378,7 +387,7 @@ export async function loadApp(alias: string) {
378
387
  'dialog_position', p.dialog_position, 'dialog_size', p.dialog_size))
379
388
  from meta.page p where p.app_id = a.id and meta.build_option_on(a.id, p.build_option)), '[]') as pages,
380
389
  ${agg('meta.nav_entry', 'app_id', 'a', 'a.id')} as nav,
381
- coalesce((select jsonb_agg(jsonb_build_object('name', s.name, 'type', s.type, 'value', s.value, 'error_message', s.error_message))
390
+ coalesce((select jsonb_agg(jsonb_build_object('name', s.name, 'type', s.type, 'value', s.value, 'error_message', s.error_message, 'mfa_minutes', s.mfa_minutes))
382
391
  from meta.authz_scheme s where s.app_id = a.id), '[]') as authz_schemes,
383
392
  coalesce((select jsonb_agg(i.name) from meta.app_item i where i.app_id = a.id), '[]') as app_items,
384
393
  ${agg('meta.app_process', 'app_id', 'a', 'a.id')} as app_processes,