├── l10n ├── .gitkeep ├── km.json ├── ms_MY.json ├── az.json ├── bn_BD.json ├── kn.json ├── ta.json ├── lb.json ├── km.js ├── id.json ├── ms_MY.js ├── az.js ├── eo.json ├── ta.js ├── uz.json ├── bn_BD.js ├── kn.js ├── lb.js ├── mn.json ├── nn_NO.json ├── kab.json ├── id.js ├── si.json ├── uz.js ├── eo.js ├── kab.js ├── mn.js ├── nn_NO.js ├── si.js ├── sr@latin.json ├── cy_GB.json ├── th.json ├── pt_PT.json ├── sr@latin.js ├── th.js ├── cy_GB.js ├── pt_PT.js ├── ka_GE.json ├── ka.json ├── lo.json ├── vi.json ├── af.json ├── ka_GE.js ├── es_CL.json ├── es_CO.json ├── es_CR.json ├── es_DO.json ├── es_GT.json ├── es_HN.json ├── es_NI.json ├── es_PA.json ├── es_PE.json ├── es_PR.json ├── es_PY.json ├── es_SV.json ├── es_UY.json ├── ka.js ├── vi.js ├── lo.js ├── sq.json ├── af.js ├── mk.json ├── es_CL.js ├── es_CO.js ├── es_CR.js ├── es_DO.js ├── es_GT.js ├── es_HN.js ├── es_NI.js ├── es_PA.js ├── es_PE.js ├── es_PR.js ├── es_PY.js ├── es_SV.js ├── es_UY.js ├── sq.js ├── mk.js ├── es_AR.json ├── oc.json ├── da.json ├── es_MX.json ├── es_AR.js ├── ko.json ├── oc.js ├── da.js ├── sc.json ├── es_MX.js ├── ko.js ├── he.json ├── sc.js ├── es_419.json ├── he.js ├── sl.json ├── hr.json ├── es_419.js ├── sl.js ├── hr.js ├── br.json ├── be.json ├── br.js ├── be.js ├── lv.json ├── lv.js ├── lt_LT.json ├── lt_LT.js ├── ja.json ├── ja.js ├── nl.json ├── ast.json ├── nl.js ├── ast.js ├── ro.json ├── ro.js ├── fi.json ├── fi.js ├── zh_CN.json ├── zh_TW.json ├── zh_HK.json ├── zh_CN.js ├── zh_TW.js ├── zh_HK.js ├── ru.json ├── ru.js ├── en_GB.json ├── fa.json ├── en_GB.js ├── fa.js ├── nb.json ├── et_EE.json ├── sv.json ├── nb.js ├── sv.js ├── et_EE.js └── hu.json ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── 1_bug_report.md.license │ ├── 3_question.md.license │ ├── 2_feature_request.md.license │ ├── 3_question.md │ ├── config.yml │ ├── 2_feature_request.md │ └── 1_bug_report.md └── workflows │ ├── reuse.yml │ ├── block-unconventional-commits.yml │ ├── lint-info-xml.yml │ ├── lint-eslint-when-unrelated.yml │ ├── lint-php-cs.yml │ ├── lint-php.yml │ └── psalm-matrix.yml ├── .babelrc ├── screenshots ├── settings.png ├── challenge.png ├── challenge.png.license └── settings.png.license ├── vendor-bin ├── psalm │ └── composer.json └── cs-fixer │ └── composer.json ├── src ├── constants.js ├── mixins │ └── Nextcloud.js ├── logger.js ├── main-login-setup.js ├── tests │ ├── e2e │ │ ├── login.js │ │ ├── virtualAuthenticator.js │ │ ├── login-setup.spec.js │ │ └── occ.js │ └── unit │ │ ├── setup.js │ │ └── components │ │ ├── Device.spec.js │ │ └── PersonalSettings.spec.js ├── main-challenge.js ├── main-settings.js ├── services │ └── RegistrationService.js ├── store.js └── components │ ├── LoginSetup.vue │ └── PersonalSettings.vue ├── css ├── auth.css └── style.css ├── krankerl.toml ├── babel.config.js ├── .eslintrc.js ├── Makefile ├── templates ├── personal.php ├── challenge.php └── login-setup.php ├── .tx └── config ├── .git-blame-ignore-revs ├── .gitignore ├── tests ├── bootstrap.php ├── phpunit.xml └── Unit │ ├── Event │ └── StateChangedTest.php │ └── Activity │ ├── SettingTest.php │ └── ProviderTest.php ├── webpack.test.config.js ├── lib ├── Settings │ └── Personal.php ├── Provider │ └── WebAuthnLoginProvider.php ├── Event │ └── StateChanged.php ├── Migration │ ├── Version000203Date20200322201800.php │ ├── Version000400Date20220524125249.php │ ├── Version000203Date20200322200700.php │ ├── Version000200Date20200310200500.php │ ├── Version000103Date20200308114300.php │ ├── Version010000Date20220929073925.php │ ├── Version010000Date20221227080000.php │ └── Version000102Date20191004200147.php ├── Activity │ ├── Setting.php │ └── Provider.php ├── Listener │ ├── StateChangeActivity.php │ ├── UserDeleted.php │ └── StateChangeRegistryUpdater.php ├── AppInfo │ └── Application.php ├── Db │ ├── Registration.php │ └── RegistrationMapper.php ├── Model │ └── Device.php ├── Controller │ └── SettingsController.php ├── Service │ └── U2FMigrator.php └── Command │ └── CleanUp.php ├── .php-cs-fixer.dist.php ├── .jshintrc ├── webpack.config.js ├── .scrutinizer.yml ├── .nextcloudignore ├── appinfo ├── routes.php └── info.xml ├── playwright.config.js ├── img ├── app-dark.svg ├── app.svg └── device-disabled.svg ├── LICENSES └── MIT.txt ├── psalm.xml ├── README.md ├── composer.json ├── package.json ├── AUTHORS.md └── REUSE.toml /l10n/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # App maintainers 2 | * @ChristophWurst @kesselb 3 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "test": { 4 | "plugins": ["istanbul"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /screenshots/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/twofactor_webauthn/HEAD/screenshots/settings.png -------------------------------------------------------------------------------- /screenshots/challenge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/twofactor_webauthn/HEAD/screenshots/challenge.png -------------------------------------------------------------------------------- /l10n/km.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "បញ្ចូល", 3 | "Remove" : "ដកចេញ" 4 | },"pluralForm" :"nplurals=1; plural=0;" 5 | } -------------------------------------------------------------------------------- /l10n/ms_MY.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Tambah", 3 | "Remove" : "Buang" 4 | },"pluralForm" :"nplurals=1; plural=0;" 5 | } -------------------------------------------------------------------------------- /l10n/az.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Əlavə etmək", 3 | "Remove" : "Sil" 4 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 5 | } -------------------------------------------------------------------------------- /l10n/bn_BD.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "যোগ করুন", 3 | "Remove" : "অপসারণ" 4 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 5 | } -------------------------------------------------------------------------------- /l10n/kn.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "ಸೇರಿಸಿ", 3 | "Remove" : "ತೆಗೆದುಹಾಕಿ" 4 | },"pluralForm" :"nplurals=2; plural=(n > 1);" 5 | } -------------------------------------------------------------------------------- /l10n/ta.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "சேர்க்க", 3 | "Remove" : "அகற்றுக" 4 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 5 | } -------------------------------------------------------------------------------- /l10n/lb.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Derbäimaachen", 3 | "Remove" : "Läschen" 4 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 5 | } -------------------------------------------------------------------------------- /screenshots/challenge.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later 3 | -------------------------------------------------------------------------------- /screenshots/settings.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later 3 | -------------------------------------------------------------------------------- /l10n/km.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "បញ្ចូល", 5 | "Remove" : "ដកចេញ" 6 | }, 7 | "nplurals=1; plural=0;"); 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/1_bug_report.md.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/3_question.md.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later 3 | -------------------------------------------------------------------------------- /l10n/id.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Masukkan", 3 | "Retry" : "Ulangi", 4 | "Remove" : "Buang" 5 | },"pluralForm" :"nplurals=1; plural=0;" 6 | } -------------------------------------------------------------------------------- /l10n/ms_MY.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Tambah", 5 | "Remove" : "Buang" 6 | }, 7 | "nplurals=1; plural=0;"); 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2_feature_request.md.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later 3 | -------------------------------------------------------------------------------- /l10n/az.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Əlavə etmək", 5 | "Remove" : "Sil" 6 | }, 7 | "nplurals=2; plural=(n != 1);"); 8 | -------------------------------------------------------------------------------- /l10n/eo.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Aldoni", 3 | "Retry" : "Reprovi", 4 | "Remove" : "Forigi" 5 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 6 | } -------------------------------------------------------------------------------- /l10n/ta.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "சேர்க்க", 5 | "Remove" : "அகற்றுக" 6 | }, 7 | "nplurals=2; plural=(n != 1);"); 8 | -------------------------------------------------------------------------------- /l10n/uz.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Add", 3 | "Retry" : "Qayta urinish", 4 | "Remove" : "O'chirish" 5 | },"pluralForm" :"nplurals=1; plural=0;" 6 | } -------------------------------------------------------------------------------- /l10n/bn_BD.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "যোগ করুন", 5 | "Remove" : "অপসারণ" 6 | }, 7 | "nplurals=2; plural=(n != 1);"); 8 | -------------------------------------------------------------------------------- /l10n/kn.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "ಸೇರಿಸಿ", 5 | "Remove" : "ತೆಗೆದುಹಾಕಿ" 6 | }, 7 | "nplurals=2; plural=(n > 1);"); 8 | -------------------------------------------------------------------------------- /l10n/lb.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Derbäimaachen", 5 | "Remove" : "Läschen" 6 | }, 7 | "nplurals=2; plural=(n != 1);"); 8 | -------------------------------------------------------------------------------- /l10n/mn.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "нэмэх", 3 | "Retry" : "Дахин оролдох", 4 | "Remove" : "Устгах" 5 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 6 | } -------------------------------------------------------------------------------- /l10n/nn_NO.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Legg til", 3 | "Retry" : "Forsøk att", 4 | "Remove" : "Fjern" 5 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 6 | } -------------------------------------------------------------------------------- /l10n/kab.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Rnu", 3 | "Retry" : "Ɛreḍ tikkelt-nniḍen", 4 | "Remove" : "Kkes" 5 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 6 | } -------------------------------------------------------------------------------- /l10n/id.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Masukkan", 5 | "Retry" : "Ulangi", 6 | "Remove" : "Buang" 7 | }, 8 | "nplurals=1; plural=0;"); 9 | -------------------------------------------------------------------------------- /l10n/si.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "එකතු කරන්න", 3 | "Retry" : "නැවත උත්සාහ කරන්න", 4 | "Remove" : "ඉවත් කරන්න" 5 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 6 | } -------------------------------------------------------------------------------- /l10n/uz.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Add", 5 | "Retry" : "Qayta urinish", 6 | "Remove" : "O'chirish" 7 | }, 8 | "nplurals=1; plural=0;"); 9 | -------------------------------------------------------------------------------- /l10n/eo.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Aldoni", 5 | "Retry" : "Reprovi", 6 | "Remove" : "Forigi" 7 | }, 8 | "nplurals=2; plural=(n != 1);"); 9 | -------------------------------------------------------------------------------- /l10n/kab.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Rnu", 5 | "Retry" : "Ɛreḍ tikkelt-nniḍen", 6 | "Remove" : "Kkes" 7 | }, 8 | "nplurals=2; plural=(n != 1);"); 9 | -------------------------------------------------------------------------------- /l10n/mn.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "нэмэх", 5 | "Retry" : "Дахин оролдох", 6 | "Remove" : "Устгах" 7 | }, 8 | "nplurals=2; plural=(n != 1);"); 9 | -------------------------------------------------------------------------------- /l10n/nn_NO.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Legg til", 5 | "Retry" : "Forsøk att", 6 | "Remove" : "Fjern" 7 | }, 8 | "nplurals=2; plural=(n != 1);"); 9 | -------------------------------------------------------------------------------- /l10n/si.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "එකතු කරන්න", 5 | "Retry" : "නැවත උත්සාහ කරන්න", 6 | "Remove" : "ඉවත් කරන්න" 7 | }, 8 | "nplurals=2; plural=(n != 1);"); 9 | -------------------------------------------------------------------------------- /l10n/sr@latin.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Dodaj", 3 | "Remove" : "Ukloni" 4 | },"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" 5 | } -------------------------------------------------------------------------------- /vendor-bin/psalm/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "platform": { 4 | "php": "8.1.32" 5 | }, 6 | "sort-packages": true 7 | }, 8 | "require-dev": { 9 | "vimeo/psalm": "^6.13.1" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /l10n/cy_GB.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Ychwanegu", 3 | "Retry" : "Ceisio eto", 4 | "Remove" : "Gwaredu" 5 | },"pluralForm" :"nplurals=4; plural=(n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3;" 6 | } -------------------------------------------------------------------------------- /l10n/th.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "เพิ่ม", 3 | "Your browser does not support WebAuthn." : "เบราว์เซอร์ของคุณไม่รองรับ WebAuthn", 4 | "Remove" : "ลบออก" 5 | },"pluralForm" :"nplurals=1; plural=0;" 6 | } -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | export const TWOFACTOR_WEBAUTHN = '[twofactor_webauthn]' 7 | -------------------------------------------------------------------------------- /l10n/pt_PT.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Adicionar", 3 | "Retry" : "Repetir", 4 | "Remove" : "Remover" 5 | },"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" 6 | } -------------------------------------------------------------------------------- /src/mixins/Nextcloud.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | export default { 7 | methods: { 8 | t, 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /css/auth.css: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | .two-factor-webauthn-icon { 7 | filter: var(--background-invert-if-dark); 8 | } 9 | -------------------------------------------------------------------------------- /krankerl.toml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | [package] 4 | before_cmds = [ 5 | "composer install --no-dev -o", 6 | "npm ci", 7 | "npm run build", 8 | ] 9 | -------------------------------------------------------------------------------- /l10n/sr@latin.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Dodaj", 5 | "Remove" : "Ukloni" 6 | }, 7 | "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"); 8 | -------------------------------------------------------------------------------- /l10n/th.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "เพิ่ม", 5 | "Your browser does not support WebAuthn." : "เบราว์เซอร์ของคุณไม่รองรับ WebAuthn", 6 | "Remove" : "ลบออก" 7 | }, 8 | "nplurals=1; plural=0;"); 9 | -------------------------------------------------------------------------------- /l10n/cy_GB.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Ychwanegu", 5 | "Retry" : "Ceisio eto", 6 | "Remove" : "Gwaredu" 7 | }, 8 | "nplurals=4; plural=(n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3;"); 9 | -------------------------------------------------------------------------------- /l10n/pt_PT.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Adicionar", 5 | "Retry" : "Repetir", 6 | "Remove" : "Remover" 7 | }, 8 | "nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"); 9 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | const babelConfig = require('@nextcloud/babel-config') 7 | 8 | module.exports = babelConfig 9 | -------------------------------------------------------------------------------- /l10n/ka_GE.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "დამატება", 3 | "Retry" : "ვცადოთ ახლიდან", 4 | "An error occurred. Please try again." : "გამოჩნდა შეცდომა. გთხოვთ სცადოთ ახლიდან.", 5 | "Remove" : "წაშლა" 6 | },"pluralForm" :"nplurals=2; plural=(n!=1);" 7 | } -------------------------------------------------------------------------------- /vendor-bin/cs-fixer/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "nextcloud/coding-standard": "^1.4.0" 4 | }, 5 | "config": { 6 | "optimize-autoloader": true, 7 | "platform": { 8 | "php": "8.1.0" 9 | }, 10 | "sort-packages": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /l10n/ka.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Add", 3 | "Retry" : "Retry", 4 | "Your browser does not support WebAuthn." : "Your browser does not support WebAuthn.", 5 | "Active" : "Active", 6 | "Remove" : "Remove" 7 | },"pluralForm" :"nplurals=2; plural=(n!=1);" 8 | } -------------------------------------------------------------------------------- /l10n/lo.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "ເພີ່ມ", 3 | "Retry" : "Retry", 4 | "Your browser does not support WebAuthn." : "Your browser does not support WebAuthn.", 5 | "Active" : "ກຳລັງໃຊ້ງານ", 6 | "Remove" : "ຍ້າຍອອກ" 7 | },"pluralForm" :"nplurals=1; plural=0;" 8 | } -------------------------------------------------------------------------------- /l10n/vi.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Thêm", 3 | "Retry" : "Thử lại", 4 | "Your browser does not support WebAuthn." : "Trình duyệt của bạn không hỗ trợ WebAuthn.", 5 | "Active" : "Hoạt động", 6 | "Remove" : "Xoá" 7 | },"pluralForm" :"nplurals=1; plural=0;" 8 | } -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | module.exports = { 7 | extends: [ 8 | '@nextcloud', 9 | ], 10 | rules: { 11 | 'no-console': 'off', 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /l10n/af.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Voeg by", 3 | "Retry" : "Herprobeer", 4 | "An error occurred. Please try again." : "’n Fout het voorgekom. Probeer weer.", 5 | "Active" : "Aktief", 6 | "Remove" : "Verwyder" 7 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 8 | } -------------------------------------------------------------------------------- /l10n/ka_GE.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "დამატება", 5 | "Retry" : "ვცადოთ ახლიდან", 6 | "An error occurred. Please try again." : "გამოჩნდა შეცდომა. გთხოვთ სცადოთ ახლიდან.", 7 | "Remove" : "წაშლა" 8 | }, 9 | "nplurals=2; plural=(n!=1);"); 10 | -------------------------------------------------------------------------------- /l10n/es_CL.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Agregar", 3 | "Retry" : "Reintentar", 4 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 5 | "Remove" : "Eliminar" 6 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" 7 | } -------------------------------------------------------------------------------- /l10n/es_CO.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Agregar", 3 | "Retry" : "Reintentar", 4 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 5 | "Remove" : "Eliminar" 6 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" 7 | } -------------------------------------------------------------------------------- /l10n/es_CR.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Agregar", 3 | "Retry" : "Reintentar", 4 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 5 | "Remove" : "Eliminar" 6 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" 7 | } -------------------------------------------------------------------------------- /l10n/es_DO.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Agregar", 3 | "Retry" : "Reintentar", 4 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 5 | "Remove" : "Eliminar" 6 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" 7 | } -------------------------------------------------------------------------------- /l10n/es_GT.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Agregar", 3 | "Retry" : "Reintentar", 4 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 5 | "Remove" : "Eliminar" 6 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" 7 | } -------------------------------------------------------------------------------- /l10n/es_HN.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Agregar", 3 | "Retry" : "Reintentar", 4 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 5 | "Remove" : "Eliminar" 6 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" 7 | } -------------------------------------------------------------------------------- /l10n/es_NI.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Agregar", 3 | "Retry" : "Reintentar", 4 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 5 | "Remove" : "Eliminar" 6 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" 7 | } -------------------------------------------------------------------------------- /l10n/es_PA.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Agregar", 3 | "Retry" : "Reintentar", 4 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 5 | "Remove" : "Eliminar" 6 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" 7 | } -------------------------------------------------------------------------------- /l10n/es_PE.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Agregar", 3 | "Retry" : "Reintentar", 4 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 5 | "Remove" : "Eliminar" 6 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" 7 | } -------------------------------------------------------------------------------- /l10n/es_PR.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Agregar", 3 | "Retry" : "Reintentar", 4 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 5 | "Remove" : "Eliminar" 6 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" 7 | } -------------------------------------------------------------------------------- /l10n/es_PY.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Agregar", 3 | "Retry" : "Reintentar", 4 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 5 | "Remove" : "Eliminar" 6 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" 7 | } -------------------------------------------------------------------------------- /l10n/es_SV.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Agregar", 3 | "Retry" : "Reintentar", 4 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 5 | "Remove" : "Eliminar" 6 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" 7 | } -------------------------------------------------------------------------------- /l10n/es_UY.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Agregar", 3 | "Retry" : "Reintentar", 4 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 5 | "Remove" : "Eliminar" 6 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" 7 | } -------------------------------------------------------------------------------- /l10n/ka.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Add", 5 | "Retry" : "Retry", 6 | "Your browser does not support WebAuthn." : "Your browser does not support WebAuthn.", 7 | "Active" : "Active", 8 | "Remove" : "Remove" 9 | }, 10 | "nplurals=2; plural=(n!=1);"); 11 | -------------------------------------------------------------------------------- /l10n/vi.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Thêm", 5 | "Retry" : "Thử lại", 6 | "Your browser does not support WebAuthn." : "Trình duyệt của bạn không hỗ trợ WebAuthn.", 7 | "Active" : "Hoạt động", 8 | "Remove" : "Xoá" 9 | }, 10 | "nplurals=1; plural=0;"); 11 | -------------------------------------------------------------------------------- /l10n/lo.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "ເພີ່ມ", 5 | "Retry" : "Retry", 6 | "Your browser does not support WebAuthn." : "Your browser does not support WebAuthn.", 7 | "Active" : "ກຳລັງໃຊ້ງານ", 8 | "Remove" : "ຍ້າຍອອກ" 9 | }, 10 | "nplurals=1; plural=0;"); 11 | -------------------------------------------------------------------------------- /l10n/sq.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Shto", 3 | "An error occurred: {msg}" : "Ndodhi një gabim: {msg}", 4 | "Retry" : "Riprovoni", 5 | "An error occurred. Please try again." : "Ndodhi një problem.Ju lutem provoni përsëri.", 6 | "Remove" : "Hiq" 7 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 8 | } -------------------------------------------------------------------------------- /l10n/af.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Voeg by", 5 | "Retry" : "Herprobeer", 6 | "An error occurred. Please try again." : "’n Fout het voorgekom. Probeer weer.", 7 | "Active" : "Aktief", 8 | "Remove" : "Verwyder" 9 | }, 10 | "nplurals=2; plural=(n != 1);"); 11 | -------------------------------------------------------------------------------- /l10n/mk.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Додади", 3 | "Retry" : "Обидете се повторно", 4 | "Your browser does not support WebAuthn." : "Вашиот прелистувач не поддржува WebAuthn.", 5 | "Active" : "Активно", 6 | "Remove" : "Отстрани " 7 | },"pluralForm" :"nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;" 8 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/3_question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🙋 Question 3 | about: Do you have a question about the app? 4 | version: 0.1 5 | --- 6 | 7 | ### Question 8 | 9 | 10 | 11 | #### Summary 12 | 13 | 14 | -------------------------------------------------------------------------------- /l10n/es_CL.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Agregar", 5 | "Retry" : "Reintentar", 6 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 7 | "Remove" : "Eliminar" 8 | }, 9 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"); 10 | -------------------------------------------------------------------------------- /l10n/es_CO.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Agregar", 5 | "Retry" : "Reintentar", 6 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 7 | "Remove" : "Eliminar" 8 | }, 9 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"); 10 | -------------------------------------------------------------------------------- /l10n/es_CR.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Agregar", 5 | "Retry" : "Reintentar", 6 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 7 | "Remove" : "Eliminar" 8 | }, 9 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"); 10 | -------------------------------------------------------------------------------- /l10n/es_DO.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Agregar", 5 | "Retry" : "Reintentar", 6 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 7 | "Remove" : "Eliminar" 8 | }, 9 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"); 10 | -------------------------------------------------------------------------------- /l10n/es_GT.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Agregar", 5 | "Retry" : "Reintentar", 6 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 7 | "Remove" : "Eliminar" 8 | }, 9 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"); 10 | -------------------------------------------------------------------------------- /l10n/es_HN.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Agregar", 5 | "Retry" : "Reintentar", 6 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 7 | "Remove" : "Eliminar" 8 | }, 9 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"); 10 | -------------------------------------------------------------------------------- /l10n/es_NI.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Agregar", 5 | "Retry" : "Reintentar", 6 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 7 | "Remove" : "Eliminar" 8 | }, 9 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"); 10 | -------------------------------------------------------------------------------- /l10n/es_PA.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Agregar", 5 | "Retry" : "Reintentar", 6 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 7 | "Remove" : "Eliminar" 8 | }, 9 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"); 10 | -------------------------------------------------------------------------------- /l10n/es_PE.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Agregar", 5 | "Retry" : "Reintentar", 6 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 7 | "Remove" : "Eliminar" 8 | }, 9 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"); 10 | -------------------------------------------------------------------------------- /l10n/es_PR.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Agregar", 5 | "Retry" : "Reintentar", 6 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 7 | "Remove" : "Eliminar" 8 | }, 9 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"); 10 | -------------------------------------------------------------------------------- /l10n/es_PY.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Agregar", 5 | "Retry" : "Reintentar", 6 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 7 | "Remove" : "Eliminar" 8 | }, 9 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"); 10 | -------------------------------------------------------------------------------- /l10n/es_SV.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Agregar", 5 | "Retry" : "Reintentar", 6 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 7 | "Remove" : "Eliminar" 8 | }, 9 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"); 10 | -------------------------------------------------------------------------------- /l10n/es_UY.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Agregar", 5 | "Retry" : "Reintentar", 6 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 7 | "Remove" : "Eliminar" 8 | }, 9 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"); 10 | -------------------------------------------------------------------------------- /l10n/sq.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Shto", 5 | "An error occurred: {msg}" : "Ndodhi një gabim: {msg}", 6 | "Retry" : "Riprovoni", 7 | "An error occurred. Please try again." : "Ndodhi një problem.Ju lutem provoni përsëri.", 8 | "Remove" : "Hiq" 9 | }, 10 | "nplurals=2; plural=(n != 1);"); 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for building the project 2 | # 3 | # SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors 4 | # SPDX-License-Identifier: AGPL-3.0-or-later 5 | all: install 6 | 7 | clean: 8 | rm -rf vendor 9 | rm -rf node_modules 10 | rm -rf js/build 11 | 12 | install: 13 | composer install --dev 14 | npm install 15 | npm run build 16 | -------------------------------------------------------------------------------- /l10n/mk.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Додади", 5 | "Retry" : "Обидете се повторно", 6 | "Your browser does not support WebAuthn." : "Вашиот прелистувач не поддржува WebAuthn.", 7 | "Active" : "Активно", 8 | "Remove" : "Отстрани " 9 | }, 10 | "nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;"); 11 | -------------------------------------------------------------------------------- /templates/personal.php: -------------------------------------------------------------------------------- 1 | 13 | 14 |
15 | -------------------------------------------------------------------------------- /l10n/es_AR.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Añadir", 3 | "Retry" : "Reintentar", 4 | "An error occurred. Please try again." : "Se presentó un erorr. Favor de volver a intentar. ", 5 | "Your browser does not support WebAuthn." : "Tu navegador no admite WebAuthn.", 6 | "Remove" : "Borrar" 7 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" 8 | } -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | lang_map = nb_NO: nb, sk_SK: sk, th_TH: th, ja_JP: ja, bg_BG: bg, cs_CZ: cs, fi_FI: fi, hu_HU: hu 4 | 5 | [o:nextcloud:p:nextcloud:r:twofactor_webauthn] 6 | file_filter = translationfiles//twofactor_webauthn.po 7 | source_file = translationfiles/templates/twofactor_webauthn.pot 8 | source_lang = en 9 | type = PO 10 | 11 | -------------------------------------------------------------------------------- /l10n/oc.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Apondre", 3 | "Retry" : "Tornar ensajar", 4 | "An error occurred. Please try again." : "Una error s'es producha. Mercé d'ensajar tornamai.", 5 | "Your browser does not support WebAuthn." : "Vòstre navigador es pas compatible amb WebAuthn.", 6 | "Active" : "Activa", 7 | "Remove" : "Suprimir" 8 | },"pluralForm" :"nplurals=2; plural=(n > 1);" 9 | } -------------------------------------------------------------------------------- /l10n/da.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Tilføj", 3 | "An error occurred: {msg}" : "Der skete en fejl: {msg}", 4 | "Retry" : "Prøv igen", 5 | "An error occurred. Please try again." : "En fejl opstod. Prøv venligst igen.", 6 | "Your browser does not support WebAuthn." : "Din browser understøtter ikke WebAuthn.", 7 | "Remove" : "Fjern" 8 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 9 | } -------------------------------------------------------------------------------- /l10n/es_MX.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Agregar", 3 | "Retry" : "Reintentar", 4 | "An error occurred. Please try again." : "Se presentó un error. Por favor vuelve a intentarlo.", 5 | "Your browser does not support WebAuthn." : "Su navegador no soporta WebAuthn.", 6 | "Active" : "Activo", 7 | "Remove" : "Eliminar" 8 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" 9 | } -------------------------------------------------------------------------------- /l10n/es_AR.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Añadir", 5 | "Retry" : "Reintentar", 6 | "An error occurred. Please try again." : "Se presentó un erorr. Favor de volver a intentar. ", 7 | "Your browser does not support WebAuthn." : "Tu navegador no admite WebAuthn.", 8 | "Remove" : "Borrar" 9 | }, 10 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"); 11 | -------------------------------------------------------------------------------- /l10n/ko.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "추가", 3 | "Authentication cancelled" : "인증이 취소됨", 4 | "An error occurred: {msg}" : "오류 발생: {msg}", 5 | "Retry" : "다시 시도", 6 | "An error occurred. Please try again." : "오류가 발생했습니다. 다시 시도하십시오.", 7 | "Your browser does not support WebAuthn." : "WebAuthn이 현재 브라우저를 지원하지 않습니다.", 8 | "Active" : "활성화", 9 | "Remove" : "삭제" 10 | },"pluralForm" :"nplurals=1; plural=0;" 11 | } -------------------------------------------------------------------------------- /l10n/oc.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Apondre", 5 | "Retry" : "Tornar ensajar", 6 | "An error occurred. Please try again." : "Una error s'es producha. Mercé d'ensajar tornamai.", 7 | "Your browser does not support WebAuthn." : "Vòstre navigador es pas compatible amb WebAuthn.", 8 | "Active" : "Activa", 9 | "Remove" : "Suprimir" 10 | }, 11 | "nplurals=2; plural=(n > 1);"); 12 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # .git-blame-ignore-revs 2 | 3 | # SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors 4 | # SPDX-License-Identifier: AGPL-3.0-or-later 5 | 6 | # Update to coding-standard 1.2.3 7 | 58601e8ebae5dd35ba2f97c343e8b4b03a810a57 8 | 9 | # Update to coding-standard 1.3.1 10 | 0a21189522498b47aa657834e259d2a6fbccb0a5 11 | 12 | # Update to coding-standard 1.3.2 13 | 5b2440c618b9a90cd6ee89f2c1d97776fea38235 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | /build 4 | /js 5 | 6 | /coverage 7 | /.nyc_output 8 | /node_modules 9 | 10 | /nbproject/private/ 11 | /vendor 12 | /vendor-bin/*/vendor 13 | composer.phar 14 | /.php-cs-fixer.cache 15 | 16 | /tests/clover*.xml 17 | /tests/.phpunit.result.cache 18 | 19 | /playwright 20 | /playwright-report 21 | /test-results 22 | -------------------------------------------------------------------------------- /l10n/da.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Tilføj", 5 | "An error occurred: {msg}" : "Der skete en fejl: {msg}", 6 | "Retry" : "Prøv igen", 7 | "An error occurred. Please try again." : "En fejl opstod. Prøv venligst igen.", 8 | "Your browser does not support WebAuthn." : "Din browser understøtter ikke WebAuthn.", 9 | "Remove" : "Fjern" 10 | }, 11 | "nplurals=2; plural=(n != 1);"); 12 | -------------------------------------------------------------------------------- /l10n/sc.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Agiunghe", 3 | "An error occurred: {msg}" : "B'at àpidu un'errore: {msg}", 4 | "Retry" : "Torra a proare", 5 | "An error occurred. Please try again." : "B'at àpidu un'errore. Torra a proare.", 6 | "Your browser does not support WebAuthn." : "Su serbidore tuo non suportat WebAuthn.", 7 | "Active" : "Ativu", 8 | "Remove" : "Boga" 9 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 10 | } -------------------------------------------------------------------------------- /l10n/es_MX.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Agregar", 5 | "Retry" : "Reintentar", 6 | "An error occurred. Please try again." : "Se presentó un error. Por favor vuelve a intentarlo.", 7 | "Your browser does not support WebAuthn." : "Su navegador no soporta WebAuthn.", 8 | "Active" : "Activo", 9 | "Remove" : "Eliminar" 10 | }, 11 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"); 12 | -------------------------------------------------------------------------------- /l10n/ko.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "추가", 5 | "Authentication cancelled" : "인증이 취소됨", 6 | "An error occurred: {msg}" : "오류 발생: {msg}", 7 | "Retry" : "다시 시도", 8 | "An error occurred. Please try again." : "오류가 발생했습니다. 다시 시도하십시오.", 9 | "Your browser does not support WebAuthn." : "WebAuthn이 현재 브라우저를 지원하지 않습니다.", 10 | "Active" : "활성화", 11 | "Remove" : "삭제" 12 | }, 13 | "nplurals=1; plural=0;"); 14 | -------------------------------------------------------------------------------- /l10n/he.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "הוספה", 3 | "An error occurred: {msg}" : "אירעה שגיאה: {msg}", 4 | "Retry" : "ניסיון חוזר", 5 | "An error occurred. Please try again." : "אירעה שגיאה. נא לנסות שוב.", 6 | "Your browser does not support WebAuthn." : "הדפדפן שלך אינו תומך ב־WebAuthn.", 7 | "Active" : "פעיל", 8 | "Remove" : "הסרה" 9 | },"pluralForm" :"nplurals=3; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: 2;" 10 | } -------------------------------------------------------------------------------- /l10n/sc.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Agiunghe", 5 | "An error occurred: {msg}" : "B'at àpidu un'errore: {msg}", 6 | "Retry" : "Torra a proare", 7 | "An error occurred. Please try again." : "B'at àpidu un'errore. Torra a proare.", 8 | "Your browser does not support WebAuthn." : "Su serbidore tuo non suportat WebAuthn.", 9 | "Active" : "Ativu", 10 | "Remove" : "Boga" 11 | }, 12 | "nplurals=2; plural=(n != 1);"); 13 | -------------------------------------------------------------------------------- /src/logger.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import { getCurrentUser } from '@nextcloud/auth' 7 | import { getLoggerBuilder } from '@nextcloud/logger' 8 | 9 | const builder = getLoggerBuilder().setApp('twofactor_webauthn') 10 | 11 | const user = getCurrentUser() 12 | if (user !== null) { 13 | builder.setUid(user.uid) 14 | } 15 | 16 | export default builder.build() 17 | -------------------------------------------------------------------------------- /l10n/es_419.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "You added an WebAuthn hardware token" : "Agregaste un token de hardware WebAuthn ", 3 | "You removed an WebAuthn hardware token" : "Removiste un token de hardware WebAuthn ", 4 | "Add" : "Agregar", 5 | "Retry" : "Reintentar", 6 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 7 | "Remove" : "Eliminar" 8 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" 9 | } -------------------------------------------------------------------------------- /l10n/he.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "הוספה", 5 | "An error occurred: {msg}" : "אירעה שגיאה: {msg}", 6 | "Retry" : "ניסיון חוזר", 7 | "An error occurred. Please try again." : "אירעה שגיאה. נא לנסות שוב.", 8 | "Your browser does not support WebAuthn." : "הדפדפן שלך אינו תומך ב־WebAuthn.", 9 | "Active" : "פעיל", 10 | "Remove" : "הסרה" 11 | }, 12 | "nplurals=3; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: 2;"); 13 | -------------------------------------------------------------------------------- /l10n/sl.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Dodaj", 3 | "An error occurred: {msg}" : "Prišlo je do napake: {msg}", 4 | "Retry" : "Poskusi znova", 5 | "An error occurred. Please try again." : "Prišlo je do napake. Poskusite znova.", 6 | "Your browser does not support WebAuthn." : "Brskalnik ne podpira overitve WebAuthn.", 7 | "Active" : "Dejavno", 8 | "Remove" : "Odstrani" 9 | },"pluralForm" :"nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);" 10 | } -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | loadApp('twofactor_webauthn'); 18 | -------------------------------------------------------------------------------- /l10n/hr.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Dodaj", 3 | "An error occurred: {msg}" : "Došlo je do pogreške: {msg}", 4 | "Retry" : "Pokušaj ponovno", 5 | "An error occurred. Please try again." : "Došlo je do pogreške. Pokušajte ponovo.", 6 | "Your browser does not support WebAuthn." : "Vaš preglednik ne podržava WebAuthn.", 7 | "Active" : "Aktivan", 8 | "Remove" : "Ukloni" 9 | },"pluralForm" :"nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;" 10 | } -------------------------------------------------------------------------------- /l10n/es_419.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "You added an WebAuthn hardware token" : "Agregaste un token de hardware WebAuthn ", 5 | "You removed an WebAuthn hardware token" : "Removiste un token de hardware WebAuthn ", 6 | "Add" : "Agregar", 7 | "Retry" : "Reintentar", 8 | "An error occurred. Please try again." : "Se presentó un erorr. Por favor vuelve a intentarlo.", 9 | "Remove" : "Eliminar" 10 | }, 11 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"); 12 | -------------------------------------------------------------------------------- /l10n/sl.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Dodaj", 5 | "An error occurred: {msg}" : "Prišlo je do napake: {msg}", 6 | "Retry" : "Poskusi znova", 7 | "An error occurred. Please try again." : "Prišlo je do napake. Poskusite znova.", 8 | "Your browser does not support WebAuthn." : "Brskalnik ne podpira overitve WebAuthn.", 9 | "Active" : "Dejavno", 10 | "Remove" : "Odstrani" 11 | }, 12 | "nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);"); 13 | -------------------------------------------------------------------------------- /src/main-login-setup.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import { createApp } from 'vue' 7 | import { createPinia } from 'pinia' 8 | 9 | import Nextcloud from './mixins/Nextcloud.js' 10 | 11 | import LoginSetup from './components/LoginSetup.vue' 12 | 13 | const pinia = createPinia() 14 | 15 | const app = createApp(LoginSetup) 16 | app.mixin(Nextcloud) 17 | app.use(pinia) 18 | app.mount('#twofactor-webauthn-login-setup') 19 | -------------------------------------------------------------------------------- /webpack.test.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | const webpackConfig = require('./webpack.config.js') 7 | 8 | // For some reason mocha(pack) fails to import webpack chunks because they have a ?v=[contenthash] 9 | // suffix that is not actually present in names of written entrypoints. 10 | webpackConfig.output.filename = webpackConfig.output.filename.replace('?v=[contenthash]', '') 11 | 12 | module.exports = webpackConfig 13 | -------------------------------------------------------------------------------- /lib/Settings/Personal.php: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 |
18 | -------------------------------------------------------------------------------- /l10n/hr.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Dodaj", 5 | "An error occurred: {msg}" : "Došlo je do pogreške: {msg}", 6 | "Retry" : "Pokušaj ponovno", 7 | "An error occurred. Please try again." : "Došlo je do pogreške. Pokušajte ponovo.", 8 | "Your browser does not support WebAuthn." : "Vaš preglednik ne podržava WebAuthn.", 9 | "Active" : "Aktivan", 10 | "Remove" : "Ukloni" 11 | }, 12 | "nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;"); 13 | -------------------------------------------------------------------------------- /templates/login-setup.php: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 |
18 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | /** icons for personal page settings **/ 7 | .nav-icon-webauthn-second-factor-auth, .icon-webauthn-device { 8 | background-image: url('../img/app-dark.svg?v=1'); 9 | } 10 | 11 | body.theme--dark .nav-icon-webauthn-second-factor-auth, 12 | body.theme--dark .icon-webauthn-device { 13 | background-image: url('../img/app.svg?v=1'); 14 | } 15 | 16 | #webauthn-http-warning { 17 | color: var(--color-warning); 18 | } 19 | -------------------------------------------------------------------------------- /l10n/br.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Add" : "Ouzhpennañ", 3 | "Retry" : "Klaskit en dro", 4 | "Your browser does not support WebAuthn." : "Ne vez ket douget WebAuthn gant o furcher.", 5 | "Active" : "O labourat", 6 | "Remove" : "Lemel" 7 | },"pluralForm" :"nplurals=5; plural=((n%10 == 1) && (n%100 != 11) && (n%100 !=71) && (n%100 !=91) ? 0 :(n%10 == 2) && (n%100 != 12) && (n%100 !=72) && (n%100 !=92) ? 1 :(n%10 ==3 || n%10==4 || n%10==9) && (n%100 < 10 || n% 100 > 19) && (n%100 < 70 || n%100 > 79) && (n%100 < 90 || n%100 > 99) ? 2 :(n != 0 && n % 1000000 == 0) ? 3 : 4);" 8 | } -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | getFinder() 17 | ->ignoreVCSIgnored(true) 18 | ->notPath('build') 19 | ->notPath('l10n') 20 | ->notPath('lib/Vendor') 21 | ->notPath('src') 22 | ->notPath('vendor') 23 | ->in(__DIR__); 24 | return $config; 25 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "camelcase": false, 3 | "eqeqeq": true, 4 | "immed": true, 5 | "latedef": false, 6 | "noarg": true, 7 | "nonbsp": true, 8 | "undef": true, 9 | "unused": true, 10 | "trailing": true, 11 | "maxparams": 5, 12 | "curly": true, 13 | "maxlen": 120, 14 | "indent": 4, 15 | "browser": true, 16 | "globals": { 17 | "t": true, 18 | "console": true, 19 | "it": true, 20 | "xit": true, 21 | "expect": true, 22 | "describe": true, 23 | "define": true, 24 | "beforeEach": true, 25 | "afterEach": true, 26 | "require": true 27 | }, 28 | "esversion": 6, 29 | "asi": true 30 | } 31 | -------------------------------------------------------------------------------- /l10n/be.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "You added an WebAuthn hardware token" : "Вы дадалі апаратны токен WebAuthn", 3 | "You removed an WebAuthn hardware token" : "Вы выдалілі апаратны токен WebAuthn", 4 | "Add" : "Дадаць", 5 | "Retry" : "Паўтарыць спробу", 6 | "Your browser does not support WebAuthn." : "Ваш браўзер не падтрымлівае WebAuthn.", 7 | "Active" : "Актыўны", 8 | "Remove" : "Выдаліць" 9 | },"pluralForm" :"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);" 10 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | const path = require('path') 7 | const webpackConfig = require('@nextcloud/webpack-vue-config') 8 | 9 | delete webpackConfig.entry['main'] 10 | webpackConfig.entry['challenge'] = path.join(__dirname, 'src', 'main-challenge.js') 11 | webpackConfig.entry['settings'] = path.join(__dirname, 'src', 'main-settings.js') 12 | webpackConfig.entry['login-setup'] = path.join(__dirname, 'src', 'main-login-setup.js') 13 | 14 | module.exports = webpackConfig 15 | -------------------------------------------------------------------------------- /l10n/br.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Add" : "Ouzhpennañ", 5 | "Retry" : "Klaskit en dro", 6 | "Your browser does not support WebAuthn." : "Ne vez ket douget WebAuthn gant o furcher.", 7 | "Active" : "O labourat", 8 | "Remove" : "Lemel" 9 | }, 10 | "nplurals=5; plural=((n%10 == 1) && (n%100 != 11) && (n%100 !=71) && (n%100 !=91) ? 0 :(n%10 == 2) && (n%100 != 12) && (n%100 !=72) && (n%100 !=92) ? 1 :(n%10 ==3 || n%10==4 || n%10==9) && (n%100 < 10 || n% 100 > 19) && (n%100 < 70 || n%100 > 79) && (n%100 < 90 || n%100 > 99) ? 2 :(n != 0 && n % 1000000 == 0) ? 3 : 4);"); 11 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | build: 4 | nodes: 5 | analysis: 6 | tests: 7 | override: 8 | - php-scrutinizer-run --enable-security-analysis 9 | 10 | filter: 11 | excluded_paths: 12 | - 'js/tests/*' 13 | - 'vendor/*' 14 | - 'l10n/*' 15 | - 'tests/*' 16 | 17 | imports: 18 | - javascript 19 | - php 20 | 21 | tools: 22 | external_code_coverage: 23 | timeout: 600 # Timeout in seconds. 10 minutes 24 | runs: 1 25 | -------------------------------------------------------------------------------- /l10n/be.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "You added an WebAuthn hardware token" : "Вы дадалі апаратны токен WebAuthn", 5 | "You removed an WebAuthn hardware token" : "Вы выдалілі апаратны токен WebAuthn", 6 | "Add" : "Дадаць", 7 | "Retry" : "Паўтарыць спробу", 8 | "Your browser does not support WebAuthn." : "Ваш браўзер не падтрымлівае WebAuthn.", 9 | "Active" : "Актыўны", 10 | "Remove" : "Выдаліць" 11 | }, 12 | "nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);"); 13 | -------------------------------------------------------------------------------- /lib/Provider/WebAuthnLoginProvider.php: -------------------------------------------------------------------------------- 1 | } 12 | */ 13 | export async function login(page) { 14 | await page.goto('./index.php/login') 15 | await page.locator('#user').fill('admin') 16 | await page.locator('#password').fill('admin') 17 | await page.locator('#password').press('Enter') 18 | 19 | // Wait for login to finish 20 | await page.waitForURL('**/apps/**') 21 | } 22 | -------------------------------------------------------------------------------- /l10n/lv.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Security key" : "Drošības atslēga", 3 | "Add security key" : "Pievienot drošības atslēgu", 4 | "Please use your security key to authorize." : "Lūgums izmantot savu drošības atslēgu, lai autorizētu.", 5 | "Name your security key" : "Nodēvē savu drošības atslēgu", 6 | "Add" : "Pievienot", 7 | "Adding your security key …" : "Pievieno Tavu drošības atslēgu...", 8 | "Retry" : "Mēģināt vēlreiz", 9 | "Use security key" : "Izmantot drošības atslēgu", 10 | "An error occurred. Please try again." : "Notika kļūda. Mēģini vēlreiz.", 11 | "Remove" : "Noņemt" 12 | },"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);" 13 | } -------------------------------------------------------------------------------- /.nextcloudignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | /AUTHORS.md 4 | /build 5 | /.babelrc 6 | /.eslintrc.js 7 | /.jshintrc 8 | /.git 9 | /.github 10 | /.gitignore 11 | /.nextcloudignore 12 | /.php-cs-fixer.dist.php 13 | /.travis.yml 14 | /.tx 15 | /.scrutinizer.yml 16 | /.git-blame-ignore-revs 17 | /babel.config.js 18 | /CONTRIBUTING.md 19 | /composer.* 20 | /karma.conf.js 21 | /krankerl.toml 22 | /l10n/no-php 23 | /Makefile 24 | /nbproject 25 | /node_modules 26 | /package* 27 | /playwright* 28 | /psalm.xml 29 | /screenshots 30 | /src 31 | /tests 32 | /test-results 33 | /vendor/bin 34 | /renovate.json 35 | /webpack*.config.* 36 | /vendor-bin 37 | -------------------------------------------------------------------------------- /l10n/lv.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Security key" : "Drošības atslēga", 5 | "Add security key" : "Pievienot drošības atslēgu", 6 | "Please use your security key to authorize." : "Lūgums izmantot savu drošības atslēgu, lai autorizētu.", 7 | "Name your security key" : "Nodēvē savu drošības atslēgu", 8 | "Add" : "Pievienot", 9 | "Adding your security key …" : "Pievieno Tavu drošības atslēgu...", 10 | "Retry" : "Mēģināt vēlreiz", 11 | "Use security key" : "Izmantot drošības atslēgu", 12 | "An error occurred. Please try again." : "Notika kļūda. Mēģini vēlreiz.", 13 | "Remove" : "Noņemt" 14 | }, 15 | "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);"); 16 | -------------------------------------------------------------------------------- /tests/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | ../lib 10 | 11 | 12 | 13 | 14 | 15 | 16 | . 17 | 18 | 19 | -------------------------------------------------------------------------------- /appinfo/routes.php: -------------------------------------------------------------------------------- 1 | [ 12 | [ 13 | 'name' => 'settings#startRegister', 14 | 'url' => '/settings/startregister', 15 | 'verb' => 'POST' 16 | ], 17 | [ 18 | 'name' => 'settings#finishRegister', 19 | 'url' => '/settings/finishregister', 20 | 'verb' => 'POST' 21 | ], 22 | [ 23 | 'name' => 'settings#remove', 24 | 'url' => '/settings/remove', 25 | 'verb' => 'POST' 26 | ], 27 | [ 28 | 'name' => 'settings#changeActivationState', 29 | 'url' => '/settings/active', 30 | 'verb' => 'POST' 31 | ], 32 | ] 33 | ]; 34 | -------------------------------------------------------------------------------- /l10n/lt_LT.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Security key" : "Saugumo raktas", 3 | "Add security key" : "Pridėti saugumo raktą", 4 | "Add" : "Pridėti", 5 | "Adding your security key …" : "Pridedamas jūsų saugumo raktas…", 6 | "An error occurred: {msg}" : "Įvyko klaida: {msg}", 7 | "Retry" : "Bandyti dar kartą", 8 | "Use security key" : "Naudoti saugumo raktą", 9 | "An error occurred. Please try again." : "Įvyko klaida. Prašome bandyti dar kartą.", 10 | "Your browser does not support WebAuthn." : "Jūsų naršyklė nepalaiko WebAuthn.", 11 | "Active" : "Aktyvi", 12 | "Remove" : "Šalinti" 13 | },"pluralForm" :"nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 : n % 1 != 0 ? 2: 3);" 14 | } -------------------------------------------------------------------------------- /l10n/lt_LT.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "Security key" : "Saugumo raktas", 5 | "Add security key" : "Pridėti saugumo raktą", 6 | "Add" : "Pridėti", 7 | "Adding your security key …" : "Pridedamas jūsų saugumo raktas…", 8 | "An error occurred: {msg}" : "Įvyko klaida: {msg}", 9 | "Retry" : "Bandyti dar kartą", 10 | "Use security key" : "Naudoti saugumo raktą", 11 | "An error occurred. Please try again." : "Įvyko klaida. Prašome bandyti dar kartą.", 12 | "Your browser does not support WebAuthn." : "Jūsų naršyklė nepalaiko WebAuthn.", 13 | "Active" : "Aktyvi", 14 | "Remove" : "Šalinti" 15 | }, 16 | "nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 : n % 1 != 0 ? 2: 3);"); 17 | -------------------------------------------------------------------------------- /src/main-challenge.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import { createApp } from 'vue' 7 | import { createPinia } from 'pinia' 8 | import Nextcloud from './mixins/Nextcloud.js' 9 | import Challenge from './components/Challenge.vue' 10 | import { loadState } from '@nextcloud/initial-state' 11 | import { useMainStore } from './store.js' 12 | 13 | const pinia = createPinia() 14 | 15 | const credentialRequestOptions = loadState('twofactor_webauthn', 'credential-request-options') 16 | const mainStore = useMainStore(pinia) 17 | mainStore.$patch({ 18 | credentialRequestOptions, 19 | }) 20 | 21 | const app = createApp(Challenge) 22 | app.mixin(Nextcloud) 23 | app.use(pinia) 24 | app.mount('#twofactor-webauthn-challenge') 25 | -------------------------------------------------------------------------------- /lib/Event/StateChanged.php: -------------------------------------------------------------------------------- 1 | user; 27 | } 28 | 29 | public function isEnabled(): bool { 30 | return $this->enabled; 31 | } 32 | 33 | public function isByAdmin(): bool { 34 | return $this->byAdmin; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /l10n/ja.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "You added an WebAuthn hardware token" : "WebAuthnハードウェアトークンを追加しました。", 3 | "You removed an WebAuthn hardware token" : "WebAuthnハードウェアトークンを削除しました。", 4 | "WebAuthn disabled by the administration" : "WebAuthnは管理者により無効にされています。", 5 | "Use WebAuthn for second factor authentication" : "WebAuthnを二要素目の認証に利用する", 6 | "Add" : "追加", 7 | "An error occurred: {msg}" : "エラーが発生しました: {msg}", 8 | "Retry" : "リトライ", 9 | "An error occurred. Please try again." : "エラーが発生しました。もう一度試してください。", 10 | "Your browser does not support WebAuthn." : "WebAuthnをサポートしていないブラウザです。", 11 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "安全でない接続を利用してサイトにアクセスしているため、ブラウザがWebAuthnでの認証を拒否する可能性があります。", 12 | "Active" : "有効", 13 | "Remove" : "削除" 14 | },"pluralForm" :"nplurals=1; plural=0;" 15 | } -------------------------------------------------------------------------------- /.github/workflows/reuse.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | 6 | # SPDX-FileCopyrightText: 2022 Free Software Foundation Europe e.V. 7 | # 8 | # SPDX-License-Identifier: CC0-1.0 9 | 10 | name: REUSE Compliance Check 11 | 12 | on: [pull_request] 13 | 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | reuse-compliance-check: 19 | runs-on: ubuntu-latest-low 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 23 | with: 24 | persist-credentials: false 25 | 26 | - name: REUSE Compliance Check 27 | uses: fsfe/reuse-action@bb774aa972c2a89ff34781233d275075cbddf542 # v5.0.0 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | contact_links: 4 | - name: 🚨 Report a security or privacy issue 5 | url: https://hackerone.com/nextcloud 6 | about: Report security and privacy related issues privately to the Nextcloud team, so we can coordinate the fix and release without potentially exposing all Nextcloud servers and users in the meantime. 7 | - name: ❓ Community Support and Help 8 | url: https://help.nextcloud.com/ 9 | about: Configuration, webserver/proxy or performance issues and other questions 10 | - name: 💼 Nextcloud Enterprise 11 | url: https://portal.nextcloud.com/ 12 | about: If you are a Nextcloud Enterprise customer, or need Professional support, so it can be resolved directly by our dedicated engineers more quickly 13 | -------------------------------------------------------------------------------- /src/tests/e2e/virtualAuthenticator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | /** 7 | * Create a new virtual authenticator for a single test. 8 | * This method is best used in a beforeEach hook. 9 | * 10 | * @param {import("@playwright/test").Page} page The page object to create the authenticator for 11 | * @return {Promise} 12 | */ 13 | export async function createVirtualAuthenticator(page) { 14 | const cdpSession = await page.context().newCDPSession(page) 15 | await cdpSession.send('WebAuthn.enable') 16 | await cdpSession.send('WebAuthn.addVirtualAuthenticator', { 17 | options: { 18 | protocol: 'ctap2', 19 | ctap2Version: 'ctap2_1', 20 | hasUserVerification: true, 21 | transport: 'usb', 22 | automaticPresenceSimulation: true, 23 | isUserVerified: true, 24 | }, 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /playwright.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | const { defineConfig, devices } = require('@playwright/test') 7 | const path = require('path') 8 | 9 | /** 10 | * @see https://playwright.dev/docs/test-configuration 11 | */ 12 | export default defineConfig({ 13 | testDir: './src/tests/e2e', 14 | fullyParallel: false, 15 | forbidOnly: !!process.env.CI, 16 | retries: process.env.CI ? 2 : 0, 17 | workers: 1, 18 | reporter: [ 19 | ['list'], 20 | ['html'], 21 | ], 22 | use: { 23 | baseURL: process.env.PLAYWRIGHT_BASE_URL ?? 'https://localhost', 24 | trace: 'on-first-retry', 25 | }, 26 | projects: [ 27 | { 28 | name: 'chromium', 29 | testMatch: '**/*.spec.js', 30 | use: { 31 | ...devices['Desktop Chrome'], 32 | ignoreHTTPSErrors: true, 33 | }, 34 | }, 35 | ], 36 | }) 37 | -------------------------------------------------------------------------------- /l10n/ja.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "You added an WebAuthn hardware token" : "WebAuthnハードウェアトークンを追加しました。", 5 | "You removed an WebAuthn hardware token" : "WebAuthnハードウェアトークンを削除しました。", 6 | "WebAuthn disabled by the administration" : "WebAuthnは管理者により無効にされています。", 7 | "Use WebAuthn for second factor authentication" : "WebAuthnを二要素目の認証に利用する", 8 | "Add" : "追加", 9 | "An error occurred: {msg}" : "エラーが発生しました: {msg}", 10 | "Retry" : "リトライ", 11 | "An error occurred. Please try again." : "エラーが発生しました。もう一度試してください。", 12 | "Your browser does not support WebAuthn." : "WebAuthnをサポートしていないブラウザです。", 13 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "安全でない接続を利用してサイトにアクセスしているため、ブラウザがWebAuthnでの認証を拒否する可能性があります。", 14 | "Active" : "有効", 15 | "Remove" : "削除" 16 | }, 17 | "nplurals=1; plural=0;"); 18 | -------------------------------------------------------------------------------- /tests/Unit/Event/StateChangedTest.php: -------------------------------------------------------------------------------- 1 | createMock(IUser::class); 19 | 20 | $event = new StateChanged($user, true); 21 | 22 | self::assertTrue($event->isEnabled()); 23 | self::assertSame($user, $event->getUser()); 24 | } 25 | 26 | public function testDisabledState(): void { 27 | $user = $this->createMock(IUser::class); 28 | 29 | $event = new StateChanged($user, false); 30 | 31 | self::assertFalse($event->isEnabled()); 32 | self::assertSame($user, $event->getUser()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /img/app-dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/app.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/tests/unit/setup.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | // Emulate browser environment for tests (inside node) 7 | // Ref (url): https://github.com/jsdom/jsdom/issues/2383#issuecomment-442199291 8 | // Ref (SVGElement): https://github.com/vuejs/core/issues/3590 9 | require('jsdom-global')('', { 10 | url: 'http://localhost', 11 | }) 12 | global.SVGElement = window.SVGElement 13 | 14 | global.expect = require('chai').expect 15 | global.OC = { 16 | getCurrentUser: () => { 17 | return { uid: false } 18 | }, 19 | isUserAdmin() { 20 | return false 21 | }, 22 | getLanguage() { 23 | return 'en' 24 | }, 25 | getLocale() { 26 | return 'en' 27 | }, 28 | } 29 | global.t = (app, str) => str 30 | 31 | // https://github.com/vuejs/vue-test-utils/issues/936 32 | // better fix for "TypeError: Super expression must either be null or 33 | // a function" than pinning an old version of prettier. 34 | window.Date = Date 35 | -------------------------------------------------------------------------------- /src/tests/unit/components/Device.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import { shallowMount } from '@vue/test-utils' 7 | import Nextcloud from '../../../mixins/Nextcloud.js' 8 | import Device from '../../../components/Device.vue' 9 | import { createPinia, setActivePinia } from 'pinia' 10 | import { useMainStore } from '../../../store.js' 11 | 12 | describe('Device', () => { 13 | let pinia 14 | 15 | beforeEach(() => { 16 | pinia = createPinia() 17 | setActivePinia(pinia) 18 | }) 19 | 20 | it('renders devices without a name', () => { 21 | const mainStore = useMainStore() 22 | mainStore.$patch({ 23 | devices: [{ 24 | id: 'k1', 25 | name: undefined, 26 | }], 27 | }) 28 | 29 | const device = shallowMount(Device, { 30 | global: { 31 | plugins: [pinia], 32 | mixins: [Nextcloud], 33 | }, 34 | }) 35 | 36 | expect(device.text()).to.have.string('Unnamed key') 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /img/device-disabled.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/Migration/Version000203Date20200322201800.php: -------------------------------------------------------------------------------- 1 | hasTable('twofactor_webauthn_registrations')) { 29 | $table = $schema->getTable('twofactor_webauthn_registrations'); 30 | $table->dropColumn('aaguid_transform'); 31 | } 32 | 33 | return $schema; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/Migration/Version000400Date20220524125249.php: -------------------------------------------------------------------------------- 1 | hasTable('twofactor_webauthn_registrations')) { 30 | $schema->dropTable('twofactor_webauthn_registrations'); 31 | } 32 | 33 | return $schema; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/Migration/Version000203Date20200322200700.php: -------------------------------------------------------------------------------- 1 | hasTable('twofactor_webauthn_registrations')) { 30 | $table = $schema->getTable('twofactor_webauthn_registrations'); 31 | $table->dropColumn('aaguid'); 32 | } 33 | 34 | return $schema; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /src/main-settings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import { loadState } from '@nextcloud/initial-state' 7 | import { createApp } from 'vue' 8 | import { createPinia } from 'pinia' 9 | 10 | import Nextcloud from './mixins/Nextcloud.js' 11 | import PersonalSettings from './components/PersonalSettings.vue' 12 | import { useMainStore } from './store.js' 13 | 14 | import '@nextcloud/password-confirmation/style.css' 15 | 16 | const pinia = createPinia() 17 | 18 | const devices = loadState('twofactor_webauthn', 'devices') 19 | devices.sort((d1, d2) => { 20 | if (!d1.name) { 21 | return 1 22 | } else if (!d2.name) { 23 | return -1 24 | } else { 25 | return d1.name.localeCompare(d2.name) 26 | } 27 | }) 28 | const mainStore = useMainStore(pinia) 29 | mainStore.$patch({ 30 | devices, 31 | }) 32 | 33 | const app = createApp(PersonalSettings, { 34 | httpWarning: document.location.protocol !== 'https:', 35 | }) 36 | app.mixin(Nextcloud) 37 | app.use(pinia) 38 | app.mount('#twofactor-webauthn-settings') 39 | -------------------------------------------------------------------------------- /lib/Migration/Version000200Date20200310200500.php: -------------------------------------------------------------------------------- 1 | hasTable('twofactor_webauthn_registrations')) { 30 | $table = $schema->getTable('twofactor_webauthn_registrations'); 31 | $table->getColumn('credential_public_key')->setOptions(['length' => 2000]); 32 | } 33 | 34 | return $schema; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/Migration/Version000103Date20200308114300.php: -------------------------------------------------------------------------------- 1 | hasTable('twofactor_webauthn_registrations')) { 30 | $table = $schema->getTable('twofactor_webauthn_registrations'); 31 | $table->addColumn('active', 'boolean', [ 32 | 'notnull' => true, 33 | 'default' => true, 34 | ]); 35 | } 36 | 37 | return $schema; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/Migration/Version010000Date20220929073925.php: -------------------------------------------------------------------------------- 1 | getTable('twofactor_webauthn_regs'); 30 | if (!$registrationsTable->hasColumn('created_at')) { 31 | $registrationsTable->addColumn('created_at', 'integer', [ 32 | 'notnull' => false, 33 | 'length' => 8, 34 | ]); 35 | } 36 | 37 | return $schema; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/Migration/Version010000Date20221227080000.php: -------------------------------------------------------------------------------- 1 | getTable('twofactor_webauthn_regs'); 30 | /** 31 | * Fixes https://github.com/nextcloud/twofactor_webauthn/issues/233 32 | */ 33 | $registrationsTable 34 | ->getColumn('public_key_credential_id') 35 | ->setLength(512); 36 | 37 | return $schema; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.github/workflows/block-unconventional-commits.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | # 6 | # SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors 7 | # SPDX-License-Identifier: MIT 8 | 9 | name: Block unconventional commits 10 | 11 | on: 12 | pull_request: 13 | types: [opened, ready_for_review, reopened, synchronize] 14 | 15 | permissions: 16 | contents: read 17 | 18 | concurrency: 19 | group: block-unconventional-commits-${{ github.head_ref || github.run_id }} 20 | cancel-in-progress: true 21 | 22 | jobs: 23 | block-unconventional-commits: 24 | name: Block unconventional commits 25 | 26 | runs-on: ubuntu-latest-low 27 | 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 31 | with: 32 | persist-credentials: false 33 | 34 | - uses: webiny/action-conventional-commits@8bc41ff4e7d423d56fa4905f6ff79209a78776c7 # v1.3.0 35 | with: 36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 37 | -------------------------------------------------------------------------------- /.github/workflows/lint-info-xml.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | # 6 | # SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors 7 | # SPDX-License-Identifier: MIT 8 | 9 | name: Lint info.xml 10 | 11 | on: pull_request 12 | 13 | permissions: 14 | contents: read 15 | 16 | concurrency: 17 | group: lint-info-xml-${{ github.head_ref || github.run_id }} 18 | cancel-in-progress: true 19 | 20 | jobs: 21 | xml-linters: 22 | runs-on: ubuntu-latest-low 23 | 24 | name: info.xml lint 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 28 | 29 | - name: Download schema 30 | run: wget https://raw.githubusercontent.com/nextcloud/appstore/master/nextcloudappstore/api/v1/release/info.xsd 31 | 32 | - name: Lint info.xml 33 | uses: ChristophWurst/xmllint-action@36f2a302f84f8c83fceea0b9c59e1eb4a616d3c1 # v1.2 34 | with: 35 | xml-file: ./appinfo/info.xml 36 | xml-schema-file: ./info.xsd 37 | -------------------------------------------------------------------------------- /src/tests/e2e/login-setup.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import { test } from '@playwright/test' 7 | import { createVirtualAuthenticator } from './virtualAuthenticator.js' 8 | import { disableTwofactorAuth, enforceTwofactorAuth } from './occ.js' 9 | 10 | test.beforeAll(async () => { 11 | await enforceTwofactorAuth(true) 12 | }) 13 | 14 | test.beforeEach(async ({ page }) => { 15 | await disableTwofactorAuth(['admin']) 16 | await createVirtualAuthenticator(page) 17 | }) 18 | 19 | test('setup and login', async ({ page }) => { 20 | await page.goto('./index.php/login') 21 | await page.locator('#user').fill('admin') 22 | await page.locator('#password').fill('admin') 23 | await page.locator('#password').press('Enter') 24 | await page.getByRole('link', { name: 'Security key Use WebAuthn for' }).click() 25 | await page.getByRole('button', { name: 'Add security key' }).click() 26 | await page.getByPlaceholder('Name your security key').fill('key') 27 | await page.getByRole('button', { name: 'Add' }).click() 28 | await page.getByRole('link', { name: 'Security key Use WebAuthn for' }).click() 29 | 30 | // Wait for log in to finish 31 | await page.waitForURL('**/apps/**') 32 | }) 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2_feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature request 3 | about: Suggest an idea for this app 4 | labels: 0. Needs triage, enhancement 5 | --- 6 | 7 | 8 | 9 | ### How to use GitHub 10 | 11 | * Please use the 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to show that you are interested into the same feature. 12 | * Please don't comment if you have no relevant information to add. It's just extra noise for everyone subscribed to this issue. 13 | * Subscribe to receive notifications on status change and new comments. 14 | 15 | --- 16 | 17 | ## Feature request 18 | 19 | **Which Nextcloud Version are you currently using:** (see administration page) 20 | 21 | **Is your feature request related to a problem? Please describe.** 22 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 23 | 24 | **Describe the solution you'd like** 25 | A clear and concise description of what you want to happen. 26 | 27 | **Describe alternatives you've considered** 28 | A clear and concise description of any alternative solutions or features you've considered. 29 | 30 | **Additional context** 31 | Add any other context or screenshots about the feature request here. 32 | -------------------------------------------------------------------------------- /l10n/nl.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "You added an WebAuthn hardware token" : "Een WebAuthn hardware token is toegevoegd", 3 | "You removed an WebAuthn hardware token" : "Een WebAuthn hardware token is verwijderd", 4 | "WebAuthn disabled by the administration" : "WebAuthn uitgeschakeld door de beheerder", 5 | "Use WebAuthn for second factor authentication" : "WebAuthn gebruiken voor tweede factor authenticatie", 6 | "Two-Factor WebAuthn" : "Twee-factor WebAuthn", 7 | "WebAuthn two-factor provider" : "WebAuthn twee-factor provider", 8 | "A two-factor provider for WebAuthn devices" : "Een twee-factor provider voor WebAuthn-toestellen", 9 | "Add" : "Toevoegen", 10 | "An error occurred: {msg}" : "Er is een fout opgetreden: {msg}", 11 | "Retry" : "Opnieuw", 12 | "An error occurred. Please try again." : "Er is een fout opgetreden. Opnieuw.", 13 | "Your browser does not support WebAuthn." : "De browser ondersteunt geen WebAuthn.", 14 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "Deze site wordt via een onveilige verbinding geopend. Browsers kunnen daarom de WebAuthn verificatie weigeren.", 15 | "Active" : "Actief", 16 | "Remove" : "Verwijderen" 17 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 18 | } -------------------------------------------------------------------------------- /src/services/RegistrationService.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import Axios from '@nextcloud/axios' 7 | import { generateUrl } from '@nextcloud/router' 8 | 9 | /** 10 | * 11 | */ 12 | export async function startRegistration() { 13 | const url = generateUrl('/apps/twofactor_webauthn/settings/startregister') 14 | 15 | const res = await Axios.post(url) 16 | return res.data 17 | } 18 | 19 | /** 20 | * @param name 21 | * @param data 22 | */ 23 | export async function finishRegistration(name, data) { 24 | const url = generateUrl('/apps/twofactor_webauthn/settings/finishregister') 25 | 26 | const res = await Axios.post(url, { name, data }) 27 | return res.data 28 | } 29 | 30 | /** 31 | * @param id 32 | */ 33 | export async function removeRegistration(id) { 34 | const url = generateUrl('/apps/twofactor_webauthn/settings/remove') 35 | 36 | const res = await Axios.post(url, { id }) 37 | return res.data 38 | } 39 | 40 | /** 41 | * @param id 42 | * @param active 43 | */ 44 | export async function changeActivationState(id, active) { 45 | const url = generateUrl('/apps/twofactor_webauthn/settings/active') 46 | 47 | const res = await Axios.post(url, { id, active: active ? 1 : 0 }) 48 | return res.data 49 | } 50 | -------------------------------------------------------------------------------- /l10n/ast.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "WebAuthn disabled by the administration" : "L'alministración desactivó WebAuthn", 3 | "Security key" : "Llave de seguranza", 4 | "WebAuthn two-factor provider" : "Fornidor de l'autenticación en dos pasos WebAuthn", 5 | "A two-factor provider for WebAuthn devices" : "Un fornidor d'autenticación en dos paasos pa preseos WebAuthn", 6 | "Add security key" : "Amestar una llave de seguranza", 7 | "Please use your security key to authorize." : "Usa la to llave de seguranza p'autorizar.", 8 | "Add" : "Amestar", 9 | "An error occurred: {msg}" : "Prodúxose un error: {msg}", 10 | "Retry" : "Retentar", 11 | "An error occurred. Please try again." : "Prodúxose un error. Volvi tentalo.", 12 | "Your browser does not support WebAuthn." : "El restolador nun ye compatible con WebAuthn.", 13 | "Unnamed key" : "Llave ensin nome", 14 | "Registered" : "Rexistróse", 15 | "Active" : "Activa", 16 | "Remove" : "Quitar", 17 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "Nun se configuró nenguna llave de seguranza. Pel momentu, nun tas usando WebAuthn como autenticación de dos pasos", 18 | "All security keys are deactivated." : "Toles llaves de seguranza tán desactivaes." 19 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 20 | } -------------------------------------------------------------------------------- /.github/workflows/lint-eslint-when-unrelated.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | # 6 | # Use lint-eslint together with lint-eslint-when-unrelated to make eslint a required check for GitHub actions 7 | # https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/troubleshooting-required-status-checks#handling-skipped-but-required-checks 8 | # 9 | # SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors 10 | # SPDX-License-Identifier: MIT 11 | 12 | name: Lint eslint 13 | 14 | on: 15 | pull_request: 16 | paths-ignore: 17 | - '.github/workflows/**' 18 | - 'src/**' 19 | - 'appinfo/info.xml' 20 | - 'package.json' 21 | - 'package-lock.json' 22 | - 'tsconfig.json' 23 | - '.eslintrc.*' 24 | - '.eslintignore' 25 | - '**.js' 26 | - '**.ts' 27 | - '**.vue' 28 | 29 | permissions: 30 | contents: read 31 | 32 | jobs: 33 | lint: 34 | permissions: 35 | contents: none 36 | 37 | runs-on: ubuntu-latest 38 | 39 | name: eslint 40 | 41 | steps: 42 | - run: 'echo "No eslint required"' 43 | -------------------------------------------------------------------------------- /l10n/nl.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "You added an WebAuthn hardware token" : "Een WebAuthn hardware token is toegevoegd", 5 | "You removed an WebAuthn hardware token" : "Een WebAuthn hardware token is verwijderd", 6 | "WebAuthn disabled by the administration" : "WebAuthn uitgeschakeld door de beheerder", 7 | "Use WebAuthn for second factor authentication" : "WebAuthn gebruiken voor tweede factor authenticatie", 8 | "Two-Factor WebAuthn" : "Twee-factor WebAuthn", 9 | "WebAuthn two-factor provider" : "WebAuthn twee-factor provider", 10 | "A two-factor provider for WebAuthn devices" : "Een twee-factor provider voor WebAuthn-toestellen", 11 | "Add" : "Toevoegen", 12 | "An error occurred: {msg}" : "Er is een fout opgetreden: {msg}", 13 | "Retry" : "Opnieuw", 14 | "An error occurred. Please try again." : "Er is een fout opgetreden. Opnieuw.", 15 | "Your browser does not support WebAuthn." : "De browser ondersteunt geen WebAuthn.", 16 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "Deze site wordt via een onveilige verbinding geopend. Browsers kunnen daarom de WebAuthn verificatie weigeren.", 17 | "Active" : "Actief", 18 | "Remove" : "Verwijderen" 19 | }, 20 | "nplurals=2; plural=(n != 1);"); 21 | -------------------------------------------------------------------------------- /l10n/ast.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "WebAuthn disabled by the administration" : "L'alministración desactivó WebAuthn", 5 | "Security key" : "Llave de seguranza", 6 | "WebAuthn two-factor provider" : "Fornidor de l'autenticación en dos pasos WebAuthn", 7 | "A two-factor provider for WebAuthn devices" : "Un fornidor d'autenticación en dos paasos pa preseos WebAuthn", 8 | "Add security key" : "Amestar una llave de seguranza", 9 | "Please use your security key to authorize." : "Usa la to llave de seguranza p'autorizar.", 10 | "Add" : "Amestar", 11 | "An error occurred: {msg}" : "Prodúxose un error: {msg}", 12 | "Retry" : "Retentar", 13 | "An error occurred. Please try again." : "Prodúxose un error. Volvi tentalo.", 14 | "Your browser does not support WebAuthn." : "El restolador nun ye compatible con WebAuthn.", 15 | "Unnamed key" : "Llave ensin nome", 16 | "Registered" : "Rexistróse", 17 | "Active" : "Activa", 18 | "Remove" : "Quitar", 19 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "Nun se configuró nenguna llave de seguranza. Pel momentu, nun tas usando WebAuthn como autenticación de dos pasos", 20 | "All security keys are deactivated." : "Toles llaves de seguranza tán desactivaes." 21 | }, 22 | "nplurals=2; plural=(n != 1);"); 23 | -------------------------------------------------------------------------------- /src/store.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import { defineStore } from 'pinia' 7 | import * as RegistrationService from './services/RegistrationService.js' 8 | 9 | export const useMainStore = defineStore('main', { 10 | state: () => ({ 11 | credentialRequestOptions: {}, 12 | devices: [], 13 | }), 14 | actions: { 15 | addDevice(device) { 16 | this.devices.push(device) 17 | this.devices.sort((d1, d2) => d1.name.localeCompare(d2.name)) 18 | }, 19 | 20 | async removeDevice(entityId) { 21 | const device = this.devices.find(device => device.entityId === entityId) 22 | 23 | this.devices = this.devices.filter(device => device.entityId !== entityId) 24 | 25 | try { 26 | await RegistrationService.removeRegistration(entityId) 27 | } catch (err) { 28 | // Rollback 29 | this.addDevice(device) 30 | 31 | throw err 32 | } 33 | }, 34 | 35 | async changeActivationState({ entityId, active }) { 36 | this.devices.find(device => device.entityId === entityId).active = active 37 | 38 | try { 39 | await RegistrationService.changeActivationState(entityId, active) 40 | } catch (err) { 41 | // Rollback 42 | this.changeActivationState({ entityId, active: !active }) 43 | 44 | throw err 45 | } 46 | }, 47 | }, 48 | }) 49 | -------------------------------------------------------------------------------- /l10n/ro.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "You added an WebAuthn hardware token" : "Ați adăugat un token hardware de tip WebAuthn", 3 | "You removed an WebAuthn hardware token" : "Ați eliminat un token hardware de tip WebAuthn", 4 | "WebAuthn disabled by the administration" : "WebAuthn dezactivat de către administrație", 5 | "Use WebAuthn for second factor authentication" : "Utilizați WebAuthn pentru autentificarea în doi pași", 6 | "Two-Factor WebAuthn" : "WebAuthn în doi pași", 7 | "WebAuthn two-factor provider" : "Furnizor WebAuthn în doi pași", 8 | "A two-factor provider for WebAuthn devices" : "Un furnizor în doi pași pentru dispozitivele WebAuthn", 9 | "Add" : "Adaugă", 10 | "An error occurred: {msg}" : "S-a produs o eroare: {msg}", 11 | "Retry" : "Reîncearcă", 12 | "An error occurred. Please try again." : "S-a produs o eroare. Vă rugăm să încercați din nou.", 13 | "Your browser does not support WebAuthn." : "Browserul dvs. nu acceptă WebAuthn.", 14 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "Accesați acest site prin intermediul unei conexiuni nesigure. Prin urmare, este posibil ca browserele să refuze autentificarea WebAuthn.", 15 | "Active" : "Activ", 16 | "Remove" : "Elimină" 17 | },"pluralForm" :"nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));" 18 | } -------------------------------------------------------------------------------- /tests/Unit/Activity/SettingTest.php: -------------------------------------------------------------------------------- 1 | l10n = $this->createMock(IL10N::class); 26 | 27 | $this->setting = new Setting($this->l10n); 28 | } 29 | 30 | public function testAll(): void { 31 | self::assertEquals(false, $this->setting->canChangeMail()); 32 | self::assertEquals(false, $this->setting->canChangeStream()); 33 | self::assertEquals('twofactor_webauthn', $this->setting->getIdentifier()); 34 | $this->l10n->expects(self::once()) 35 | ->method('t') 36 | ->with('Security key') 37 | ->willReturn('Sicherheitsschlüssel'); 38 | self::assertEquals('Sicherheitsschlüssel', $this->setting->getName()); 39 | self::assertEquals(30, $this->setting->getPriority()); 40 | self::assertEquals(true, $this->setting->isDefaultEnabledMail()); 41 | self::assertEquals(true, $this->setting->isDefaultEnabledStream()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/Activity/Setting.php: -------------------------------------------------------------------------------- 1 | l10n = $l10n; 25 | } 26 | 27 | /** 28 | * @return boolean 29 | */ 30 | public function canChangeMail() { 31 | return false; 32 | } 33 | 34 | /** 35 | * @return boolean 36 | */ 37 | public function canChangeStream() { 38 | return false; 39 | } 40 | 41 | /** 42 | * @return string 43 | */ 44 | public function getIdentifier() { 45 | return 'twofactor_webauthn'; 46 | } 47 | 48 | /** 49 | * @return string 50 | */ 51 | public function getName() { 52 | return $this->l10n->t('Security key'); 53 | } 54 | 55 | /** 56 | * @return int 57 | */ 58 | public function getPriority() { 59 | return 30; 60 | } 61 | 62 | /** 63 | * @return boolean 64 | */ 65 | public function isDefaultEnabledMail() { 66 | return true; 67 | } 68 | 69 | /** 70 | * @return boolean 71 | */ 72 | public function isDefaultEnabledStream() { 73 | return true; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/Listener/StateChangeActivity.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class StateChangeActivity implements IEventListener { 21 | 22 | /** @var IManager */ 23 | private $activityManager; 24 | 25 | public function __construct(IManager $activityManager) { 26 | $this->activityManager = $activityManager; 27 | } 28 | 29 | public function handle(Event $event): void { 30 | if ($event instanceof StateChanged) { 31 | if ($event->isByAdmin()) { 32 | $subject = 'webauthn_disabled_by_admin'; 33 | } else { 34 | $subject = $event->isEnabled() ? 'webauthn_device_added' : 'webauthn_device_removed'; 35 | } 36 | 37 | $activity = $this->activityManager->generateEvent(); 38 | $activity->setApp('twofactor_webauthn') 39 | ->setType('security') 40 | ->setAuthor($event->getUser()->getUID()) 41 | ->setAffectedUser($event->getUser()->getUID()) 42 | ->setSubject($subject); 43 | $this->activityManager->publish($activity); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /l10n/ro.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "You added an WebAuthn hardware token" : "Ați adăugat un token hardware de tip WebAuthn", 5 | "You removed an WebAuthn hardware token" : "Ați eliminat un token hardware de tip WebAuthn", 6 | "WebAuthn disabled by the administration" : "WebAuthn dezactivat de către administrație", 7 | "Use WebAuthn for second factor authentication" : "Utilizați WebAuthn pentru autentificarea în doi pași", 8 | "Two-Factor WebAuthn" : "WebAuthn în doi pași", 9 | "WebAuthn two-factor provider" : "Furnizor WebAuthn în doi pași", 10 | "A two-factor provider for WebAuthn devices" : "Un furnizor în doi pași pentru dispozitivele WebAuthn", 11 | "Add" : "Adaugă", 12 | "An error occurred: {msg}" : "S-a produs o eroare: {msg}", 13 | "Retry" : "Reîncearcă", 14 | "An error occurred. Please try again." : "S-a produs o eroare. Vă rugăm să încercați din nou.", 15 | "Your browser does not support WebAuthn." : "Browserul dvs. nu acceptă WebAuthn.", 16 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "Accesați acest site prin intermediul unei conexiuni nesigure. Prin urmare, este posibil ca browserele să refuze autentificarea WebAuthn.", 17 | "Active" : "Activ", 18 | "Remove" : "Elimină" 19 | }, 20 | "nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));"); 21 | -------------------------------------------------------------------------------- /lib/Listener/UserDeleted.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | class UserDeleted implements IEventListener { 23 | 24 | /** @var PublicKeyCredentialEntityMapper */ 25 | private $publicKeyCredentialEntityMapper; 26 | 27 | /** @var LoggerInterface */ 28 | private $logger; 29 | 30 | public function __construct(PublicKeyCredentialEntityMapper $publicKeyCredentialEntityMapper, LoggerInterface $logger) { 31 | $this->publicKeyCredentialEntityMapper = $publicKeyCredentialEntityMapper; 32 | $this->logger = $logger; 33 | } 34 | 35 | public function handle(Event $event): void { 36 | if ($event instanceof UserDeletedEvent) { 37 | try { 38 | $this->publicKeyCredentialEntityMapper->deletePublicKeyCredentialsByUserId($event->getUser()->getUID()); 39 | } catch (Exception $e) { 40 | $this->logger->warning($e->getMessage(), ['uid' => $event->getUser()->getUID()]); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/AppInfo/Application.php: -------------------------------------------------------------------------------- 1 | registerEventListener(StateChanged::class, StateChangeActivity::class); 33 | $context->registerEventListener(StateChanged::class, StateChangeRegistryUpdater::class); 34 | $context->registerEventListener(UserDeletedEvent::class, UserDeleted::class); 35 | } 36 | 37 | public function boot(IBootContext $context): void { 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/Db/Registration.php: -------------------------------------------------------------------------------- 1 | addType('counter', 'integer'); 39 | } 40 | 41 | public function jsonSerialize(): array { 42 | return [ 43 | 'id' => $this->getId(), 44 | 'userId' => $this->getUserId(), 45 | 'keyHandle' => $this->getKeyHandle(), 46 | 'publicKey' => $this->getPublicKey(), 47 | 'certificate' => $this->getCertificate(), 48 | 'counter' => $this->getCounter(), 49 | 'name' => $this->getName(), 50 | ]; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /psalm.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/tests/e2e/occ.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import { exec as execCallback } from 'child_process' 7 | import util from 'util' 8 | 9 | const exec = util.promisify(execCallback) 10 | 11 | /** 12 | * Helper to execute the occ command. 13 | * 14 | * @param {string} args Arguments ot pass to occ 15 | * @return {Promise<{stdout: string, stderr: string}>} The result of the command 16 | */ 17 | export async function occ(args) { 18 | return exec(`php ../../occ ${args}`) 19 | } 20 | 21 | /** 22 | * Enforce twofactor auth for all users. 23 | * 24 | * @param {boolean} enforce True to enforce twofactor auth, false to disable it 25 | * @return {Promise} 26 | */ 27 | export async function enforceTwofactorAuth(enforce) { 28 | const flag = enforce ? '--on' : '--off' 29 | try { 30 | await occ(`twofactorauth:enforce ${flag}`) 31 | } catch (error) { 32 | console.error(`Failed to enfore twofactor auth: ${error}`) 33 | throw error 34 | } 35 | } 36 | 37 | /** 38 | * Disable webauthn twofactor auth for all given users. 39 | * 40 | * @param {string[]} users List of uids to disable twofactor auth for 41 | * @return {Promise} 42 | */ 43 | export async function disableTwofactorAuth(users) { 44 | for (const user of users) { 45 | try { 46 | await occ(`twofactorauth:disable ${user} webauthn`) 47 | } catch (error) { 48 | console.error(`Failed to disable twofactor webauthn for ${user}: ${error}`) 49 | throw error 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/Model/Device.php: -------------------------------------------------------------------------------- 1 | getId(), 27 | $entity->getPublicKeyCredentialId(), 28 | $entity->getName(), 29 | $entity->getCreatedAt(), 30 | $entity->isActive() === true, 31 | ); 32 | } 33 | 34 | public function getEntityId(): int { 35 | return $this->entityId; 36 | } 37 | 38 | public function getId(): string { 39 | return $this->id; 40 | } 41 | 42 | public function getName(): string { 43 | return $this->name; 44 | } 45 | 46 | public function getCreatedAt(): ?int { 47 | return $this->createdAt; 48 | } 49 | 50 | public function isActive(): bool { 51 | return $this->active; 52 | } 53 | 54 | #[\ReturnTypeWillChange] 55 | public function jsonSerialize() { 56 | return [ 57 | 'entityId' => $this->getEntityId(), 58 | 'id' => $this->getId(), 59 | 'name' => $this->getName(), 60 | 'createdAt' => $this->getCreatedAt(), 61 | 'active' => $this->isActive(), 62 | ]; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /l10n/fi.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "WebAuthn disabled by the administration" : "WebAuthn on poistettu käytöstä ylläpitäjän toimesta", 3 | "Security key" : "Turva-avain", 4 | "Use WebAuthn for second factor authentication" : "Käytä WebAuthnia kaksivaiheiseen todentamiseen", 5 | "Server error while trying to complete security key registration" : "Palvelinvirhe turva-avaimen rekisteröinnin aikana", 6 | "Add security key" : "Lisää turva-avain", 7 | "Name your security key" : "Anna turva-avaimellesi nimi", 8 | "Add" : "Lisä", 9 | "Adding your security key …" : "Lisätään turva-avaimesi…", 10 | "An error occurred: {msg}" : "Tapahtui virhe: {msg}", 11 | "Retry" : "Yritä uudelleen", 12 | "Use security key" : "Käytä turva-avainta", 13 | "An error occurred. Please try again." : "Tapahtui virhe. Yritä myöhemmin uudelleen.", 14 | "Your browser does not support WebAuthn." : "Selaimesi ei tue WebAuthn-standardia.", 15 | "Unnamed key" : "Nimetön avain", 16 | "Active" : "Aktiivinen", 17 | "Remove" : "Poista", 18 | "Your security key was added successfully. You are now being redirected to the login page." : "Turva-avaimesi liitettiin onnistuneesti. Sinut uudelleenohjataan nyt kirjautumissivulle.", 19 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "Turva-avaimia ei ole määritetty. Et käytä kaksivaiheista WebAuthn-todennusta tällä hetkellä.", 20 | "The following security keys are configured for WebAuthn two-factor authentication:" : "Seuraavat turva-avaimet on määritetty kaksivaiheista WebAuthn-todennusta varten:" 21 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 22 | } -------------------------------------------------------------------------------- /src/components/LoginSetup.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 26 | 27 | 54 | 55 | 61 | -------------------------------------------------------------------------------- /l10n/fi.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "WebAuthn disabled by the administration" : "WebAuthn on poistettu käytöstä ylläpitäjän toimesta", 5 | "Security key" : "Turva-avain", 6 | "Use WebAuthn for second factor authentication" : "Käytä WebAuthnia kaksivaiheiseen todentamiseen", 7 | "Server error while trying to complete security key registration" : "Palvelinvirhe turva-avaimen rekisteröinnin aikana", 8 | "Add security key" : "Lisää turva-avain", 9 | "Name your security key" : "Anna turva-avaimellesi nimi", 10 | "Add" : "Lisä", 11 | "Adding your security key …" : "Lisätään turva-avaimesi…", 12 | "An error occurred: {msg}" : "Tapahtui virhe: {msg}", 13 | "Retry" : "Yritä uudelleen", 14 | "Use security key" : "Käytä turva-avainta", 15 | "An error occurred. Please try again." : "Tapahtui virhe. Yritä myöhemmin uudelleen.", 16 | "Your browser does not support WebAuthn." : "Selaimesi ei tue WebAuthn-standardia.", 17 | "Unnamed key" : "Nimetön avain", 18 | "Active" : "Aktiivinen", 19 | "Remove" : "Poista", 20 | "Your security key was added successfully. You are now being redirected to the login page." : "Turva-avaimesi liitettiin onnistuneesti. Sinut uudelleenohjataan nyt kirjautumissivulle.", 21 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "Turva-avaimia ei ole määritetty. Et käytä kaksivaiheista WebAuthn-todennusta tällä hetkellä.", 22 | "The following security keys are configured for WebAuthn two-factor authentication:" : "Seuraavat turva-avaimet on määritetty kaksivaiheista WebAuthn-todennusta varten:" 23 | }, 24 | "nplurals=2; plural=(n != 1);"); 25 | -------------------------------------------------------------------------------- /lib/Db/RegistrationMapper.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class RegistrationMapper extends QBMapper { 21 | public function __construct(IDBConnection $db) { 22 | parent::__construct($db, 'twofactor_u2f_registrations'); 23 | } 24 | 25 | /** 26 | * @param IUser $user 27 | * @param int $id 28 | */ 29 | public function findRegistration(IUser $user, $id): Registration { 30 | /* @var $qb IQueryBuilder */ 31 | $qb = $this->db->getQueryBuilder(); 32 | 33 | $qb->select('id', 'user_id', 'key_handle', 'public_key', 'certificate', 'counter', 'name') 34 | ->from('twofactor_u2f_registrations') 35 | ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($user->getUID()))) 36 | ->andWhere($qb->expr()->eq('id', $qb->createNamedParameter($id))); 37 | return $this->findEntity($qb); 38 | } 39 | 40 | /** 41 | * @param IUser $user 42 | * @return Registration[] 43 | */ 44 | public function findRegistrations(IUser $user): array { 45 | /* @var $qb IQueryBuilder */ 46 | $qb = $this->db->getQueryBuilder(); 47 | 48 | $qb->select('id', 'user_id', 'key_handle', 'public_key', 'certificate', 'counter', 'name') 49 | ->from('twofactor_u2f_registrations') 50 | ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($user->getUID()))); 51 | return $this->findEntities($qb); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /.github/workflows/lint-php-cs.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | # 6 | # SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors 7 | # SPDX-License-Identifier: MIT 8 | 9 | name: Lint php-cs 10 | 11 | on: pull_request 12 | 13 | permissions: 14 | contents: read 15 | 16 | concurrency: 17 | group: lint-php-cs-${{ github.head_ref || github.run_id }} 18 | cancel-in-progress: true 19 | 20 | jobs: 21 | lint: 22 | runs-on: ubuntu-latest 23 | 24 | name: php-cs 25 | 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 29 | 30 | - name: Get php version 31 | id: versions 32 | uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 33 | 34 | - name: Set up php${{ steps.versions.outputs.php-min }} 35 | uses: shivammathur/setup-php@cf4cade2721270509d5b1c766ab3549210a39a2a # 2.33.0 36 | with: 37 | php-version: ${{ steps.versions.outputs.php-min }} 38 | extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite 39 | coverage: none 40 | ini-file: development 41 | env: 42 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 43 | 44 | - name: Install dependencies 45 | run: composer i 46 | 47 | - name: Lint 48 | run: composer run cs:check || ( echo 'Please run `composer run cs:fix` to format your code' && exit 1 ) 49 | -------------------------------------------------------------------------------- /lib/Activity/Provider.php: -------------------------------------------------------------------------------- 1 | getApp() !== 'twofactor_webauthn') { 39 | throw new InvalidArgumentException(); 40 | } 41 | 42 | $l = $this->l10n->get('twofactor_webauthn', $language); 43 | 44 | $event->setIcon($this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'actions/password.svg'))); 45 | switch ($event->getSubject()) { 46 | case 'webauthn_device_added': 47 | $event->setSubject($l->t('You added an WebAuthn hardware token')); 48 | break; 49 | case 'webauthn_device_removed': 50 | $event->setSubject($l->t('You removed an WebAuthn hardware token')); 51 | break; 52 | case 'webauthn_disabled_by_admin': 53 | $event->setSubject($l->t('WebAuthn disabled by the administration')); 54 | break; 55 | } 56 | return $event; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 5 | # WebAuthn second factor provider for Nextcloud 6 | 7 | [![REUSE status](https://api.reuse.software/badge/github.com/nextcloud/twofactor_webauthn)](https://api.reuse.software/info/github.com/nextcloud/twofactor_webauthn) 8 | 9 | ## Requirements 10 | 11 | In order to use this app for authentication, you have to use a browser that supports the WebAuthn standard. 12 | 13 | ## Migration from Two-Factor U2F 14 | 15 | It is possible to migrate U2F device registrations to WebAuthn devices registrations. For the migratation, you need command line access to run [occ](https://docs.nextcloud.com/server/stable/admin_manual/configuration_server/occ_command.html). 16 | 17 | ```shell 18 | # View options – you can run this for all or only specific users 19 | php occ twofactor_webauthn:migrate-u2f --help 20 | 21 | # Migrate all users 22 | php occ twofactor_webauthn:migrate-u2f --all 23 | 24 | # Disable the U2F app 25 | php occ app:disable twofactor_u2f 26 | 27 | # Clean up any U2F registrations 28 | php occ twofactorauth:cleanup u2f 29 | ``` 30 | 31 | ## Login with external apps 32 | 33 | Once you enable WebAuthn with Two Factor WebAuthn, your applications (for example your GNOME app) will need to login using device passwords. Find out more about this in the [user documentation](https://docs.nextcloud.com/server/stable/user_manual/en/user_2fa.html#using-client-applications-with-two-factor-authentication). 34 | 35 | ## Development Setup 36 | 37 | This app uses [composer](https://getcomposer.org/) and [npm](https://www.npmjs.com/) to manage dependencies. Use 38 | 39 | ```bash 40 | composer install 41 | npm install 42 | npm run build 43 | ``` 44 | 45 | to set up a development version of this app. 46 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextcloud/twofactor_webauthn", 3 | "description": "A two factor provider for WebAuthn devices for Nextcloud", 4 | "type": "project", 5 | "license": "agplv3", 6 | "authors": [ 7 | { 8 | "name": "Christoph Wurst" 9 | }, 10 | { 11 | "name": "Michael Blumenstein" 12 | } 13 | ], 14 | "require": { 15 | "php": ">=8.1.0", 16 | "ext-json": "*", 17 | "bamarni/composer-bin-plugin": "^1.8.2", 18 | "psr/log": "^2||^3", 19 | "web-auth/webauthn-lib": "^4.9.2" 20 | }, 21 | "require-dev": { 22 | "christophwurst/nextcloud_testing": "^1.1.0", 23 | "phpunit/phpunit": "^9.6.31", 24 | "roave/security-advisories": "dev-master" 25 | }, 26 | "scripts": { 27 | "lint": "find . -name \\*.php -not -path './vendor/*' -print0 | xargs -0 -n1 php -l", 28 | "cs:check": "php-cs-fixer fix --dry-run --diff", 29 | "cs:fix": "php-cs-fixer fix", 30 | "psalm": "psalm", 31 | "test": "phpunit -c tests/phpunit.xml", 32 | "test:acceptance": "phpunit -c tests/phpunit.xml tests/Acceptance", 33 | "test:integration": "phpunit -c tests/phpunit.xml tests/Integration", 34 | "test:unit": "phpunit -c tests/phpunit.xml tests/Unit", 35 | "test:acceptance:dev": "phpunit -c tests/phpunit.xml tests/Acceptance --no-coverage", 36 | "test:unit:dev": "phpunit -c tests/phpunit.xml tests/Unit --no-coverage --order-by=defects --stop-on-defect --fail-on-warning --stop-on-error --stop-on-failure", 37 | "post-install-cmd": [ 38 | "@composer bin all install --ansi" 39 | ], 40 | "post-update-cmd": [ 41 | "@composer bin all update --ansi" 42 | ] 43 | }, 44 | "config": { 45 | "optimize-autoloader": true, 46 | "classmap-authoritative": true, 47 | "platform": { 48 | "php": "8.1.0" 49 | }, 50 | "sort-packages": true, 51 | "allow-plugins": { 52 | "bamarni/composer-bin-plugin": true 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/tests/unit/components/PersonalSettings.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import { shallowMount } from '@vue/test-utils' 7 | import { createPinia, setActivePinia } from 'pinia' 8 | import Nextcloud from '../../../mixins/Nextcloud.js' 9 | import PersonalSettings from '../../../components/PersonalSettings.vue' 10 | import { useMainStore } from '../../../store.js' 11 | 12 | describe('PersonalSettings', () => { 13 | let pinia 14 | 15 | beforeEach(() => { 16 | pinia = createPinia() 17 | setActivePinia(pinia) 18 | }) 19 | 20 | it('shows text if no devices are configured', () => { 21 | const settings = shallowMount(PersonalSettings, { 22 | global: { 23 | plugins: [pinia], 24 | mixins: [Nextcloud], 25 | }, 26 | }) 27 | 28 | expect(settings.text()).to.contain('No security keys configured. You are not using WebAuthn as second factor at the moment.') 29 | }) 30 | 31 | it('shows no info text if devices are configured', () => { 32 | const mainStore = useMainStore() 33 | mainStore.$patch({ 34 | devices: [{ 35 | id: 'k1', 36 | name: 'a', 37 | }], 38 | }) 39 | 40 | const settings = shallowMount(PersonalSettings, { 41 | global: { 42 | plugins: [pinia], 43 | mixins: [Nextcloud], 44 | }, 45 | }) 46 | 47 | expect(settings.text()).to.not.contain('No security keys configured. You are not using WebAuthn as second factor at the moment.') 48 | }) 49 | 50 | it('shows a HTTP warning', () => { 51 | const settings = shallowMount(PersonalSettings, { 52 | global: { 53 | plugins: [pinia], 54 | mixins: [Nextcloud], 55 | }, 56 | propsData: { 57 | httpWarning: true, 58 | }, 59 | }) 60 | 61 | expect(settings.text()).to.contain('You are accessing this site via an') 62 | }) 63 | }) 64 | -------------------------------------------------------------------------------- /lib/Listener/StateChangeRegistryUpdater.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | class StateChangeRegistryUpdater implements IEventListener { 24 | 25 | /** @var IRegistry */ 26 | private $providerRegistry; 27 | 28 | /** @var WebAuthnManager */ 29 | private $manager; 30 | 31 | /** @var WebAuthnProvider */ 32 | private $provider; 33 | 34 | public function __construct(IRegistry $providerRegistry, WebAuthnManager $manager, WebAuthnProvider $provider) { 35 | $this->providerRegistry = $providerRegistry; 36 | $this->provider = $provider; 37 | $this->manager = $manager; 38 | } 39 | 40 | public function handle(Event $event): void { 41 | if ($event instanceof StateChanged) { 42 | $devices = array_filter( 43 | $this->manager->getDevices($event->getUser()), 44 | static fn (Device $device) => $device->isActive(), 45 | ); 46 | $hasDevices = !empty($devices); 47 | if ($hasDevices && $event->isEnabled()) { 48 | // The first device was enabled -> enable provider for this user 49 | $this->providerRegistry->enableProviderFor($this->provider, $event->getUser()); 50 | } elseif (!$hasDevices && !$event->isEnabled()) { 51 | // The last device was removed -> disable provider for this user 52 | $this->providerRegistry->disableProviderFor($this->provider, $event->getUser()); 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/1_bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐞 Bug report 3 | about: Help us to improve by reporting a bug 4 | labels: 0. Needs triage, bug 5 | --- 6 | 7 | 8 | 9 | ### How to use GitHub 10 | 11 | * Please use the 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to show that you are affected by the same issue. 12 | * Please don't comment if you have no relevant information to add. It's just extra noise for everyone subscribed to this issue. 13 | * Subscribe to receive notifications on status change and new comments. 14 | 15 | --- 16 | 17 | ### Steps to reproduce 18 | 1. 19 | 2. 20 | 3. 21 | 22 | ### Expected behaviour 23 | Tell us what should happen 24 | 25 | ### Actual behaviour 26 | Tell us what happens instead, if possible also add a screenshot 27 | 28 | ### Server configuration 29 | 30 | **Web server:** Apache/Nginx 31 | 32 | **Database:** MySQL/Maria/SQLite/PostgreSQL 33 | 34 | **PHP version:** 8.1/8.2/8.3 35 | 36 | **Nextcloud version:** (see Nextcloud admin page) 37 | 38 |
39 | List of activated apps 40 | 41 | ``` 42 | If you have access to your command line run e.g.: 43 | sudo -u www-data php occ app:list 44 | from within your Nextcloud installation folder 45 | ``` 46 |
47 | 48 |
49 | Nextcloud configuration 50 | 51 | ``` 52 | If you have access to your command line run e.g.: 53 | sudo -u www-data php occ config:list system 54 | from within your Nextcloud installation folder 55 | ``` 56 |
57 | 58 | ### Browser 59 | 60 | **Browser name:** Firefox/Chrome/Safari/… 61 | 62 | **Browser version:** 124/125/… 63 | 64 | **Operating system:** Windows/Ubuntu/Mac/… 65 | 66 |
67 | Browser log 68 | 69 | ``` 70 | Insert your browser log here, this could for example include: 71 | a) The javascript console log 72 | b) The network log 73 | c) ... 74 | ``` 75 | 76 |
77 | -------------------------------------------------------------------------------- /l10n/zh_CN.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "You added an WebAuthn hardware token" : "您新增了一个 WebAuthn 硬件设备", 3 | "You removed an WebAuthn hardware token" : "您移除了一个 WebAuthn 硬件设备", 4 | "WebAuthn disabled by the administration" : "WebAuthn 已被管理员禁用", 5 | "Security key" : "安全密钥", 6 | "Use WebAuthn for second factor authentication" : "将 WebAuthn 用于两步验证", 7 | "Two-Factor WebAuthn" : "WebAuthn 两步验证", 8 | "WebAuthn two-factor provider" : "WebAuthn 两步验证提供程序", 9 | "A two-factor provider for WebAuthn devices" : "使用 WebAuthn 设备的两步验证提供程序", 10 | "Server error while trying to complete security key registration" : "完成安全密钥注册的过程中发生了服务器错误", 11 | "Add security key" : "添加安全密钥", 12 | "Please use your security key to authorize." : "请使用您的安全密钥进行授权", 13 | "Name your security key" : "命名您的密钥", 14 | "Add" : "添加", 15 | "Adding your security key …" : "正在添加您的安全密钥...", 16 | "Authentication cancelled" : "验证已取消", 17 | "An error occurred: {msg}" : "发生了错误:{msg}", 18 | "Retry" : "重试", 19 | "Use security key" : "使用安全密钥", 20 | "An error occurred. Please try again." : "发生错误。请重试。", 21 | "Your browser does not support WebAuthn." : "您的浏览器不支持 WebAuthn。", 22 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "您正在通过不安全的连接访问此网站。浏览器可能因此拒绝使用 WebAuthn 验证。", 23 | "Unnamed key" : "未命名的密钥", 24 | "Registered" : "已注册", 25 | "Active" : "活动", 26 | "Remove" : "移除", 27 | "Set up a security key as a second factor." : "使用安全密钥进行两步验证", 28 | "Your security key was added successfully. You are now being redirected to the login page." : "您的安全密钥已添加成功。您将被重定向到登录页面。", 29 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "没有已配置的安全密钥。您目前没有将 WebAuthn 用于两步验证。", 30 | "The following security keys are configured for WebAuthn two-factor authentication:" : "配置用于 WebAuthn 两步验证的安全密钥列表如下:", 31 | "All security keys are deactivated." : "所有安全密钥均已停用。" 32 | },"pluralForm" :"nplurals=1; plural=0;" 33 | } -------------------------------------------------------------------------------- /l10n/zh_TW.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "You added an WebAuthn hardware token" : "您已新增一個 WebAuthn 硬體權杖", 3 | "You removed an WebAuthn hardware token" : "您已移除一個 WebAuthn 硬體權杖", 4 | "WebAuthn disabled by the administration" : "管理員已停用 WebAuthn", 5 | "Security key" : "安全金鑰", 6 | "Use WebAuthn for second factor authentication" : "使用 WebAuthn 作為第二步驟驗證", 7 | "Two-Factor WebAuthn" : "兩步驟 WebAuthn", 8 | "WebAuthn two-factor provider" : "WebAuthn 兩步驟提供者", 9 | "A two-factor provider for WebAuthn devices" : "WebAuthn 裝置的兩步驟提供者", 10 | "Server error while trying to complete security key registration" : "嘗試完成安全金鑰註冊時發生伺服器錯誤。", 11 | "Add security key" : "新增安全金鑰", 12 | "Please use your security key to authorize." : "請使用您的安全金鑰驗證。", 13 | "Name your security key" : "為您的安全金鑰命名", 14 | "Add" : "新增", 15 | "Adding your security key …" : "正在心憎您的安全金鑰……", 16 | "Authentication cancelled" : "驗證已取消", 17 | "An error occurred: {msg}" : "遇到錯誤:{msg}", 18 | "Retry" : "重試", 19 | "Use security key" : "使用安全金鑰", 20 | "An error occurred. Please try again." : "發生錯誤。請再試一次。", 21 | "Your browser does not support WebAuthn." : "您的瀏覽器不支援 WebAuthn。", 22 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "您正在透過不安全的連線存取此網站。瀏覽器可能會因此拒絕 WebAuthn 驗證。", 23 | "Unnamed key" : "未命名的金鑰", 24 | "Registered" : "已註冊", 25 | "Active" : "作用中", 26 | "Remove" : "移除", 27 | "Set up a security key as a second factor." : "設定安全金鑰作為第二步驟。", 28 | "Your security key was added successfully. You are now being redirected to the login page." : "您的安全金鑰已新增成功。您現在會被重新導向至登入頁面。", 29 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "尚未設定安全金鑰。您目前無法使用 WebAuthn 作為第二步驟的驗證方式。", 30 | "The following security keys are configured for WebAuthn two-factor authentication:" : "下列安全金鑰已被設定為 WebAuthn 第二步驟驗證使用:", 31 | "All security keys are deactivated." : "所有安全金鑰均已停用。" 32 | },"pluralForm" :"nplurals=1; plural=0;" 33 | } -------------------------------------------------------------------------------- /l10n/zh_HK.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "You added an WebAuthn hardware token" : "你已新增一個 WebAuthn 硬體檢驗代碼。", 3 | "You removed an WebAuthn hardware token" : "你已移除一個 WebAuthn 硬體檢驗代碼。", 4 | "WebAuthn disabled by the administration" : "WebAuthn 已被管理員停用", 5 | "Security key" : "私密密鑰", 6 | "Use WebAuthn for second factor authentication" : "使用 WebAuthn 進行第二因數身份驗證", 7 | "Two-Factor WebAuthn" : "雙步驟 Webauthn", 8 | "WebAuthn two-factor provider" : "Webauthn 雙步驟認證供應商", 9 | "A two-factor provider for WebAuthn devices" : "Webauthn 裝置的雙步驟驟驗證供應商", 10 | "Server error while trying to complete security key registration" : "嘗試完成私密密鑰註冊時發生伺服器錯誤。", 11 | "Add security key" : "添加私密密鑰", 12 | "Please use your security key to authorize." : "請使用您的私密密鑰授權。", 13 | "Name your security key" : "命名您的私密密鑰", 14 | "Add" : "添加", 15 | "Adding your security key …" : "正在新增你的私密密鑰 …", 16 | "Authentication cancelled" : "驗證已取消", 17 | "An error occurred: {msg}" : "發生錯誤:{msg}", 18 | "Retry" : "重試", 19 | "Use security key" : "使用私密密鑰", 20 | "An error occurred. Please try again." : "發生錯誤,請再試一次。", 21 | "Your browser does not support WebAuthn." : "您的瀏覽器不支援 WebAuthn。", 22 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "您正使用不安全的連線。瀏覽器可能會因此拒絕 WebAuthn 驗證。", 23 | "Unnamed key" : "未命名的密鑰", 24 | "Registered" : "已註冊", 25 | "Active" : "活動的", 26 | "Remove" : "移除", 27 | "Set up a security key as a second factor." : "設置安全密鑰作為第二個因素。", 28 | "Your security key was added successfully. You are now being redirected to the login page." : "您的私密密鑰已添加成功。您現在會被重新導向到登入頁面。", 29 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "尚未設定私密密鑰。您現階段無法使用 WebAuthn 進行雙重認證。", 30 | "The following security keys are configured for WebAuthn two-factor authentication:" : "下列私密密鑰已被設定為 WebAuthn 雙重認證使用。", 31 | "All security keys are deactivated." : "所有私密密鑰均已停用。" 32 | },"pluralForm" :"nplurals=1; plural=0;" 33 | } -------------------------------------------------------------------------------- /l10n/zh_CN.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "You added an WebAuthn hardware token" : "您新增了一个 WebAuthn 硬件设备", 5 | "You removed an WebAuthn hardware token" : "您移除了一个 WebAuthn 硬件设备", 6 | "WebAuthn disabled by the administration" : "WebAuthn 已被管理员禁用", 7 | "Security key" : "安全密钥", 8 | "Use WebAuthn for second factor authentication" : "将 WebAuthn 用于两步验证", 9 | "Two-Factor WebAuthn" : "WebAuthn 两步验证", 10 | "WebAuthn two-factor provider" : "WebAuthn 两步验证提供程序", 11 | "A two-factor provider for WebAuthn devices" : "使用 WebAuthn 设备的两步验证提供程序", 12 | "Server error while trying to complete security key registration" : "完成安全密钥注册的过程中发生了服务器错误", 13 | "Add security key" : "添加安全密钥", 14 | "Please use your security key to authorize." : "请使用您的安全密钥进行授权", 15 | "Name your security key" : "命名您的密钥", 16 | "Add" : "添加", 17 | "Adding your security key …" : "正在添加您的安全密钥...", 18 | "Authentication cancelled" : "验证已取消", 19 | "An error occurred: {msg}" : "发生了错误:{msg}", 20 | "Retry" : "重试", 21 | "Use security key" : "使用安全密钥", 22 | "An error occurred. Please try again." : "发生错误。请重试。", 23 | "Your browser does not support WebAuthn." : "您的浏览器不支持 WebAuthn。", 24 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "您正在通过不安全的连接访问此网站。浏览器可能因此拒绝使用 WebAuthn 验证。", 25 | "Unnamed key" : "未命名的密钥", 26 | "Registered" : "已注册", 27 | "Active" : "活动", 28 | "Remove" : "移除", 29 | "Set up a security key as a second factor." : "使用安全密钥进行两步验证", 30 | "Your security key was added successfully. You are now being redirected to the login page." : "您的安全密钥已添加成功。您将被重定向到登录页面。", 31 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "没有已配置的安全密钥。您目前没有将 WebAuthn 用于两步验证。", 32 | "The following security keys are configured for WebAuthn two-factor authentication:" : "配置用于 WebAuthn 两步验证的安全密钥列表如下:", 33 | "All security keys are deactivated." : "所有安全密钥均已停用。" 34 | }, 35 | "nplurals=1; plural=0;"); 36 | -------------------------------------------------------------------------------- /l10n/zh_TW.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "You added an WebAuthn hardware token" : "您已新增一個 WebAuthn 硬體權杖", 5 | "You removed an WebAuthn hardware token" : "您已移除一個 WebAuthn 硬體權杖", 6 | "WebAuthn disabled by the administration" : "管理員已停用 WebAuthn", 7 | "Security key" : "安全金鑰", 8 | "Use WebAuthn for second factor authentication" : "使用 WebAuthn 作為第二步驟驗證", 9 | "Two-Factor WebAuthn" : "兩步驟 WebAuthn", 10 | "WebAuthn two-factor provider" : "WebAuthn 兩步驟提供者", 11 | "A two-factor provider for WebAuthn devices" : "WebAuthn 裝置的兩步驟提供者", 12 | "Server error while trying to complete security key registration" : "嘗試完成安全金鑰註冊時發生伺服器錯誤。", 13 | "Add security key" : "新增安全金鑰", 14 | "Please use your security key to authorize." : "請使用您的安全金鑰驗證。", 15 | "Name your security key" : "為您的安全金鑰命名", 16 | "Add" : "新增", 17 | "Adding your security key …" : "正在心憎您的安全金鑰……", 18 | "Authentication cancelled" : "驗證已取消", 19 | "An error occurred: {msg}" : "遇到錯誤:{msg}", 20 | "Retry" : "重試", 21 | "Use security key" : "使用安全金鑰", 22 | "An error occurred. Please try again." : "發生錯誤。請再試一次。", 23 | "Your browser does not support WebAuthn." : "您的瀏覽器不支援 WebAuthn。", 24 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "您正在透過不安全的連線存取此網站。瀏覽器可能會因此拒絕 WebAuthn 驗證。", 25 | "Unnamed key" : "未命名的金鑰", 26 | "Registered" : "已註冊", 27 | "Active" : "作用中", 28 | "Remove" : "移除", 29 | "Set up a security key as a second factor." : "設定安全金鑰作為第二步驟。", 30 | "Your security key was added successfully. You are now being redirected to the login page." : "您的安全金鑰已新增成功。您現在會被重新導向至登入頁面。", 31 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "尚未設定安全金鑰。您目前無法使用 WebAuthn 作為第二步驟的驗證方式。", 32 | "The following security keys are configured for WebAuthn two-factor authentication:" : "下列安全金鑰已被設定為 WebAuthn 第二步驟驗證使用:", 33 | "All security keys are deactivated." : "所有安全金鑰均已停用。" 34 | }, 35 | "nplurals=1; plural=0;"); 36 | -------------------------------------------------------------------------------- /appinfo/info.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | twofactor_webauthn 8 | Two-Factor WebAuthn 9 | WebAuthn two-factor provider 10 | A two-factor provider for WebAuthn devices 11 | 2.5.0-dev.0 12 | agpl 13 | Christoph Wurst 14 | Michael Blumenstein 15 | Richard Steinmetz 16 | TwoFactorWebauthn 17 | security 18 | 19 | https://github.com/nextcloud/twofactor_webauthn#readme 20 | https://github.com/nextcloud/twofactor_webauthn/issues 21 | https://github.com/nextcloud/twofactor_webauthn.git 22 | 23 | https://raw.githubusercontent.com/nextcloud/twofactor_webauthn/main/screenshots/challenge.png 24 | 25 | 26 | 27 | gmp 28 | 29 | 30 | 31 | 32 | 33 | OCA\TwoFactorWebauthn\Migration\RepairProviderRegistrations 34 | 35 | 36 | 37 | 38 | OCA\TwoFactorWebauthn\Provider\WebAuthnProvider 39 | 40 | 41 | 42 | OCA\TwoFactorWebauthn\Command\CleanUp 43 | OCA\TwoFactorWebauthn\Command\MigrateU2F 44 | 45 | 46 | 47 | 48 | OCA\TwoFactorWebauthn\Activity\Setting 49 | 50 | 51 | OCA\TwoFactorWebauthn\Activity\Provider 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /l10n/zh_HK.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "You added an WebAuthn hardware token" : "你已新增一個 WebAuthn 硬體檢驗代碼。", 5 | "You removed an WebAuthn hardware token" : "你已移除一個 WebAuthn 硬體檢驗代碼。", 6 | "WebAuthn disabled by the administration" : "WebAuthn 已被管理員停用", 7 | "Security key" : "私密密鑰", 8 | "Use WebAuthn for second factor authentication" : "使用 WebAuthn 進行第二因數身份驗證", 9 | "Two-Factor WebAuthn" : "雙步驟 Webauthn", 10 | "WebAuthn two-factor provider" : "Webauthn 雙步驟認證供應商", 11 | "A two-factor provider for WebAuthn devices" : "Webauthn 裝置的雙步驟驟驗證供應商", 12 | "Server error while trying to complete security key registration" : "嘗試完成私密密鑰註冊時發生伺服器錯誤。", 13 | "Add security key" : "添加私密密鑰", 14 | "Please use your security key to authorize." : "請使用您的私密密鑰授權。", 15 | "Name your security key" : "命名您的私密密鑰", 16 | "Add" : "添加", 17 | "Adding your security key …" : "正在新增你的私密密鑰 …", 18 | "Authentication cancelled" : "驗證已取消", 19 | "An error occurred: {msg}" : "發生錯誤:{msg}", 20 | "Retry" : "重試", 21 | "Use security key" : "使用私密密鑰", 22 | "An error occurred. Please try again." : "發生錯誤,請再試一次。", 23 | "Your browser does not support WebAuthn." : "您的瀏覽器不支援 WebAuthn。", 24 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "您正使用不安全的連線。瀏覽器可能會因此拒絕 WebAuthn 驗證。", 25 | "Unnamed key" : "未命名的密鑰", 26 | "Registered" : "已註冊", 27 | "Active" : "活動的", 28 | "Remove" : "移除", 29 | "Set up a security key as a second factor." : "設置安全密鑰作為第二個因素。", 30 | "Your security key was added successfully. You are now being redirected to the login page." : "您的私密密鑰已添加成功。您現在會被重新導向到登入頁面。", 31 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "尚未設定私密密鑰。您現階段無法使用 WebAuthn 進行雙重認證。", 32 | "The following security keys are configured for WebAuthn two-factor authentication:" : "下列私密密鑰已被設定為 WebAuthn 雙重認證使用。", 33 | "All security keys are deactivated." : "所有私密密鑰均已停用。" 34 | }, 35 | "nplurals=1; plural=0;"); 36 | -------------------------------------------------------------------------------- /lib/Controller/SettingsController.php: -------------------------------------------------------------------------------- 1 | manager = $manager; 29 | $this->userSession = $userSession; 30 | } 31 | 32 | /** 33 | * @NoAdminRequired 34 | * @PasswordConfirmationRequired 35 | * @UseSession 36 | */ 37 | public function startRegister(): JSONResponse { 38 | return new JSONResponse($this->manager->startRegistration($this->userSession->getUser(), $this->request->getServerHost())); 39 | } 40 | 41 | /** 42 | * @NoAdminRequired 43 | * @PasswordConfirmationRequired 44 | * 45 | * @param string $name 46 | * @param string $data 47 | */ 48 | public function finishRegister(string $name, string $data): JSONResponse { 49 | return new JSONResponse( 50 | $this->manager->finishRegister( 51 | $this->userSession->getUser(), 52 | $name, 53 | $data 54 | ) 55 | ); 56 | } 57 | 58 | /** 59 | * @NoAdminRequired 60 | * @PasswordConfirmationRequired 61 | */ 62 | public function remove(int $id): JSONResponse { 63 | $this->manager->removeDevice($this->userSession->getUser(), $id); 64 | return new JSONResponse([]); 65 | } 66 | 67 | /** 68 | * @NoAdminRequired 69 | * @PasswordConfirmationRequired 70 | */ 71 | public function changeActivationState(int $id, bool $active): JSONResponse { 72 | $this->manager->changeActivationState($this->userSession->getUser(), $id, $active); 73 | return new JSONResponse([]); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/components/PersonalSettings.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 37 | 38 | 66 | 67 | 69 | -------------------------------------------------------------------------------- /lib/Service/U2FMigrator.php: -------------------------------------------------------------------------------- 1 | base64_urlsafe_decode($registration->getKeyHandle()); 45 | 46 | // AAGUID is not required for legacy U2F sources and should be all zeros 47 | $aaguid = $this->zeroUuid(); 48 | 49 | // Decode U2F key and reuse it 50 | // Raw format of u2f key: 0x4 . [x: 32 bytes] . [y: 32 bytes] 51 | $decodedPublicKey = base64_decode($registration->getPublicKey(), true); 52 | 53 | return new PublicKeyCredentialSource( 54 | $credentialId, 55 | PublicKeyCredentialDescriptor::CREDENTIAL_TYPE_PUBLIC_KEY, 56 | [], 57 | $attestationType, 58 | $trustPath, 59 | $aaguid, 60 | $decodedPublicKey, 61 | $registration->getUserId(), 62 | $registration->getCounter(), 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /.github/workflows/lint-php.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | # 6 | # SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors 7 | # SPDX-License-Identifier: MIT 8 | 9 | name: Lint php 10 | 11 | on: pull_request 12 | 13 | permissions: 14 | contents: read 15 | 16 | concurrency: 17 | group: lint-php-${{ github.head_ref || github.run_id }} 18 | cancel-in-progress: true 19 | 20 | jobs: 21 | matrix: 22 | runs-on: ubuntu-latest-low 23 | outputs: 24 | php-versions: ${{ steps.versions.outputs.php-versions }} 25 | steps: 26 | - name: Checkout app 27 | uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 28 | - name: Get version matrix 29 | id: versions 30 | uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 31 | 32 | php-lint: 33 | runs-on: ubuntu-latest 34 | needs: matrix 35 | strategy: 36 | matrix: 37 | php-versions: ${{fromJson(needs.matrix.outputs.php-versions)}} 38 | 39 | name: php-lint 40 | 41 | steps: 42 | - name: Checkout 43 | uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 44 | 45 | - name: Set up php ${{ matrix.php-versions }} 46 | uses: shivammathur/setup-php@cf4cade2721270509d5b1c766ab3549210a39a2a # 2.33.0 47 | with: 48 | php-version: ${{ matrix.php-versions }} 49 | extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite 50 | coverage: none 51 | ini-file: development 52 | env: 53 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 54 | 55 | - name: Lint 56 | run: composer run lint 57 | 58 | summary: 59 | permissions: 60 | contents: none 61 | runs-on: ubuntu-latest-low 62 | needs: php-lint 63 | 64 | if: always() 65 | 66 | name: php-lint-summary 67 | 68 | steps: 69 | - name: Summary status 70 | run: if ${{ needs.php-lint.result != 'success' && needs.php-lint.result != 'skipped' }}; then exit 1; fi 71 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "twofactor_webauthn", 3 | "version": "2.5.0-dev.0", 4 | "description": "WebAuthn second factor provider for Nextcloud", 5 | "private": true, 6 | "dependencies": { 7 | "@nextcloud/auth": "^2.5.3", 8 | "@nextcloud/axios": "^2.5.2", 9 | "@nextcloud/initial-state": "^3.0.0", 10 | "@nextcloud/logger": "^3.0.2", 11 | "@nextcloud/moment": "^1.3.5", 12 | "@nextcloud/password-confirmation": "^6.0.2", 13 | "@nextcloud/router": "^3.1.0", 14 | "@nextcloud/vue": "^9.3.0", 15 | "@simplewebauthn/browser": "^13.2.2", 16 | "pinia": "^3.0.4", 17 | "vue": "^3.5.25", 18 | "vue-material-design-icons": "^5.3.1" 19 | }, 20 | "devDependencies": { 21 | "@nextcloud/babel-config": "^1.2.0", 22 | "@nextcloud/eslint-config": "^8.4.2", 23 | "@nextcloud/webpack-vue-config": "^6.3.0", 24 | "@playwright/test": "^1.55.0", 25 | "@vue/test-utils": "^2.4.6", 26 | "chai": "^4.5.0", 27 | "jsdom": "^21.1.2", 28 | "jsdom-global": "^3.0.2", 29 | "mocha": "^10.8.2", 30 | "mochapack": "^2.1.5" 31 | }, 32 | "scripts": { 33 | "dev": "webpack --node-env development --progress", 34 | "watch": "webpack --node-env development --progress --watch", 35 | "build": "webpack --node-env production --progress", 36 | "lint": "eslint --ext .js,.vue src", 37 | "lint:fix": "eslint --ext .js,.vue src --fix", 38 | "test:unit": "mochapack --mode development --webpack-config webpack.test.config.js --require src/tests/unit/setup.js src/tests/unit/**/*.spec.js", 39 | "test:unit:watch": "mochapack --mode development -w --webpack-config webpack.test.config.js --require src/tests/unit/setup.js src/tests/unit/**/*.spec.js", 40 | "test:e2e": "playwright test", 41 | "test:e2e:ui": "playwright test --ui" 42 | }, 43 | "browserslist": [ 44 | "last 2 versions", 45 | "ie >= 11" 46 | ], 47 | "engines": { 48 | "node": "^22.0.0", 49 | "npm": "^10.5.0" 50 | }, 51 | "jshintConfig": { 52 | "esversion": 6 53 | }, 54 | "repository": { 55 | "type": "git", 56 | "url": "git+https://github.com/nextcloud/twofactor_webauthn.git" 57 | }, 58 | "author": "Christoph Wurst", 59 | "license": "AGPL-3.0-or-later", 60 | "bugs": { 61 | "url": "https://github.com/nextcloud/twofactor_webauthn/issues" 62 | }, 63 | "homepage": "https://github.com/nextcloud/twofactor_webauthn#readme" 64 | } 65 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | 5 | # Authors 6 | 7 | - Alexander Paetzelt <36155797+alex-nitrokey@users.noreply.github.com> 8 | - Andy Scherzinger 9 | - Ashutosh Verma <81686677+ashuio@users.noreply.github.com> 10 | - Austin Bohannon 11 | - Ben Schumacher 12 | - Christoph Wurst 13 | - Christopher Ng 14 | - comradekingu 15 | - Daniel Kesselberg 16 | - danielkr123 17 | - Ferdinand Thiessen 18 | - j-ed 19 | - Jan-Christoph Borchardt 20 | - Joas Schilling 21 | - John Molakvoæ 22 | - Lukas Reschke 23 | - Michael Blumenstein 24 | - mjanssens <24758392+mjanssens@users.noreply.github.com> 25 | - Morris Jobke 26 | - Oliv4945 27 | - Pablo Hinojosa 28 | - rakekniven 29 | - Richard Steinmetz 30 | - Robin Appelman 31 | - Roeland Jago Douma 32 | - Simon Dellenbach 33 | - Simon Spannagel 34 | - Somebodyisnobody <35230554+Somebodyisnobody@users.noreply.github.com> 35 | - Valdnet <47037905+Valdnet@users.noreply.github.com> 36 | - Vincent Petry 37 | - Xaver Maierhofer 38 | - Zev Lee <60147316+zevlee@users.noreply.github.com> 39 | # Authors 40 | 41 | - 2018 Christoph Wurst 42 | - 2022 Christoph Wurst 43 | - 2023 Richard Steinmetz 44 | - Christoph Wurst 45 | - Christoph Wurst 46 | - Daniel Kesselberg 47 | - Daniel Kesselberg 48 | - Michael Blumenstein 49 | - Richard Steinmetz 50 | - Richard Steinmetz 51 | - Richard Steinmetz 52 | - Zev Lee <60147316+zevlee@users.noreply.github.com> 53 | - Zev Lee <60147316+zevlee@users.noreply.github.com> 54 | -------------------------------------------------------------------------------- /REUSE.toml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | version = 1 4 | SPDX-PackageName = "twofactor_webauthn" 5 | SPDX-PackageSupplier = "Nextcloud " 6 | SPDX-PackageDownloadLocation = "https://github.com/nextcloud/twofactor_webauthn" 7 | 8 | [[annotations]] 9 | path = ["l10n/**.js", "l10n/**.json"] 10 | precedence = "aggregate" 11 | SPDX-FileCopyrightText = "2016-2024 Nextcloud translators" 12 | SPDX-License-Identifier = "AGPL-3.0-or-later" 13 | 14 | [[annotations]] 15 | path = ["composer.json", "composer.lock"] 16 | precedence = "aggregate" 17 | SPDX-FileCopyrightText = "2016 Nextcloud GmbH and Nextcloud contributors" 18 | SPDX-License-Identifier = "AGPL-3.0-or-later" 19 | 20 | [[annotations]] 21 | path = ["package.json", "package-lock.json"] 22 | precedence = "aggregate" 23 | SPDX-FileCopyrightText = "2017 Nextcloud GmbH and Nextcloud contributors" 24 | SPDX-License-Identifier = "AGPL-3.0-or-later" 25 | 26 | [[annotations]] 27 | path = [".tx/config", ".babelrc"] 28 | precedence = "aggregate" 29 | SPDX-FileCopyrightText = "2018 Nextcloud GmbH and Nextcloud contributors" 30 | SPDX-License-Identifier = "AGPL-3.0-or-later" 31 | 32 | [[annotations]] 33 | path = [".jshintrc", ".babelrc"] 34 | precedence = "aggregate" 35 | SPDX-FileCopyrightText = "2019 Nextcloud GmbH and Nextcloud contributors" 36 | SPDX-License-Identifier = "AGPL-3.0-or-later" 37 | 38 | [[annotations]] 39 | path = ["vendor-bin/cs-fixer/composer.json", "vendor-bin/cs-fixer/composer.lock"] 40 | precedence = "aggregate" 41 | SPDX-FileCopyrightText = "2022 Nextcloud GmbH and Nextcloud contributors" 42 | SPDX-License-Identifier = "AGPL-3.0-or-later" 43 | 44 | [[annotations]] 45 | path = ["vendor-bin/psalm/composer.json", "vendor-bin/psalm/composer.lock"] 46 | precedence = "aggregate" 47 | SPDX-FileCopyrightText = "2025 Nextcloud GmbH and Nextcloud contributors" 48 | SPDX-License-Identifier = "AGPL-3.0-or-later" 49 | 50 | [[annotations]] 51 | path = ["renovate.json"] 52 | precedence = "aggregate" 53 | SPDX-FileCopyrightText = "2023 Nextcloud GmbH and Nextcloud contributors" 54 | SPDX-License-Identifier = "AGPL-3.0-or-later" 55 | 56 | [[annotations]] 57 | path = ["img/app.svg", "img/app-dark.svg", "img/device-disabled.svg"] 58 | precedence = "aggregate" 59 | SPDX-FileCopyrightText = "2018-2024 Google LLC" 60 | SPDX-License-Identifier = "Apache-2.0" 61 | 62 | [[annotations]] 63 | path = ".github/CODEOWNERS" 64 | precedence = "aggregate" 65 | SPDX-FileCopyrightText = "2025 Nextcloud GmbH and Nextcloud contributors" 66 | SPDX-License-Identifier = "AGPL-3.0-or-later" 67 | -------------------------------------------------------------------------------- /l10n/ru.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "You added an WebAuthn hardware token" : "Вы добавили аппаратный токен WebAuthn", 3 | "You removed an WebAuthn hardware token" : "Вы удалили аппаратный токен WebAuthn", 4 | "WebAuthn disabled by the administration" : "WebAuthn отключен администрацией", 5 | "Security key" : "Ключ безопасности", 6 | "Use WebAuthn for second factor authentication" : "Используйте WebAuthn для двухфакторной аутентификации", 7 | "Two-Factor WebAuthn" : "Двухфакторный WebAuthn", 8 | "WebAuthn two-factor provider" : "Двухфакторный провайдер WebAuthn", 9 | "A two-factor provider for WebAuthn devices" : "Двухфакторный провайдер для устройств WebAuthn", 10 | "Server error while trying to complete security key registration" : "Ошибка сервера при попытке завершить регистрацию ключа безопасности", 11 | "Add security key" : "Добавить ключ безопасности", 12 | "Name your security key" : "Назовите свой ключ безопасности", 13 | "Add" : "Добавить", 14 | "Adding your security key …" : "Добавление вашего ключа безопасности …", 15 | "An error occurred: {msg}" : "Произошла ошибка: {msg}", 16 | "Retry" : "Повторить", 17 | "Use security key" : "Использовать ключ безопасности", 18 | "An error occurred. Please try again." : "Произошла ошибка. Попробуйте ещё раз.", 19 | "Your browser does not support WebAuthn." : "Ваш браузер не поддерживает WebAuthn.", 20 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "Вы заходите на этот сайт через небезопасное соединение. Поэтому браузеры могут отказаться от проверки подлинности WebAuthn.", 21 | "Unnamed key" : "Безымянный ключ", 22 | "Active" : "Активный", 23 | "Remove" : "Удалить", 24 | "Your security key was added successfully. You are now being redirected to the login page." : "Ваш ключ безопасности был успешно добавлен. Теперь вы перенаправлены на страницу входа в систему.", 25 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "Ключи безопасности не настроены. В данный момент вы не используете WebAuthn в качестве двухэтапной авторизации.", 26 | "The following security keys are configured for WebAuthn two-factor authentication:" : "Следующие ключи безопасности настроены для двухфакторной аутентификации WebAuthn:", 27 | "All security keys are deactivated." : "Все ключи безопасности деактивированы." 28 | },"pluralForm" :"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);" 29 | } -------------------------------------------------------------------------------- /lib/Command/CleanUp.php: -------------------------------------------------------------------------------- 1 | db = $db; 39 | $this->userManager = $userManager; 40 | $this->webauthnMapper = $webauthnMapper; 41 | } 42 | 43 | protected function configure(): void { 44 | $this 45 | ->setName('twofactor_webauthn:cleanup') 46 | ->setDescription('Remove orphaned webauthn credentials'); 47 | } 48 | 49 | protected function execute(InputInterface $input, OutputInterface $output): int { 50 | $io = new SymfonyStyle($input, $output); 51 | $io->title('Remove webauthn credentials for deleted users'); 52 | 53 | foreach ($this->findUserIds() as $userId) { 54 | if ($this->userManager->userExists($userId) === false) { 55 | try { 56 | $io->text('Delete credentials for uid "' . $userId . '"'); 57 | $this->webauthnMapper->deletePublicKeyCredentialsByUserId($userId); 58 | } catch (Exception $e) { 59 | $io->caution('Error deleting credentials: ' . $e->getMessage()); 60 | } 61 | } 62 | } 63 | 64 | $io->success('Orphaned webauthn credentials removed.'); 65 | 66 | $io->text('Thank you for using Two-Factor WebAuthn!'); 67 | return 0; 68 | } 69 | 70 | /** 71 | * @throws Exception 72 | */ 73 | private function findUserIds(): array { 74 | $userIds = []; 75 | 76 | $qb = $this->db->getQueryBuilder() 77 | ->selectDistinct('user_handle') 78 | ->from($this->webauthnMapper->getTableName()); 79 | 80 | $result = $qb->executeQuery(); 81 | 82 | while ($row = $result->fetch()) { 83 | $userIds[] = $row['user_handle']; 84 | } 85 | 86 | $result->closeCursor(); 87 | 88 | return $userIds; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /l10n/ru.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "You added an WebAuthn hardware token" : "Вы добавили аппаратный токен WebAuthn", 5 | "You removed an WebAuthn hardware token" : "Вы удалили аппаратный токен WebAuthn", 6 | "WebAuthn disabled by the administration" : "WebAuthn отключен администрацией", 7 | "Security key" : "Ключ безопасности", 8 | "Use WebAuthn for second factor authentication" : "Используйте WebAuthn для двухфакторной аутентификации", 9 | "Two-Factor WebAuthn" : "Двухфакторный WebAuthn", 10 | "WebAuthn two-factor provider" : "Двухфакторный провайдер WebAuthn", 11 | "A two-factor provider for WebAuthn devices" : "Двухфакторный провайдер для устройств WebAuthn", 12 | "Server error while trying to complete security key registration" : "Ошибка сервера при попытке завершить регистрацию ключа безопасности", 13 | "Add security key" : "Добавить ключ безопасности", 14 | "Name your security key" : "Назовите свой ключ безопасности", 15 | "Add" : "Добавить", 16 | "Adding your security key …" : "Добавление вашего ключа безопасности …", 17 | "An error occurred: {msg}" : "Произошла ошибка: {msg}", 18 | "Retry" : "Повторить", 19 | "Use security key" : "Использовать ключ безопасности", 20 | "An error occurred. Please try again." : "Произошла ошибка. Попробуйте ещё раз.", 21 | "Your browser does not support WebAuthn." : "Ваш браузер не поддерживает WebAuthn.", 22 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "Вы заходите на этот сайт через небезопасное соединение. Поэтому браузеры могут отказаться от проверки подлинности WebAuthn.", 23 | "Unnamed key" : "Безымянный ключ", 24 | "Active" : "Активный", 25 | "Remove" : "Удалить", 26 | "Your security key was added successfully. You are now being redirected to the login page." : "Ваш ключ безопасности был успешно добавлен. Теперь вы перенаправлены на страницу входа в систему.", 27 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "Ключи безопасности не настроены. В данный момент вы не используете WebAuthn в качестве двухэтапной авторизации.", 28 | "The following security keys are configured for WebAuthn two-factor authentication:" : "Следующие ключи безопасности настроены для двухфакторной аутентификации WebAuthn:", 29 | "All security keys are deactivated." : "Все ключи безопасности деактивированы." 30 | }, 31 | "nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);"); 32 | -------------------------------------------------------------------------------- /l10n/en_GB.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "You added an WebAuthn hardware token" : "You added an WebAuthn hardware token", 3 | "You removed an WebAuthn hardware token" : "You removed an WebAuthn hardware token", 4 | "WebAuthn disabled by the administration" : "WebAuthn disabled by the administration", 5 | "Security key" : "Security key", 6 | "Use WebAuthn for second factor authentication" : "Use WebAuthn for second factor authentication", 7 | "Two-Factor WebAuthn" : "Two-Factor WebAuthn", 8 | "WebAuthn two-factor provider" : "WebAuthn two-factor provider", 9 | "A two-factor provider for WebAuthn devices" : "A two-factor provider for WebAuthn devices", 10 | "Server error while trying to complete security key registration" : "Server error while trying to complete security key registration", 11 | "Add security key" : "Add security key", 12 | "Please use your security key to authorize." : "Please use your security key to authorise.", 13 | "Name your security key" : "Name your security key", 14 | "Add" : "Add", 15 | "Adding your security key …" : "Adding your security key …", 16 | "Authentication cancelled" : "Authentication cancelled", 17 | "An error occurred: {msg}" : "An error occurred: {msg}", 18 | "Retry" : "Retry", 19 | "Use security key" : "Use security key", 20 | "An error occurred. Please try again." : "An error occurred. Please try again.", 21 | "Your browser does not support WebAuthn." : "Your browser does not support WebAuthn.", 22 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication.", 23 | "Unnamed key" : "Unnamed key", 24 | "Registered" : "Registered", 25 | "Active" : "Active", 26 | "Remove" : "Remove", 27 | "Set up a security key as a second factor." : "Set up a security key as a second factor.", 28 | "Your security key was added successfully. You are now being redirected to the login page." : "Your security key was added successfully. You are now being redirected to the login page.", 29 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "No security keys configured. You are not using WebAuthn as second factor at the moment.", 30 | "The following security keys are configured for WebAuthn two-factor authentication:" : "The following security keys are configured for WebAuthn two-factor authentication:", 31 | "All security keys are deactivated." : "All security keys are deactivated." 32 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 33 | } -------------------------------------------------------------------------------- /lib/Migration/Version000102Date20191004200147.php: -------------------------------------------------------------------------------- 1 | hasTable('twofactor_webauthn_registrations')) { 31 | $table = $schema->createTable('twofactor_webauthn_registrations'); 32 | $table->addColumn('id', 'integer', [ 33 | 'autoincrement' => true, 34 | 'notnull' => true, 35 | 'length' => 255, 36 | ]); 37 | $table->addColumn('name', 'string', [ 38 | 'notnull' => true, 39 | 'length' => 64, 40 | 'default' => 'default' 41 | ]); 42 | $table->addColumn('public_key_credential_id', 'string', [ 43 | 'notnull' => true, 44 | 'length' => 255 45 | ]); 46 | $table->addColumn('type', 'string', [ 47 | 'notnull' => true, 48 | 'length' => 30, 49 | ]); 50 | $table->addColumn('transports', 'string', [ 51 | 'notnull' => true, 52 | 'length' => 30, 53 | ]); 54 | $table->addColumn('attestation_type', 'string', [ 55 | 'notnull' => true, 56 | 'length' => 6, 57 | ]); 58 | $table->addColumn('trust_path', 'string', [ 59 | 'notnull' => true, 60 | 'length' => 2500, 61 | ]); 62 | $table->addColumn('aaguid', 'string', [ 63 | 'notnull' => false, 64 | 'length' => 36, 65 | ]); 66 | $table->addColumn('credential_public_key', 'string', [ 67 | 'notnull' => true, 68 | 'length' => 2000, 69 | ]); 70 | $table->addColumn('user_handle', 'string', [ 71 | 'notnull' => true, 72 | 'length' => 64 73 | ]); 74 | $table->addColumn('counter', 'integer', [ 75 | 'notnull' => true, 76 | 'length' => 255 77 | ]); 78 | $table->setPrimaryKey(['id']); 79 | $table->addIndex(['user_handle'], 'webauthn_registrations_userHandle'); 80 | $table->addIndex(['public_key_credential_id'], 'webauthn_registrations_publicKeyCredentialId'); 81 | } 82 | */ 83 | 84 | return $schema; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /l10n/fa.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "You added an WebAuthn hardware token" : "You added an WebAuthn hardware token", 3 | "You removed an WebAuthn hardware token" : "You removed an WebAuthn hardware token", 4 | "WebAuthn disabled by the administration" : "WebAuthn disabled by the administration", 5 | "Security key" : "Security key", 6 | "Use WebAuthn for second factor authentication" : "Use WebAuthn for second factor authentication", 7 | "Two-Factor WebAuthn" : "Two-Factor WebAuthn", 8 | "WebAuthn two-factor provider" : "WebAuthn two-factor provider", 9 | "A two-factor provider for WebAuthn devices" : "A two-factor provider for WebAuthn devices", 10 | "Server error while trying to complete security key registration" : "Server error while trying to complete security key registration", 11 | "Add security key" : "Add security key", 12 | "Please use your security key to authorize." : "Please use your security key to authorize.", 13 | "Name your security key" : "Name your security key", 14 | "Add" : "افزودن", 15 | "Adding your security key …" : "Adding your security key …", 16 | "Authentication cancelled" : "Authentication cancelled", 17 | "An error occurred: {msg}" : "An error occurred: {msg}", 18 | "Retry" : "تلاش دوباره", 19 | "Use security key" : "Use security key", 20 | "An error occurred. Please try again." : "An error occurred. Please try again.", 21 | "Your browser does not support WebAuthn." : "Your browser does not support WebAuthn.", 22 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication.", 23 | "Unnamed key" : "Unnamed key", 24 | "Registered" : "Registered", 25 | "Active" : "فعال کردن", 26 | "Remove" : "حذف", 27 | "Set up a security key as a second factor." : "Set up a security key as a second factor.", 28 | "Your security key was added successfully. You are now being redirected to the login page." : "Your security key was added successfully. You are now being redirected to the login page.", 29 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "No security keys configured. You are not using WebAuthn as second factor at the moment.", 30 | "The following security keys are configured for WebAuthn two-factor authentication:" : "The following security keys are configured for WebAuthn two-factor authentication:", 31 | "All security keys are deactivated." : "All security keys are deactivated." 32 | },"pluralForm" :"nplurals=2; plural=(n > 1);" 33 | } -------------------------------------------------------------------------------- /.github/workflows/psalm-matrix.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | # 6 | # SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors 7 | # SPDX-License-Identifier: MIT 8 | 9 | name: Static analysis 10 | 11 | on: pull_request 12 | 13 | concurrency: 14 | group: psalm-${{ github.head_ref || github.run_id }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | matrix: 19 | runs-on: ubuntu-latest-low 20 | outputs: 21 | ocp-matrix: ${{ steps.versions.outputs.ocp-matrix }} 22 | php-min: ${{ steps.versions.outputs.php-min }} 23 | steps: 24 | - name: Checkout app 25 | uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 26 | - name: Get version matrix 27 | id: versions 28 | uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 29 | 30 | static-analysis: 31 | runs-on: ubuntu-latest 32 | needs: matrix 33 | strategy: 34 | # do not stop on another job's failure 35 | fail-fast: false 36 | matrix: ${{ fromJson(needs.matrix.outputs.ocp-matrix) }} 37 | 38 | name: static-psalm-analysis ${{ matrix.ocp-version }} 39 | steps: 40 | - name: Checkout 41 | uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 42 | 43 | - name: Set up php${{ needs.matrix.outputs.php-min }} 44 | uses: shivammathur/setup-php@cf4cade2721270509d5b1c766ab3549210a39a2a # 2.33.0 45 | with: 46 | php-version: ${{ needs.matrix.outputs.php-min }} 47 | extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite 48 | coverage: none 49 | ini-file: development 50 | env: 51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 52 | 53 | - name: Install dependencies 54 | run: composer i 55 | 56 | - name: Install dependencies 57 | run: composer require --dev 'nextcloud/ocp:${{ matrix.ocp-version }}' --ignore-platform-reqs --with-all-dependencies 58 | 59 | - name: Run coding standards check 60 | run: composer run psalm 61 | 62 | summary: 63 | runs-on: ubuntu-latest-low 64 | needs: static-analysis 65 | 66 | if: always() 67 | 68 | name: static-psalm-analysis-summary 69 | 70 | steps: 71 | - name: Summary status 72 | run: if ${{ needs.static-analysis.result != 'success' }}; then exit 1; fi 73 | -------------------------------------------------------------------------------- /l10n/en_GB.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "You added an WebAuthn hardware token" : "You added an WebAuthn hardware token", 5 | "You removed an WebAuthn hardware token" : "You removed an WebAuthn hardware token", 6 | "WebAuthn disabled by the administration" : "WebAuthn disabled by the administration", 7 | "Security key" : "Security key", 8 | "Use WebAuthn for second factor authentication" : "Use WebAuthn for second factor authentication", 9 | "Two-Factor WebAuthn" : "Two-Factor WebAuthn", 10 | "WebAuthn two-factor provider" : "WebAuthn two-factor provider", 11 | "A two-factor provider for WebAuthn devices" : "A two-factor provider for WebAuthn devices", 12 | "Server error while trying to complete security key registration" : "Server error while trying to complete security key registration", 13 | "Add security key" : "Add security key", 14 | "Please use your security key to authorize." : "Please use your security key to authorise.", 15 | "Name your security key" : "Name your security key", 16 | "Add" : "Add", 17 | "Adding your security key …" : "Adding your security key …", 18 | "Authentication cancelled" : "Authentication cancelled", 19 | "An error occurred: {msg}" : "An error occurred: {msg}", 20 | "Retry" : "Retry", 21 | "Use security key" : "Use security key", 22 | "An error occurred. Please try again." : "An error occurred. Please try again.", 23 | "Your browser does not support WebAuthn." : "Your browser does not support WebAuthn.", 24 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication.", 25 | "Unnamed key" : "Unnamed key", 26 | "Registered" : "Registered", 27 | "Active" : "Active", 28 | "Remove" : "Remove", 29 | "Set up a security key as a second factor." : "Set up a security key as a second factor.", 30 | "Your security key was added successfully. You are now being redirected to the login page." : "Your security key was added successfully. You are now being redirected to the login page.", 31 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "No security keys configured. You are not using WebAuthn as second factor at the moment.", 32 | "The following security keys are configured for WebAuthn two-factor authentication:" : "The following security keys are configured for WebAuthn two-factor authentication:", 33 | "All security keys are deactivated." : "All security keys are deactivated." 34 | }, 35 | "nplurals=2; plural=(n != 1);"); 36 | -------------------------------------------------------------------------------- /l10n/fa.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "You added an WebAuthn hardware token" : "You added an WebAuthn hardware token", 5 | "You removed an WebAuthn hardware token" : "You removed an WebAuthn hardware token", 6 | "WebAuthn disabled by the administration" : "WebAuthn disabled by the administration", 7 | "Security key" : "Security key", 8 | "Use WebAuthn for second factor authentication" : "Use WebAuthn for second factor authentication", 9 | "Two-Factor WebAuthn" : "Two-Factor WebAuthn", 10 | "WebAuthn two-factor provider" : "WebAuthn two-factor provider", 11 | "A two-factor provider for WebAuthn devices" : "A two-factor provider for WebAuthn devices", 12 | "Server error while trying to complete security key registration" : "Server error while trying to complete security key registration", 13 | "Add security key" : "Add security key", 14 | "Please use your security key to authorize." : "Please use your security key to authorize.", 15 | "Name your security key" : "Name your security key", 16 | "Add" : "افزودن", 17 | "Adding your security key …" : "Adding your security key …", 18 | "Authentication cancelled" : "Authentication cancelled", 19 | "An error occurred: {msg}" : "An error occurred: {msg}", 20 | "Retry" : "تلاش دوباره", 21 | "Use security key" : "Use security key", 22 | "An error occurred. Please try again." : "An error occurred. Please try again.", 23 | "Your browser does not support WebAuthn." : "Your browser does not support WebAuthn.", 24 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication.", 25 | "Unnamed key" : "Unnamed key", 26 | "Registered" : "Registered", 27 | "Active" : "فعال کردن", 28 | "Remove" : "حذف", 29 | "Set up a security key as a second factor." : "Set up a security key as a second factor.", 30 | "Your security key was added successfully. You are now being redirected to the login page." : "Your security key was added successfully. You are now being redirected to the login page.", 31 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "No security keys configured. You are not using WebAuthn as second factor at the moment.", 32 | "The following security keys are configured for WebAuthn two-factor authentication:" : "The following security keys are configured for WebAuthn two-factor authentication:", 33 | "All security keys are deactivated." : "All security keys are deactivated." 34 | }, 35 | "nplurals=2; plural=(n > 1);"); 36 | -------------------------------------------------------------------------------- /l10n/nb.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "You added an WebAuthn hardware token" : "Du har lagt til et WebAuthn-maskinvaretoken", 3 | "You removed an WebAuthn hardware token" : "Du fjernet et WebAuthn-maskinvaretoken", 4 | "WebAuthn disabled by the administration" : "WebAuthn deaktivert av administrasjonen", 5 | "Security key" : "Sikkerhetsnøkkel", 6 | "Use WebAuthn for second factor authentication" : "Bruk WebAuthn for tofaktorautentisering", 7 | "Two-Factor WebAuthn" : "Tofaktor WebAuthn", 8 | "WebAuthn two-factor provider" : "WebAuthn tofaktorleverandør", 9 | "A two-factor provider for WebAuthn devices" : "En tofaktorleverandør for WebAuthn-enheter", 10 | "Server error while trying to complete security key registration" : "Serverfeil under forsøk på å fullføre registrering av sikkerhetsnøkkel", 11 | "Add security key" : "Legg til sikkerhetsnøkkel", 12 | "Please use your security key to authorize." : "Vennligst bruk sikkerhetsnøkkelen din for å autentisere.", 13 | "Name your security key" : "Navngi sikkerhetsnøkkelen din", 14 | "Add" : "Legg til", 15 | "Adding your security key …" : "Legger til sikkerhetsnøkkelen din...", 16 | "Authentication cancelled" : "Autentisering avbrutt", 17 | "An error occurred: {msg}" : "En feil oppsto: {msg}", 18 | "Retry" : "Prøv igjen", 19 | "Use security key" : "Bruk sikkerhetsnøkkel", 20 | "An error occurred. Please try again." : "Det oppsto en feil. Prøv igjen.", 21 | "Your browser does not support WebAuthn." : "Nettleseren din støtter ikke WebAuthn.", 22 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "Du får tilgang til dette nettstedet via en usikker tilkobling. Nettlesere kan derfor nekte WebAuthn-autentisering.", 23 | "Unnamed key" : "Ikke navngitt nøkkel", 24 | "Registered" : "Registrert", 25 | "Active" : "Aktiv", 26 | "Remove" : "Fjern", 27 | "Set up a security key as a second factor." : "Sett opp en sikkerhetsnøkkel som en tofaktor.", 28 | "Your security key was added successfully. You are now being redirected to the login page." : "Sikkerhetsnøkkelen din ble lagt til. Du blir nå omdirigert til påloggingssiden.", 29 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "Ingen sikkerhetsnøkler konfigurert. Du bruker for øyeblikket ikke WebAuthn som tofaktor.", 30 | "The following security keys are configured for WebAuthn two-factor authentication:" : "Følgende sikkerhetsnøkler er konfigurert for WebAuthns tofaktorautentisering:", 31 | "All security keys are deactivated." : "Alle sikkerhetsnøkler er deaktivert." 32 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 33 | } -------------------------------------------------------------------------------- /tests/Unit/Activity/ProviderTest.php: -------------------------------------------------------------------------------- 1 | l10n = $this->createMock(IFactory::class); 32 | $this->urlGenerator = $this->createMock(IURLGenerator::class); 33 | 34 | $this->provider = new Provider($this->l10n, $this->urlGenerator); 35 | } 36 | 37 | public function testParseUnrelated(): void { 38 | $lang = 'ru'; 39 | $event = $this->createMock(IEvent::class); 40 | $event->expects(self::once()) 41 | ->method('getApp') 42 | ->willReturn('comments'); 43 | $this->expectException(InvalidArgumentException::class); 44 | 45 | $this->provider->parse($lang, $event); 46 | } 47 | 48 | public function subjectData(): array { 49 | return [ 50 | ['webauthn_device_added'], 51 | ['webauthn_device_removed'], 52 | ['webauthn_disabled_by_admin'], 53 | ]; 54 | } 55 | 56 | /** 57 | * @dataProvider subjectData 58 | */ 59 | public function testParse($subject): void { 60 | $lang = 'ru'; 61 | $event = $this->createMock(IEvent::class); 62 | $l = $this->createMock(IL10N::class); 63 | 64 | $event->expects(self::once()) 65 | ->method('getApp') 66 | ->willReturn('twofactor_webauthn'); 67 | $this->l10n->expects(self::once()) 68 | ->method('get') 69 | ->with('twofactor_webauthn', $lang) 70 | ->willReturn($l); 71 | $this->urlGenerator->expects(self::once()) 72 | ->method('imagePath') 73 | ->with('core', 'actions/password.svg') 74 | ->willReturn('path/to/image'); 75 | $this->urlGenerator->expects(self::once()) 76 | ->method('getAbsoluteURL') 77 | ->with('path/to/image') 78 | ->willReturn('absolute/path/to/image'); 79 | $event->expects(self::once()) 80 | ->method('setIcon') 81 | ->with('absolute/path/to/image'); 82 | $event->expects(self::once()) 83 | ->method('getSubject') 84 | ->willReturn($subject); 85 | $event->expects(self::once()) 86 | ->method('setSubject'); 87 | 88 | $this->provider->parse($lang, $event); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /l10n/et_EE.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "You added an WebAuthn hardware token" : "Sa lisasid WebAuthni raudvaralise pääsmiku", 3 | "You removed an WebAuthn hardware token" : "Sa eemaldasid WebAuthni raudvaralise pääsmiku", 4 | "WebAuthn disabled by the administration" : "WebAuthn on peakasutaja poolt välja lülitatud", 5 | "Security key" : "Turvavõti", 6 | "Use WebAuthn for second factor authentication" : "Kasuta WebAuthni kaheastmeline autentimise jaoks", 7 | "Two-Factor WebAuthn" : " Kaheastmeline autentimine WebAuthni abil", 8 | "WebAuthn two-factor provider" : "WebAuthni kaheastmelise autentimise teenusepakkuja", 9 | "A two-factor provider for WebAuthn devices" : "Kaheastmelise autentimise teenusepakkuja WebAuthni sedamete jaoks", 10 | "Server error while trying to complete security key registration" : "Serveriviga turvavõtme registreerimise lõpetamisel", 11 | "Add security key" : "Lisa turvavõti", 12 | "Please use your security key to authorize." : "Palun kasuta tuvastamiseks turvavõtit.", 13 | "Name your security key" : "Turvavõtme nimi", 14 | "Add" : "Lisa", 15 | "Adding your security key …" : "Turvavõti on lisamisel…", 16 | "Authentication cancelled" : "Autentimine on katkestatud", 17 | "An error occurred: {msg}" : "Tekkis viga: {msg}", 18 | "Retry" : "Proovi uuesti", 19 | "Use security key" : "Kasuta turvavõtit", 20 | "An error occurred. Please try again." : "Tekkis viga. Palun proovi uuesti", 21 | "Your browser does not support WebAuthn." : "Sinu veebibrauseril puudub WebAuthni tugi.", 22 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "Sa kasutad seda veebisaiti ebaturvalise ühenduse abil. Veebibrauserid võivad keelduda WebAuthni abil tuvastamisest.", 23 | "Unnamed key" : "Ilma nimeta turvavõti", 24 | "Registered" : "Registreeritud", 25 | "Active" : "Aktiivne", 26 | "Remove" : "Eemalda", 27 | "Set up a security key as a second factor." : "Seadista turvavõti autentimise teise sammuna.", 28 | "Your security key was added successfully. You are now being redirected to the login page." : "Sinu turvavõtme lisamine õnnestus. Suunan sind edasi sisselogimise lehele.", 29 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "Ühtegi turvavõtit pole registreeritud. Sa hetkel ei kasuta WebAuthni teise sammuna.", 30 | "The following security keys are configured for WebAuthn two-factor authentication:" : "Järgnevad turvavõtmed on sedaistatud WebAuthni kaheastmeline autentimise jaoks:", 31 | "All security keys are deactivated." : "Kõik turvavõtmed on kautuselt eemaldatud" 32 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 33 | } -------------------------------------------------------------------------------- /l10n/sv.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "You added an WebAuthn hardware token" : "Du har lagt till en WebAuthn enhets-token", 3 | "You removed an WebAuthn hardware token" : "Du tog bort en WebAuthn enhets-token", 4 | "WebAuthn disabled by the administration" : "WebAuthn inaktiverad av administratören", 5 | "Security key" : "Säkerhetsnyckel", 6 | "Use WebAuthn for second factor authentication" : "Använd WebAuthn för andra faktor autentisering", 7 | "Two-Factor WebAuthn" : "Tvåfaktors WebAuthn", 8 | "WebAuthn two-factor provider" : "WebAuthn tvåfaktor-leverantör", 9 | "A two-factor provider for WebAuthn devices" : "En tvåfaktor-leverantör för WebAuthn-enheter", 10 | "Server error while trying to complete security key registration" : "Serverfel vid försök att slutföra registreringen av säkerhetsnyckel", 11 | "Add security key" : "Lägg till säkerhetsnyckel", 12 | "Please use your security key to authorize." : "Använd din säkerhetsnyckel för att auktorisera.", 13 | "Name your security key" : "Namnge din säkerhetsnyckel", 14 | "Add" : "Lägg till", 15 | "Adding your security key …" : "Lägger till din säkerhetsnyckel ...", 16 | "Authentication cancelled" : "Autentiseringen avbröts", 17 | "An error occurred: {msg}" : "Ett fel uppstod: {msg}", 18 | "Retry" : "Försök igen", 19 | "Use security key" : "Använd säkerhetsnyckel", 20 | "An error occurred. Please try again." : "Ett fel har inträffat. Vänligen försök igen.", 21 | "Your browser does not support WebAuthn." : "Din webbläsare stödjer inte WebAuthn.", 22 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "Din åtkomst till den här webbplatsen sker via en osäker anslutning. Webbläsaren kan därför vägra WebAuthn-autentisering.", 23 | "Unnamed key" : "Namnlös nyckel", 24 | "Registered" : "Registrerad", 25 | "Active" : "Aktiv", 26 | "Remove" : "Ta bort", 27 | "Set up a security key as a second factor." : "Ställ in en säkerhetsnyckel som andra faktor.", 28 | "Your security key was added successfully. You are now being redirected to the login page." : "Din säkerhetsnyckel har lagts till. Du omdirigeras nu till inloggningssidan.", 29 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "Inga säkerhetsnycklar är konfigurerade. Du använder inte WebAuthn som tvåfaktorautentisering just nu.", 30 | "The following security keys are configured for WebAuthn two-factor authentication:" : "Följande enheter är konfigurerade för WebAuthn tvåfaktorautentisering:", 31 | "All security keys are deactivated." : "Alla säkerhetsnycklar är avaktiverade." 32 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 33 | } -------------------------------------------------------------------------------- /l10n/nb.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "You added an WebAuthn hardware token" : "Du har lagt til et WebAuthn-maskinvaretoken", 5 | "You removed an WebAuthn hardware token" : "Du fjernet et WebAuthn-maskinvaretoken", 6 | "WebAuthn disabled by the administration" : "WebAuthn deaktivert av administrasjonen", 7 | "Security key" : "Sikkerhetsnøkkel", 8 | "Use WebAuthn for second factor authentication" : "Bruk WebAuthn for tofaktorautentisering", 9 | "Two-Factor WebAuthn" : "Tofaktor WebAuthn", 10 | "WebAuthn two-factor provider" : "WebAuthn tofaktorleverandør", 11 | "A two-factor provider for WebAuthn devices" : "En tofaktorleverandør for WebAuthn-enheter", 12 | "Server error while trying to complete security key registration" : "Serverfeil under forsøk på å fullføre registrering av sikkerhetsnøkkel", 13 | "Add security key" : "Legg til sikkerhetsnøkkel", 14 | "Please use your security key to authorize." : "Vennligst bruk sikkerhetsnøkkelen din for å autentisere.", 15 | "Name your security key" : "Navngi sikkerhetsnøkkelen din", 16 | "Add" : "Legg til", 17 | "Adding your security key …" : "Legger til sikkerhetsnøkkelen din...", 18 | "Authentication cancelled" : "Autentisering avbrutt", 19 | "An error occurred: {msg}" : "En feil oppsto: {msg}", 20 | "Retry" : "Prøv igjen", 21 | "Use security key" : "Bruk sikkerhetsnøkkel", 22 | "An error occurred. Please try again." : "Det oppsto en feil. Prøv igjen.", 23 | "Your browser does not support WebAuthn." : "Nettleseren din støtter ikke WebAuthn.", 24 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "Du får tilgang til dette nettstedet via en usikker tilkobling. Nettlesere kan derfor nekte WebAuthn-autentisering.", 25 | "Unnamed key" : "Ikke navngitt nøkkel", 26 | "Registered" : "Registrert", 27 | "Active" : "Aktiv", 28 | "Remove" : "Fjern", 29 | "Set up a security key as a second factor." : "Sett opp en sikkerhetsnøkkel som en tofaktor.", 30 | "Your security key was added successfully. You are now being redirected to the login page." : "Sikkerhetsnøkkelen din ble lagt til. Du blir nå omdirigert til påloggingssiden.", 31 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "Ingen sikkerhetsnøkler konfigurert. Du bruker for øyeblikket ikke WebAuthn som tofaktor.", 32 | "The following security keys are configured for WebAuthn two-factor authentication:" : "Følgende sikkerhetsnøkler er konfigurert for WebAuthns tofaktorautentisering:", 33 | "All security keys are deactivated." : "Alle sikkerhetsnøkler er deaktivert." 34 | }, 35 | "nplurals=2; plural=(n != 1);"); 36 | -------------------------------------------------------------------------------- /l10n/sv.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "You added an WebAuthn hardware token" : "Du har lagt till en WebAuthn enhets-token", 5 | "You removed an WebAuthn hardware token" : "Du tog bort en WebAuthn enhets-token", 6 | "WebAuthn disabled by the administration" : "WebAuthn inaktiverad av administratören", 7 | "Security key" : "Säkerhetsnyckel", 8 | "Use WebAuthn for second factor authentication" : "Använd WebAuthn för andra faktor autentisering", 9 | "Two-Factor WebAuthn" : "Tvåfaktors WebAuthn", 10 | "WebAuthn two-factor provider" : "WebAuthn tvåfaktor-leverantör", 11 | "A two-factor provider for WebAuthn devices" : "En tvåfaktor-leverantör för WebAuthn-enheter", 12 | "Server error while trying to complete security key registration" : "Serverfel vid försök att slutföra registreringen av säkerhetsnyckel", 13 | "Add security key" : "Lägg till säkerhetsnyckel", 14 | "Please use your security key to authorize." : "Använd din säkerhetsnyckel för att auktorisera.", 15 | "Name your security key" : "Namnge din säkerhetsnyckel", 16 | "Add" : "Lägg till", 17 | "Adding your security key …" : "Lägger till din säkerhetsnyckel ...", 18 | "Authentication cancelled" : "Autentiseringen avbröts", 19 | "An error occurred: {msg}" : "Ett fel uppstod: {msg}", 20 | "Retry" : "Försök igen", 21 | "Use security key" : "Använd säkerhetsnyckel", 22 | "An error occurred. Please try again." : "Ett fel har inträffat. Vänligen försök igen.", 23 | "Your browser does not support WebAuthn." : "Din webbläsare stödjer inte WebAuthn.", 24 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "Din åtkomst till den här webbplatsen sker via en osäker anslutning. Webbläsaren kan därför vägra WebAuthn-autentisering.", 25 | "Unnamed key" : "Namnlös nyckel", 26 | "Registered" : "Registrerad", 27 | "Active" : "Aktiv", 28 | "Remove" : "Ta bort", 29 | "Set up a security key as a second factor." : "Ställ in en säkerhetsnyckel som andra faktor.", 30 | "Your security key was added successfully. You are now being redirected to the login page." : "Din säkerhetsnyckel har lagts till. Du omdirigeras nu till inloggningssidan.", 31 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "Inga säkerhetsnycklar är konfigurerade. Du använder inte WebAuthn som tvåfaktorautentisering just nu.", 32 | "The following security keys are configured for WebAuthn two-factor authentication:" : "Följande enheter är konfigurerade för WebAuthn tvåfaktorautentisering:", 33 | "All security keys are deactivated." : "Alla säkerhetsnycklar är avaktiverade." 34 | }, 35 | "nplurals=2; plural=(n != 1);"); 36 | -------------------------------------------------------------------------------- /l10n/et_EE.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "twofactor_webauthn", 3 | { 4 | "You added an WebAuthn hardware token" : "Sa lisasid WebAuthni raudvaralise pääsmiku", 5 | "You removed an WebAuthn hardware token" : "Sa eemaldasid WebAuthni raudvaralise pääsmiku", 6 | "WebAuthn disabled by the administration" : "WebAuthn on peakasutaja poolt välja lülitatud", 7 | "Security key" : "Turvavõti", 8 | "Use WebAuthn for second factor authentication" : "Kasuta WebAuthni kaheastmeline autentimise jaoks", 9 | "Two-Factor WebAuthn" : " Kaheastmeline autentimine WebAuthni abil", 10 | "WebAuthn two-factor provider" : "WebAuthni kaheastmelise autentimise teenusepakkuja", 11 | "A two-factor provider for WebAuthn devices" : "Kaheastmelise autentimise teenusepakkuja WebAuthni sedamete jaoks", 12 | "Server error while trying to complete security key registration" : "Serveriviga turvavõtme registreerimise lõpetamisel", 13 | "Add security key" : "Lisa turvavõti", 14 | "Please use your security key to authorize." : "Palun kasuta tuvastamiseks turvavõtit.", 15 | "Name your security key" : "Turvavõtme nimi", 16 | "Add" : "Lisa", 17 | "Adding your security key …" : "Turvavõti on lisamisel…", 18 | "Authentication cancelled" : "Autentimine on katkestatud", 19 | "An error occurred: {msg}" : "Tekkis viga: {msg}", 20 | "Retry" : "Proovi uuesti", 21 | "Use security key" : "Kasuta turvavõtit", 22 | "An error occurred. Please try again." : "Tekkis viga. Palun proovi uuesti", 23 | "Your browser does not support WebAuthn." : "Sinu veebibrauseril puudub WebAuthni tugi.", 24 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "Sa kasutad seda veebisaiti ebaturvalise ühenduse abil. Veebibrauserid võivad keelduda WebAuthni abil tuvastamisest.", 25 | "Unnamed key" : "Ilma nimeta turvavõti", 26 | "Registered" : "Registreeritud", 27 | "Active" : "Aktiivne", 28 | "Remove" : "Eemalda", 29 | "Set up a security key as a second factor." : "Seadista turvavõti autentimise teise sammuna.", 30 | "Your security key was added successfully. You are now being redirected to the login page." : "Sinu turvavõtme lisamine õnnestus. Suunan sind edasi sisselogimise lehele.", 31 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "Ühtegi turvavõtit pole registreeritud. Sa hetkel ei kasuta WebAuthni teise sammuna.", 32 | "The following security keys are configured for WebAuthn two-factor authentication:" : "Järgnevad turvavõtmed on sedaistatud WebAuthni kaheastmeline autentimise jaoks:", 33 | "All security keys are deactivated." : "Kõik turvavõtmed on kautuselt eemaldatud" 34 | }, 35 | "nplurals=2; plural=(n != 1);"); 36 | -------------------------------------------------------------------------------- /l10n/hu.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "You added an WebAuthn hardware token" : "Hozzáadott egy WebAuthn hardvertokent", 3 | "You removed an WebAuthn hardware token" : "Eltávolított egy WebAuthn hardvertokent", 4 | "WebAuthn disabled by the administration" : "A WebAuthnt letiltotta a rendszergazda", 5 | "Security key" : "Biztonsági kulcs", 6 | "Use WebAuthn for second factor authentication" : "WebAuthn használata a hitelesítés második lépéseként", 7 | "Two-Factor WebAuthn" : "Kétlépcsős WebAuthn", 8 | "WebAuthn two-factor provider" : "WebAuthn kétlépcsős szolgáltató", 9 | "A two-factor provider for WebAuthn devices" : "Kétlépcsős szolgáltató a WebAuthn-eszközökhöz", 10 | "Server error while trying to complete security key registration" : "Kiszolgálóhiba a biztonsági kulcs regisztrációjának befejezése során", 11 | "Add security key" : "Biztonsági kulcs hozzáadása", 12 | "Please use your security key to authorize." : "Használja a biztonsági kulcsát a jogosultság-ellenőrzéshez.", 13 | "Name your security key" : "Nevezze el a biztonsági kulcsot", 14 | "Add" : "Hozzáadás", 15 | "Adding your security key …" : "Biztonsági kulcs hozzáadása…", 16 | "Authentication cancelled" : "Hitelesítés megszakítva", 17 | "An error occurred: {msg}" : "Hiba történt: {msg}", 18 | "Retry" : "Újra", 19 | "Use security key" : "Biztonsági kulcs használata", 20 | "An error occurred. Please try again." : "Hiba történt. Próbálja újra.", 21 | "Your browser does not support WebAuthn." : "A böngészője nem támogatja a WebAuthn szabványt.", 22 | "You are accessing this site via an insecure connection. Browsers might therefore refuse the WebAuthn authentication." : "Nem biztonságos kapcsolaton keresztül éri el ezt az oldalt. A böngészők ezért megtagadhatják a WebAuthn-hitelesítést.", 23 | "Unnamed key" : "Névtelen kulcs", 24 | "Registered" : "Regisztrált", 25 | "Active" : "Aktív", 26 | "Remove" : "Eltávolítás", 27 | "Set up a security key as a second factor." : "Biztonsági kulcs beállítása második lépcsőként.", 28 | "Your security key was added successfully. You are now being redirected to the login page." : "A biztonsági kulcs sikeresen hozzáadva. Most át lesz irányítva a bejelentkezési oldalra.", 29 | "No security keys configured. You are not using WebAuthn as second factor at the moment." : "Nincsenek biztonsági kulcsok beállítva. Jelenleg nem használ WebAuthnt második lépésként.", 30 | "The following security keys are configured for WebAuthn two-factor authentication:" : "A következő biztonsági kulcsok lettek beállítva a WebAuthn kétlépcsős hitelesítéshez:", 31 | "All security keys are deactivated." : "Az összes biztonsági kulcsot deaktiválták." 32 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 33 | } --------------------------------------------------------------------------------