From 9efb54adf440d962f67e3fdf19279de7eda643e3 Mon Sep 17 00:00:00 2001 From: Bubka <858858+Bubka@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:29:26 +0100 Subject: [PATCH] Enable the Vue 3 front-end --- resources/{js_vue3 => js}/App.vue | 0 resources/js/api.js | 58 -- resources/js/app.js | 150 ++-- resources/{js_vue3 => js}/assets/app.scss | 0 .../components/ActionButtons.vue | 0 resources/js/components/App.vue | 41 - resources/js/components/Button.vue | 42 - .../components/DestinationGroupSelector.vue | 0 resources/js/components/Dots.vue | 92 +- resources/js/components/FieldError.vue | 23 - resources/js/components/Footer.vue | 58 -- resources/js/components/Form.js | 317 ------- resources/js/components/FormButtons.vue | 64 -- resources/js/components/FormCheckbox.vue | 57 -- resources/js/components/FormField.vue | 81 -- resources/js/components/FormPasswordField.vue | 132 --- resources/js/components/FormSelect.vue | 54 -- resources/js/components/FormSwitch.vue | 43 - resources/js/components/FormToggle.vue | 95 -- resources/js/components/FormWrapper.vue | 31 - .../components/GroupSwitch.vue | 0 resources/js/components/Kicker.vue | 114 +-- resources/js/components/Modal.vue | 63 -- .../{js_vue3 => js}/components/OtpDisplay.vue | 0 resources/js/components/OtpDisplayer.vue | 294 ------- .../components/QrContentDisplay.vue | 0 .../js/components/ResponsiveWidthWrapper.vue | 19 - .../{js_vue3 => js}/components/SearchBox.vue | 0 resources/js/components/SettingTabs.vue | 55 -- resources/js/components/Spinner.vue | 63 +- .../{js_vue3 => js}/components/Toolbar.vue | 0 resources/js/components/TotpLooper.vue | 242 +++-- resources/js/components/Twofaccount.vue | 73 -- resources/js/components/VersionChecker.vue | 62 +- .../components/formElements/Button.vue | 0 .../formElements/ButtonBackCloseCancel.vue | 0 .../components/formElements/FieldError.vue | 0 .../components/formElements/Form.js | 0 .../components/formElements/FormButtons.vue | 0 .../components/formElements/FormCheckbox.vue | 0 .../{ => formElements}/FormErrors.js | 0 .../components/formElements/FormField.vue | 0 .../components/formElements/FormLockField.vue | 0 .../formElements/FormPasswordField.vue | 0 .../components/formElements/FormSelect.vue | 0 .../components/formElements/FormToggle.vue | 0 resources/js/components/index.js | 37 - .../{js_vue3 => js}/composables/helpers.js | 0 resources/{js_vue3 => js}/helpers.js | 0 resources/{js_vue3 => js}/icons.js | 0 resources/js/langs/i18n.js | 14 - resources/{js_vue3 => js}/layouts/Footer.vue | 0 .../{js_vue3 => js}/layouts/FormWrapper.vue | 0 resources/{js_vue3 => js}/layouts/Modal.vue | 0 .../layouts/ResponsiveWidthWrapper.vue | 0 .../{js_vue3 => js}/layouts/SettingTabs.vue | 0 resources/js/mixins.js | 140 --- resources/js/packages/clipboard.js | 4 - resources/js/packages/fontawesome.js | 95 -- resources/js/packages/vue-storage.js | 10 - resources/{js_vue3 => js}/router/index.js | 0 .../router/middlewarePipeline.js | 0 .../router/middlewares/authGuard.js | 0 .../router/middlewares/noEmptyError.js | 0 .../router/middlewares/noRegistration.js | 0 .../router/middlewares/setReturnTo.js | 0 .../router/middlewares/starter.js | 0 resources/js/routes.js | 127 --- .../services/appSettingService.js | 0 .../{js_vue3 => js}/services/authService.js | 0 .../{js_vue3 => js}/services/groupService.js | 0 .../services/httpClientFactory.js | 0 .../{js_vue3 => js}/services/systemService.js | 0 .../services/twofaccountService.js | 0 .../{js_vue3 => js}/services/userService.js | 0 .../webauthn/identifyAuthenticationError.js | 0 .../webauthn/identifyRegistrationError.js | 0 .../{ => services}/webauthn/isValidDomain.js | 0 .../webauthn/webauthnAbortService.js | 0 .../services/webauthn/webauthnService.js | 0 .../{js_vue3 => js}/stores/appSettings.js | 0 resources/{js_vue3 => js}/stores/bus.js | 0 resources/{js_vue3 => js}/stores/groups.js | 0 resources/{js_vue3 => js}/stores/notify.js | 0 .../{js_vue3 => js}/stores/twofaccounts.js | 0 resources/{js_vue3 => js}/stores/user.js | 0 resources/js/views/About.vue | 202 ++--- resources/js/views/Accounts.vue | 830 ------------------ resources/js/views/Capture.vue | 127 --- resources/js/views/Error.vue | 137 +-- resources/js/views/Groups.vue | 127 --- resources/js/views/Start.vue | 190 ++-- resources/js/views/auth/Autolock.vue | 27 - resources/js/views/auth/Login.vue | 337 ++++--- resources/js/views/auth/Register.vue | 233 ++--- .../views/auth/RequestReset.vue | 0 resources/js/views/auth/password/Request.vue | 60 -- resources/js/views/auth/password/Reset.vue | 120 ++- resources/js/views/auth/webauthn/Lost.vue | 55 -- resources/js/views/auth/webauthn/Recover.vue | 130 ++- resources/js/views/groups/Create.vue | 44 - .../views/groups/CreateUpdate.vue | 0 resources/js/views/groups/Edit.vue | 46 - .../{js_vue3 => js}/views/groups/Groups.vue | 0 resources/js/views/settings/Account.vue | 261 +++--- .../js/views/settings/Credentials/Edit.vue | 78 +- resources/js/views/settings/OAuth.vue | 311 ++++--- resources/js/views/settings/Options.vue | 386 ++++---- .../js/views/settings/PATokens/Create.vue | 43 - resources/js/views/settings/Settings.vue | 43 - resources/js/views/settings/WebAuthn.vue | 336 +++---- .../views/twofaccounts/Accounts.vue | 0 .../views/twofaccounts/Capture.vue | 0 resources/js/views/twofaccounts/Create.vue | 436 --------- .../views/twofaccounts/CreateUpdate.vue | 0 resources/js/views/twofaccounts/Edit.vue | 301 ------- resources/js/views/twofaccounts/Import.vue | 685 ++++++++------- resources/js/views/twofaccounts/QRcode.vue | 67 +- resources/js/webauthn/webauthnService.js | 161 ---- resources/js_vue3/app.js | 95 -- resources/js_vue3/components/Dots.vue | 54 -- resources/js_vue3/components/Kicker.vue | 70 -- resources/js_vue3/components/Spinner.vue | 64 -- resources/js_vue3/components/TotpLooper.vue | 122 --- .../js_vue3/components/VersionChecker.vue | 44 - .../components/formElements/FormErrors.js | 141 --- .../webauthn/identifyAuthenticationError.js | 95 -- .../webauthn/identifyRegistrationError.js | 160 ---- .../services/webauthn/isValidDomain.js | 15 - .../services/webauthn/webauthnAbortService.js | 51 -- resources/js_vue3/views/About.vue | 138 --- resources/js_vue3/views/Error.vue | 51 -- resources/js_vue3/views/Start.vue | 117 --- resources/js_vue3/views/auth/Login.vue | 156 ---- resources/js_vue3/views/auth/Register.vue | 115 --- .../js_vue3/views/auth/password/Reset.vue | 62 -- .../js_vue3/views/auth/webauthn/Recover.vue | 71 -- resources/js_vue3/views/settings/Account.vue | 145 --- .../views/settings/Credentials/Edit.vue | 41 - resources/js_vue3/views/settings/OAuth.vue | 210 ----- resources/js_vue3/views/settings/Options.vue | 199 ----- resources/js_vue3/views/settings/WebAuthn.vue | 174 ---- .../js_vue3/views/twofaccounts/Import.vue | 433 --------- .../js_vue3/views/twofaccounts/QRcode.vue | 35 - resources/views/landing_v3.blade.php | 2 +- vite.config.js | 4 +- 146 files changed, 2000 insertions(+), 9387 deletions(-) rename resources/{js_vue3 => js}/App.vue (100%) delete mode 100644 resources/js/api.js rename resources/{js_vue3 => js}/assets/app.scss (100%) rename resources/{js_vue3 => js}/components/ActionButtons.vue (100%) delete mode 100644 resources/js/components/App.vue delete mode 100644 resources/js/components/Button.vue rename resources/{js_vue3 => js}/components/DestinationGroupSelector.vue (100%) delete mode 100644 resources/js/components/FieldError.vue delete mode 100644 resources/js/components/Footer.vue delete mode 100644 resources/js/components/Form.js delete mode 100644 resources/js/components/FormButtons.vue delete mode 100644 resources/js/components/FormCheckbox.vue delete mode 100644 resources/js/components/FormField.vue delete mode 100644 resources/js/components/FormPasswordField.vue delete mode 100644 resources/js/components/FormSelect.vue delete mode 100644 resources/js/components/FormSwitch.vue delete mode 100644 resources/js/components/FormToggle.vue delete mode 100644 resources/js/components/FormWrapper.vue rename resources/{js_vue3 => js}/components/GroupSwitch.vue (100%) delete mode 100644 resources/js/components/Modal.vue rename resources/{js_vue3 => js}/components/OtpDisplay.vue (100%) delete mode 100644 resources/js/components/OtpDisplayer.vue rename resources/{js_vue3 => js}/components/QrContentDisplay.vue (100%) delete mode 100644 resources/js/components/ResponsiveWidthWrapper.vue rename resources/{js_vue3 => js}/components/SearchBox.vue (100%) delete mode 100644 resources/js/components/SettingTabs.vue rename resources/{js_vue3 => js}/components/Toolbar.vue (100%) delete mode 100644 resources/js/components/Twofaccount.vue rename resources/{js_vue3 => js}/components/formElements/Button.vue (100%) rename resources/{js_vue3 => js}/components/formElements/ButtonBackCloseCancel.vue (100%) rename resources/{js_vue3 => js}/components/formElements/FieldError.vue (100%) rename resources/{js_vue3 => js}/components/formElements/Form.js (100%) rename resources/{js_vue3 => js}/components/formElements/FormButtons.vue (100%) rename resources/{js_vue3 => js}/components/formElements/FormCheckbox.vue (100%) rename resources/js/components/{ => formElements}/FormErrors.js (100%) rename resources/{js_vue3 => js}/components/formElements/FormField.vue (100%) rename resources/{js_vue3 => js}/components/formElements/FormLockField.vue (100%) rename resources/{js_vue3 => js}/components/formElements/FormPasswordField.vue (100%) rename resources/{js_vue3 => js}/components/formElements/FormSelect.vue (100%) rename resources/{js_vue3 => js}/components/formElements/FormToggle.vue (100%) delete mode 100644 resources/js/components/index.js rename resources/{js_vue3 => js}/composables/helpers.js (100%) rename resources/{js_vue3 => js}/helpers.js (100%) rename resources/{js_vue3 => js}/icons.js (100%) delete mode 100644 resources/js/langs/i18n.js rename resources/{js_vue3 => js}/layouts/Footer.vue (100%) rename resources/{js_vue3 => js}/layouts/FormWrapper.vue (100%) rename resources/{js_vue3 => js}/layouts/Modal.vue (100%) rename resources/{js_vue3 => js}/layouts/ResponsiveWidthWrapper.vue (100%) rename resources/{js_vue3 => js}/layouts/SettingTabs.vue (100%) delete mode 100644 resources/js/mixins.js delete mode 100644 resources/js/packages/clipboard.js delete mode 100644 resources/js/packages/fontawesome.js delete mode 100644 resources/js/packages/vue-storage.js rename resources/{js_vue3 => js}/router/index.js (100%) rename resources/{js_vue3 => js}/router/middlewarePipeline.js (100%) rename resources/{js_vue3 => js}/router/middlewares/authGuard.js (100%) rename resources/{js_vue3 => js}/router/middlewares/noEmptyError.js (100%) rename resources/{js_vue3 => js}/router/middlewares/noRegistration.js (100%) rename resources/{js_vue3 => js}/router/middlewares/setReturnTo.js (100%) rename resources/{js_vue3 => js}/router/middlewares/starter.js (100%) delete mode 100644 resources/js/routes.js rename resources/{js_vue3 => js}/services/appSettingService.js (100%) rename resources/{js_vue3 => js}/services/authService.js (100%) rename resources/{js_vue3 => js}/services/groupService.js (100%) rename resources/{js_vue3 => js}/services/httpClientFactory.js (100%) rename resources/{js_vue3 => js}/services/systemService.js (100%) rename resources/{js_vue3 => js}/services/twofaccountService.js (100%) rename resources/{js_vue3 => js}/services/userService.js (100%) rename resources/js/{ => services}/webauthn/identifyAuthenticationError.js (100%) rename resources/js/{ => services}/webauthn/identifyRegistrationError.js (100%) rename resources/js/{ => services}/webauthn/isValidDomain.js (100%) rename resources/js/{ => services}/webauthn/webauthnAbortService.js (100%) rename resources/{js_vue3 => js}/services/webauthn/webauthnService.js (100%) rename resources/{js_vue3 => js}/stores/appSettings.js (100%) rename resources/{js_vue3 => js}/stores/bus.js (100%) rename resources/{js_vue3 => js}/stores/groups.js (100%) rename resources/{js_vue3 => js}/stores/notify.js (100%) rename resources/{js_vue3 => js}/stores/twofaccounts.js (100%) rename resources/{js_vue3 => js}/stores/user.js (100%) delete mode 100644 resources/js/views/Accounts.vue delete mode 100644 resources/js/views/Capture.vue delete mode 100644 resources/js/views/Groups.vue delete mode 100644 resources/js/views/auth/Autolock.vue rename resources/{js_vue3 => js}/views/auth/RequestReset.vue (100%) delete mode 100644 resources/js/views/auth/password/Request.vue delete mode 100644 resources/js/views/auth/webauthn/Lost.vue delete mode 100644 resources/js/views/groups/Create.vue rename resources/{js_vue3 => js}/views/groups/CreateUpdate.vue (100%) delete mode 100644 resources/js/views/groups/Edit.vue rename resources/{js_vue3 => js}/views/groups/Groups.vue (100%) delete mode 100644 resources/js/views/settings/PATokens/Create.vue delete mode 100644 resources/js/views/settings/Settings.vue rename resources/{js_vue3 => js}/views/twofaccounts/Accounts.vue (100%) rename resources/{js_vue3 => js}/views/twofaccounts/Capture.vue (100%) delete mode 100644 resources/js/views/twofaccounts/Create.vue rename resources/{js_vue3 => js}/views/twofaccounts/CreateUpdate.vue (100%) delete mode 100644 resources/js/views/twofaccounts/Edit.vue delete mode 100644 resources/js/webauthn/webauthnService.js delete mode 100644 resources/js_vue3/app.js delete mode 100644 resources/js_vue3/components/Dots.vue delete mode 100644 resources/js_vue3/components/Kicker.vue delete mode 100644 resources/js_vue3/components/Spinner.vue delete mode 100644 resources/js_vue3/components/TotpLooper.vue delete mode 100644 resources/js_vue3/components/VersionChecker.vue delete mode 100644 resources/js_vue3/components/formElements/FormErrors.js delete mode 100644 resources/js_vue3/services/webauthn/identifyAuthenticationError.js delete mode 100644 resources/js_vue3/services/webauthn/identifyRegistrationError.js delete mode 100644 resources/js_vue3/services/webauthn/isValidDomain.js delete mode 100644 resources/js_vue3/services/webauthn/webauthnAbortService.js delete mode 100644 resources/js_vue3/views/About.vue delete mode 100644 resources/js_vue3/views/Error.vue delete mode 100644 resources/js_vue3/views/Start.vue delete mode 100644 resources/js_vue3/views/auth/Login.vue delete mode 100644 resources/js_vue3/views/auth/Register.vue delete mode 100644 resources/js_vue3/views/auth/password/Reset.vue delete mode 100644 resources/js_vue3/views/auth/webauthn/Recover.vue delete mode 100644 resources/js_vue3/views/settings/Account.vue delete mode 100644 resources/js_vue3/views/settings/Credentials/Edit.vue delete mode 100644 resources/js_vue3/views/settings/OAuth.vue delete mode 100644 resources/js_vue3/views/settings/Options.vue delete mode 100644 resources/js_vue3/views/settings/WebAuthn.vue delete mode 100644 resources/js_vue3/views/twofaccounts/Import.vue delete mode 100644 resources/js_vue3/views/twofaccounts/QRcode.vue diff --git a/resources/js_vue3/App.vue b/resources/js/App.vue similarity index 100% rename from resources/js_vue3/App.vue rename to resources/js/App.vue diff --git a/resources/js/api.js b/resources/js/api.js deleted file mode 100644 index a0fbd480..00000000 --- a/resources/js/api.js +++ /dev/null @@ -1,58 +0,0 @@ -import Vue from 'vue' -import axios from 'axios' -import VueAxios from 'vue-axios' -import router from './routes.js' - -Vue.use(VueAxios, axios) - -Vue.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; -Vue.axios.defaults.headers.common['Content-Type'] = 'application/json' - -if (window.appConfig.subdirectory) { - Vue.axios.defaults.baseURL = window.appConfig.subdirectory; -} - -Vue.axios.interceptors.response.use(response => response, error => { - - // Whether or not the promise must be returned, if unauthenticated is received - // we update the auth state of the front-end - if ( error.response.status === 401 ) { - Vue.$storage.remove('authenticated') - } - - // Return the error when we need to handle it at component level - if( error.config.hasOwnProperty('returnError') && error.config.returnError === true ) { - return Promise.reject(error); - } - - // Return the error for form validation at component level - if( error.response.status === 422 ) { - return Promise.reject(error); - } - - // Push to the login view and force the page to refresh to get a fresh CSRF token - if ( error.response.status === 401 ) { - router.push({ name: 'login', params: { forceRefresh: true } }) - return new Promise(() => {}) - } - - if ( error.response.status === 407 ) { - router.push({ name: 'genericError', params: { err: error.response, closable: false } }) - return new Promise(() => {}) - } - - // we push to a specific or generic error view - let routeName = 'genericError' - - // api calls are stateless so when user inactivity is detected - // by the backend middleware it cannot logout the user directly - // so it returns a 418 response. - // We catch the 418 response and push the user to the autolock view - if ( error.response.status === 418 ) routeName = 'autolock' - - if ( error.response.status === 404 ) routeName = '404' - - router.push({ name: routeName, params: { err: error.response } }) - return new Promise(() => {}) - -}) \ No newline at end of file diff --git a/resources/js/app.js b/resources/js/app.js index 9f29b65d..4ec43582 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -1,65 +1,95 @@ -import Vue from 'vue' -import mixins from './mixins' -import VueStorage from './packages/vue-storage' -import i18n from './langs/i18n' -import router from './routes' -import api from './api' -import FontAwesome from './packages/fontawesome' -import Clipboard from './packages/clipboard' -import Notifications from 'vue-notification' +import '/resources/js/assets/app.scss'; -import './components' +import Notifications from '@kyvg/vue3-notification' +import App from './App.vue' +import router from './router' +import FontAwesomeIcon from './icons' +// import helpers from './helpers' -Vue.use(Notifications) +const app = createApp(App) -const app = new Vue({ - el: '#app', - data: { - appSettings: window.appSettings, - appConfig: window.appConfig, - userPreferences: window.userPreferences, - isDemoApp: window.isDemoApp, - isTestingApp: window.isTestingApp, - prefersDarkScheme: window.matchMedia('(prefers-color-scheme: dark)').matches, - spinner: { - active: false, - message: 'loading' - }, - }, +// Immutable app properties provided by the laravel blade view +const $2fauth = { + prefix: '2fauth_', + config: window.appConfig, + version: window.appVersion, + isDemoApp: window.isDemoApp, + isTestingApp: window.isTestingApp, + langs: window.appLocales, +} +app.provide('2fauth', readonly($2fauth)) - computed: { - showDarkMode: function() { - return this.userPreferences.theme == 'dark' || - (this.userPreferences.theme == 'system' && this.prefersDarkScheme) - } - }, - - mounted () { - this.mediaQueryList = window.matchMedia('(prefers-color-scheme: dark)') - this.$nextTick(() => { - this.mediaQueryList.addEventListener('change', this.setDarkScheme) - }) - }, - - beforeDestroy () { - this.mediaQueryList.removeEventListener('change', this.setDarkScheme) - }, - - methods: { - setDarkScheme ({ matches }) { - this.prefersDarkScheme = matches - }, - - showSpinner(message) { - this.spinner.message = message; - this.spinner.active = true; - }, - - hideSpinner() { - this.spinner.active = false; - this.spinner.message = 'loading'; - } - }, - i18n, - router, +// Stores +const pinia = createPinia() +pinia.use(({ store }) => { + store.$2fauth = $2fauth; }); +app.use(pinia) + +// Router +app.use(router) + +// Localization +app.use(i18nVue, { + lang: document.documentElement.lang.substring(0, 2), + resolve: async lang => { + const langs = import.meta.glob('../lang/*.json'); + if (lang.includes('php_')) { + return await langs[`../lang/${lang}.json`](); + } + } +}) + +// Notifications +app.use(Notifications) + +// Global components registration +import ResponsiveWidthWrapper from '@/layouts/ResponsiveWidthWrapper.vue' +import FormWrapper from '@/layouts/FormWrapper.vue' +import Footer from '@/layouts/Footer.vue' +import Modal from '@/layouts/Modal.vue' +import VueButton from '@/components/formElements/Button.vue' +import ButtonBackCloseCancel from '@/components/formElements/ButtonBackCloseCancel.vue' +import FieldError from '@/components/formElements/FieldError.vue' +import FormField from '@/components/formElements/FormField.vue' +import FormPasswordField from '@/components/formElements/FormPasswordField.vue' +import FormSelect from '@/components/formElements/FormSelect.vue' +import FormToggle from '@/components/formElements/FormToggle.vue' +import FormCheckbox from '@/components/formElements/FormCheckbox.vue' +import FormButtons from '@/components/formElements/FormButtons.vue' +import Kicker from '@/components/Kicker.vue' + +app + .component('FontAwesomeIcon', FontAwesomeIcon) + .component('ResponsiveWidthWrapper', ResponsiveWidthWrapper) + .component('FormWrapper', FormWrapper) + .component('VueFooter', Footer) + .component('Modal', Modal) + .component('VueButton', VueButton) + .component('ButtonBackCloseCancel', ButtonBackCloseCancel) + .component('FieldError', FieldError) + .component('FormField', FormField) + .component('FormPasswordField', FormPasswordField) + .component('FormSelect', FormSelect) + .component('FormToggle', FormToggle) + .component('FormCheckbox', FormCheckbox) + .component('FormButtons', FormButtons) + .component('Kicker', Kicker) + +// Global error handling +// import { useNotifyStore } from '@/stores/notify' +// if (process.env.NODE_ENV != 'development') { +// app.config.errorHandler = (err) => { +// useNotifyStore().error(err) +// } +// } + +// Helpers +// app.config.globalProperties.$helpers = helpers + +// App mounting +app.mount('#app') + +// Theme +import { useUserStore } from '@/stores/user' +useUserStore().applyUserPrefs() diff --git a/resources/js_vue3/assets/app.scss b/resources/js/assets/app.scss similarity index 100% rename from resources/js_vue3/assets/app.scss rename to resources/js/assets/app.scss diff --git a/resources/js_vue3/components/ActionButtons.vue b/resources/js/components/ActionButtons.vue similarity index 100% rename from resources/js_vue3/components/ActionButtons.vue rename to resources/js/components/ActionButtons.vue diff --git a/resources/js/components/App.vue b/resources/js/components/App.vue deleted file mode 100644 index 52930527..00000000 --- a/resources/js/components/App.vue +++ /dev/null @@ -1,41 +0,0 @@ - - - diff --git a/resources/js/components/Button.vue b/resources/js/components/Button.vue deleted file mode 100644 index ec7a180c..00000000 --- a/resources/js/components/Button.vue +++ /dev/null @@ -1,42 +0,0 @@ - - - diff --git a/resources/js_vue3/components/DestinationGroupSelector.vue b/resources/js/components/DestinationGroupSelector.vue similarity index 100% rename from resources/js_vue3/components/DestinationGroupSelector.vue rename to resources/js/components/DestinationGroupSelector.vue diff --git a/resources/js/components/Dots.vue b/resources/js/components/Dots.vue index db1aa8a9..db17f217 100644 --- a/resources/js/components/Dots.vue +++ b/resources/js/components/Dots.vue @@ -1,44 +1,54 @@ + + - - \ No newline at end of file + \ No newline at end of file diff --git a/resources/js/components/FieldError.vue b/resources/js/components/FieldError.vue deleted file mode 100644 index ad6e7e17..00000000 --- a/resources/js/components/FieldError.vue +++ /dev/null @@ -1,23 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js/components/Footer.vue b/resources/js/components/Footer.vue deleted file mode 100644 index 7ba32023..00000000 --- a/resources/js/components/Footer.vue +++ /dev/null @@ -1,58 +0,0 @@ - - - diff --git a/resources/js/components/Form.js b/resources/js/components/Form.js deleted file mode 100644 index ba2323ca..00000000 --- a/resources/js/components/Form.js +++ /dev/null @@ -1,317 +0,0 @@ -import Vue from 'vue' -import Errors from './FormErrors' - -class Form { - /** - * Create a new form instance. - * - * @param {Object} data - */ - constructor (data = {}) { - this.isBusy = false - this.isDisabled = false - // this.successful = false - this.errors = new Errors() - this.originalData = this.deepCopy(data) - - Object.assign(this, data) - } - - /** - * Fill form data. - * - * @param {Object} data - */ - fill (data) { - this.keys().forEach(key => { - this[key] = data[key] - }) - } - - /** - * Update original form data. - */ - setOriginal () { - Object.keys(this) - .filter(key => !Form.ignore.includes(key)) - .forEach(key => { - this.originalData[key] = this.deepCopy(this[key]) - }) - } - - /** - * Fill form data. - * - * @param {Object} data - */ - fillWithKeyValueObject (data) { - this.keys().forEach(key => { - const keyValueObject = data.find(s => s.key === key.toString()) - if(keyValueObject != undefined) { - this[key] = keyValueObject.value - } - }) - } - - /** - * Get the form data. - * - * @return {Object} - */ - data () { - return this.keys().reduce((data, key) => ( - { ...data, [key]: this[key] } - ), {}) - } - - /** - * Get the form data keys. - * - * @return {Array} - */ - keys () { - return Object.keys(this) - .filter(key => !Form.ignore.includes(key)) - } - - /** - * Start processing the form. - */ - startProcessing () { - this.errors.clear() - this.isBusy = true - // this.successful = false - } - - /** - * Finish processing the form. - */ - finishProcessing () { - this.isBusy = false - // this.successful = true - } - - /** - * Clear the form errors. - */ - clear () { - this.errors.clear() - // this.successful = false - } - - /** - * Reset the form fields. - */ - reset () { - Object.keys(this) - .filter(key => !Form.ignore.includes(key)) - .forEach(key => { - this[key] = this.deepCopy(this.originalData[key]) - }) - } - - /** - * Submit the form via a GET request. - * - * @param {String} url - * @param {Object} config (axios config) - * @return {Promise} - */ - get (url, config = {}) { - return this.submit('get', url, config) - } - - /** - * Submit the form via a POST request. - * - * @param {String} url - * @param {Object} config (axios config) - * @return {Promise} - */ - post (url, config = {}) { - return this.submit('post', url, config) - } - - /** - * Submit the form via a PATCH request. - * - * @param {String} url - * @param {Object} config (axios config) - * @return {Promise} - */ - patch (url, config = {}) { - return this.submit('patch', url, config) - } - - /** - * Submit the form via a PUT request. - * - * @param {String} url - * @param {Object} config (axios config) - * @return {Promise} - */ - put (url, config = {}) { - return this.submit('put', url, config) - } - - /** - * Submit the form via a DELETE request. - * - * @param {String} url - * @param {Object} config (axios config) - * @return {Promise} - */ - delete (url, config = {}) { - return this.submit('delete', url, config) - } - - /** - * Submit the form data via an HTTP request. - * - * @param {String} method (get, post, patch, put) - * @param {String} url - * @param {Object} config (axios config) - * @return {Promise} - */ - submit (method, url, config = {}) { - this.startProcessing() - - const data = method === 'get' - ? { params: this.data() } - : this.data() - - return new Promise((resolve, reject) => { - // (Form.axios || axios).request({ url: this.route(url), method, data, ...config }) - Vue.axios.request({ url: this.route(url), method, data, ...config }) - .then(response => { - this.finishProcessing() - - resolve(response) - }) - .catch(error => { - this.isBusy = false - - if (error.response) { - this.errors.set(this.extractErrors(error.response)) - } - - reject(error) - }) - }) - } - - /** - * Submit the form data via an HTTP request. - * - * @param {String} method (get, post, patch, put) - * @param {String} url - * @param {Object} config (axios config) - * @return {Promise} - */ - upload (url, formData, config = {}) { - this.startProcessing() - - return new Promise((resolve, reject) => { - // (Form.axios || axios).request({ url: this.route(url), method, data, ...config }) - Vue.axios.request({ url: this.route(url), method: 'post', data: formData, header: {'Content-Type' : 'multipart/form-data'}, ...config }) - .then(response => { - this.finishProcessing() - - resolve(response) - }) - .catch(error => { - this.isBusy = false - - if (error.response) { - this.errors.set(this.extractErrors(error.response)) - } - - reject(error) - }) - }) - } - - /** - * Extract the errors from the response object. - * - * @param {Object} response - * @return {Object} - */ - extractErrors (response) { - if (!response.data || typeof response.data !== 'object') { - return { error: Form.errorMessage } - } - - if (response.data.errors) { - return { ...response.data.errors } - } - - if (response.data.message) { - return { error: response.data.message } - } - - return { ...response.data } - } - - /** - * Get a named route. - * - * @param {String} name - * @return {Object} parameters - * @return {String} - */ - route (name, parameters = {}) { - let url = name - - if (Form.routes.hasOwnProperty(name)) { - url = decodeURI(Form.routes[name]) - } - - if (typeof parameters !== 'object') { - parameters = { id: parameters } - } - - Object.keys(parameters).forEach(key => { - url = url.replace(`{${key}}`, parameters[key]) - }) - - return url - } - - /** - * Clear errors on keydown. - * - * @param {KeyboardEvent} event - */ - onKeydown (event) { - if (event.target.name) { - this.errors.clear(event.target.name) - } - } - - /** - * Deep copy the given object. - * - * @param {Object} obj - * @return {Object} - */ - deepCopy (obj) { - if (obj === null || typeof obj !== 'object') { - return obj - } - - const copy = Array.isArray(obj) ? [] : {} - - Object.keys(obj).forEach(key => { - copy[key] = this.deepCopy(obj[key]) - }) - - return copy - } -} - -Form.routes = {} -Form.errorMessage = 'Something went wrong. Please try again.' -Form.ignore = ['isBusy', 'isDisabled', 'errors', 'originalData'] - -export default Form diff --git a/resources/js/components/FormButtons.vue b/resources/js/components/FormButtons.vue deleted file mode 100644 index 506049f7..00000000 --- a/resources/js/components/FormButtons.vue +++ /dev/null @@ -1,64 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js/components/FormCheckbox.vue b/resources/js/components/FormCheckbox.vue deleted file mode 100644 index 4c63f5b2..00000000 --- a/resources/js/components/FormCheckbox.vue +++ /dev/null @@ -1,57 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js/components/FormField.vue b/resources/js/components/FormField.vue deleted file mode 100644 index 5826e29e..00000000 --- a/resources/js/components/FormField.vue +++ /dev/null @@ -1,81 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js/components/FormPasswordField.vue b/resources/js/components/FormPasswordField.vue deleted file mode 100644 index 61c86941..00000000 --- a/resources/js/components/FormPasswordField.vue +++ /dev/null @@ -1,132 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js/components/FormSelect.vue b/resources/js/components/FormSelect.vue deleted file mode 100644 index 35620d46..00000000 --- a/resources/js/components/FormSelect.vue +++ /dev/null @@ -1,54 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js/components/FormSwitch.vue b/resources/js/components/FormSwitch.vue deleted file mode 100644 index 78f69206..00000000 --- a/resources/js/components/FormSwitch.vue +++ /dev/null @@ -1,43 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js/components/FormToggle.vue b/resources/js/components/FormToggle.vue deleted file mode 100644 index 63232a2f..00000000 --- a/resources/js/components/FormToggle.vue +++ /dev/null @@ -1,95 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js/components/FormWrapper.vue b/resources/js/components/FormWrapper.vue deleted file mode 100644 index 81856793..00000000 --- a/resources/js/components/FormWrapper.vue +++ /dev/null @@ -1,31 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js_vue3/components/GroupSwitch.vue b/resources/js/components/GroupSwitch.vue similarity index 100% rename from resources/js_vue3/components/GroupSwitch.vue rename to resources/js/components/GroupSwitch.vue diff --git a/resources/js/components/Kicker.vue b/resources/js/components/Kicker.vue index 875ffdfa..fddca3bc 100644 --- a/resources/js/components/Kicker.vue +++ b/resources/js/components/Kicker.vue @@ -1,58 +1,70 @@ - \ No newline at end of file diff --git a/resources/js/views/settings/OAuth.vue b/resources/js/views/settings/OAuth.vue index 951970f6..e08a6fbb 100644 --- a/resources/js/views/settings/OAuth.vue +++ b/resources/js/views/settings/OAuth.vue @@ -1,30 +1,170 @@ + + - - \ No newline at end of file diff --git a/resources/js/views/settings/Options.vue b/resources/js/views/settings/Options.vue index 2b093a01..ee9beb56 100644 --- a/resources/js/views/settings/Options.vue +++ b/resources/js/views/settings/Options.vue @@ -1,277 +1,199 @@ + + - - \ No newline at end of file + \ No newline at end of file diff --git a/resources/js/views/settings/PATokens/Create.vue b/resources/js/views/settings/PATokens/Create.vue deleted file mode 100644 index 042dbe15..00000000 --- a/resources/js/views/settings/PATokens/Create.vue +++ /dev/null @@ -1,43 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js/views/settings/Settings.vue b/resources/js/views/settings/Settings.vue deleted file mode 100644 index d5809374..00000000 --- a/resources/js/views/settings/Settings.vue +++ /dev/null @@ -1,43 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js/views/settings/WebAuthn.vue b/resources/js/views/settings/WebAuthn.vue index 5318e77e..9f582cc8 100644 --- a/resources/js/views/settings/WebAuthn.vue +++ b/resources/js/views/settings/WebAuthn.vue @@ -1,8 +1,125 @@ + + - - \ No newline at end of file + \ No newline at end of file diff --git a/resources/js_vue3/views/twofaccounts/Accounts.vue b/resources/js/views/twofaccounts/Accounts.vue similarity index 100% rename from resources/js_vue3/views/twofaccounts/Accounts.vue rename to resources/js/views/twofaccounts/Accounts.vue diff --git a/resources/js_vue3/views/twofaccounts/Capture.vue b/resources/js/views/twofaccounts/Capture.vue similarity index 100% rename from resources/js_vue3/views/twofaccounts/Capture.vue rename to resources/js/views/twofaccounts/Capture.vue diff --git a/resources/js/views/twofaccounts/Create.vue b/resources/js/views/twofaccounts/Create.vue deleted file mode 100644 index 0188d74c..00000000 --- a/resources/js/views/twofaccounts/Create.vue +++ /dev/null @@ -1,436 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js_vue3/views/twofaccounts/CreateUpdate.vue b/resources/js/views/twofaccounts/CreateUpdate.vue similarity index 100% rename from resources/js_vue3/views/twofaccounts/CreateUpdate.vue rename to resources/js/views/twofaccounts/CreateUpdate.vue diff --git a/resources/js/views/twofaccounts/Edit.vue b/resources/js/views/twofaccounts/Edit.vue deleted file mode 100644 index ac73f434..00000000 --- a/resources/js/views/twofaccounts/Edit.vue +++ /dev/null @@ -1,301 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js/views/twofaccounts/Import.vue b/resources/js/views/twofaccounts/Import.vue index 8972459d..096b9149 100644 --- a/resources/js/views/twofaccounts/Import.vue +++ b/resources/js/views/twofaccounts/Import.vue @@ -1,70 +1,369 @@ + + - - \ No newline at end of file diff --git a/resources/js/views/twofaccounts/QRcode.vue b/resources/js/views/twofaccounts/QRcode.vue index ab076fab..c0025fe8 100644 --- a/resources/js/views/twofaccounts/QRcode.vue +++ b/resources/js/views/twofaccounts/QRcode.vue @@ -1,48 +1,35 @@ + + - - \ No newline at end of file + \ No newline at end of file diff --git a/resources/js/webauthn/webauthnService.js b/resources/js/webauthn/webauthnService.js deleted file mode 100644 index 4db87ae6..00000000 --- a/resources/js/webauthn/webauthnService.js +++ /dev/null @@ -1,161 +0,0 @@ -/** - * MIT License - * - * Copyright (c) Italo Israel Baeza Cabrera - * https://github.com/Laragear/WebAuthn - * - * 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: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * 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. - */ - -export default class WebauthnService { - - /** - * Create a new WebauthnService instance. - * - */ - constructor () { - - } - - /** - * Parses the Public Key Options received from the Server for the browser. - * - * @param publicKey {Object} - * @returns {Object} - */ - parseIncomingServerOptions(publicKey) { - publicKey.challenge = WebauthnService.uint8Array(publicKey.challenge); - - if ('user' in publicKey) { - publicKey.user = { - ...publicKey.user, - id: WebauthnService.uint8Array(publicKey.user.id) - }; - } - - [ - "excludeCredentials", - "allowCredentials" - ] - .filter(key => key in publicKey) - .forEach(key => { - publicKey[key] = publicKey[key].map(data => { - return {...data, id: WebauthnService.uint8Array(data.id)}; - }); - }); - - return publicKey; - } - - - /** - * Parses the outgoing credentials from the browser to the server. - * - * @param credentials {Credential|PublicKeyCredential} - * @return {{response: {string}, rawId: string, id: string, type: string}} - */ - parseOutgoingCredentials(credentials) { - let parseCredentials = { - id: credentials.id, - type: credentials.type, - rawId: WebauthnService.arrayToBase64String(credentials.rawId), - response: {} - }; - - [ - "clientDataJSON", - "attestationObject", - "authenticatorData", - "signature", - "userHandle" - ] - .filter(key => key in credentials.response) - .forEach(key => parseCredentials.response[key] = WebauthnService.arrayToBase64String(credentials.response[key])); - - return parseCredentials; - } - - - /** - * Transform a string into Uint8Array instance. - * - * @param input {string} - * @param useAtob {boolean} - * @returns {Uint8Array} - */ - static uint8Array(input, useAtob = false) { - return Uint8Array.from( - useAtob ? atob(input) : WebauthnService.base64UrlDecode(input), c => c.charCodeAt(0) - ); - } - - - /** - * Encodes an array of bytes to a BASE64 URL string - * - * @param arrayBuffer {ArrayBuffer|Uint8Array} - * @returns {string} - */ - static arrayToBase64String(arrayBuffer) { - return btoa(String.fromCharCode(...new Uint8Array(arrayBuffer))); - } - - - /** - * Decodes a BASE64 URL string into a normal string. - * - * @param input {string} - * @returns {string|Iterable} - */ - static base64UrlDecode(input) { - input = input.replace(/-/g, "+").replace(/_/g, "/"); - - const pad = input.length % 4; - - if (pad) { - if (pad === 1) { - throw new Error("InvalidLengthError: Input base64url string is the wrong length to determine padding"); - } - - input += new Array(5 - pad).join("="); - } - - return atob(input); - } - - - /** - * Checks if the browser supports WebAuthn. - * - * @returns {boolean} - */ - static supportsWebAuthn() { - return (window?.PublicKeyCredential !== undefined && typeof window.PublicKeyCredential === 'function'); - } - - - /** - * Checks if the browser doesn't support WebAuthn. - * - * @returns {boolean} - */ - static doesntSupportWebAuthn() { - return !this.supportsWebAuthn(); - } -} diff --git a/resources/js_vue3/app.js b/resources/js_vue3/app.js deleted file mode 100644 index 4b996b3d..00000000 --- a/resources/js_vue3/app.js +++ /dev/null @@ -1,95 +0,0 @@ -import '/resources/js_vue3/assets/app.scss'; - -import Notifications from '@kyvg/vue3-notification' -import App from './App.vue' -import router from './router' -import FontAwesomeIcon from './icons' -// import helpers from './helpers' - -const app = createApp(App) - -// Immutable app properties provided by the laravel blade view -const $2fauth = { - prefix: '2fauth_', - config: window.appConfig, - version: window.appVersion, - isDemoApp: window.isDemoApp, - isTestingApp: window.isTestingApp, - langs: window.appLocales, -} -app.provide('2fauth', readonly($2fauth)) - -// Stores -const pinia = createPinia() -pinia.use(({ store }) => { - store.$2fauth = $2fauth; -}); -app.use(pinia) - -// Router -app.use(router) - -// Localization -app.use(i18nVue, { - lang: document.documentElement.lang.substring(0, 2), - resolve: async lang => { - const langs = import.meta.glob('../lang/*.json'); - if (lang.includes('php_')) { - return await langs[`../lang/${lang}.json`](); - } - } -}) - -// Notifications -app.use(Notifications) - -// Global components registration -import ResponsiveWidthWrapper from '@/layouts/ResponsiveWidthWrapper.vue' -import FormWrapper from '@/layouts/FormWrapper.vue' -import Footer from '@/layouts/Footer.vue' -import Modal from '@/layouts/Modal.vue' -import VueButton from '@/components/formElements/Button.vue' -import ButtonBackCloseCancel from '@/components/formElements/ButtonBackCloseCancel.vue' -import FieldError from '@/components/formElements/FieldError.vue' -import FormField from '@/components/formElements/FormField.vue' -import FormPasswordField from '@/components/formElements/FormPasswordField.vue' -import FormSelect from '@/components/formElements/FormSelect.vue' -import FormToggle from '@/components/formElements/FormToggle.vue' -import FormCheckbox from '@/components/formElements/FormCheckbox.vue' -import FormButtons from '@/components/formElements/FormButtons.vue' -import Kicker from '@/components/Kicker.vue' - -app - .component('FontAwesomeIcon', FontAwesomeIcon) - .component('ResponsiveWidthWrapper', ResponsiveWidthWrapper) - .component('FormWrapper', FormWrapper) - .component('VueFooter', Footer) - .component('Modal', Modal) - .component('VueButton', VueButton) - .component('ButtonBackCloseCancel', ButtonBackCloseCancel) - .component('FieldError', FieldError) - .component('FormField', FormField) - .component('FormPasswordField', FormPasswordField) - .component('FormSelect', FormSelect) - .component('FormToggle', FormToggle) - .component('FormCheckbox', FormCheckbox) - .component('FormButtons', FormButtons) - .component('Kicker', Kicker) - -// Global error handling -// import { useNotifyStore } from '@/stores/notify' -// if (process.env.NODE_ENV != 'development') { -// app.config.errorHandler = (err) => { -// useNotifyStore().error(err) -// } -// } - -// Helpers -// app.config.globalProperties.$helpers = helpers - -// App mounting -app.mount('#app') - -// Theme -import { useUserStore } from '@/stores/user' -useUserStore().applyUserPrefs() diff --git a/resources/js_vue3/components/Dots.vue b/resources/js_vue3/components/Dots.vue deleted file mode 100644 index db17f217..00000000 --- a/resources/js_vue3/components/Dots.vue +++ /dev/null @@ -1,54 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js_vue3/components/Kicker.vue b/resources/js_vue3/components/Kicker.vue deleted file mode 100644 index fddca3bc..00000000 --- a/resources/js_vue3/components/Kicker.vue +++ /dev/null @@ -1,70 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js_vue3/components/Spinner.vue b/resources/js_vue3/components/Spinner.vue deleted file mode 100644 index 88bf758f..00000000 --- a/resources/js_vue3/components/Spinner.vue +++ /dev/null @@ -1,64 +0,0 @@ - - - - - diff --git a/resources/js_vue3/components/TotpLooper.vue b/resources/js_vue3/components/TotpLooper.vue deleted file mode 100644 index ef998e17..00000000 --- a/resources/js_vue3/components/TotpLooper.vue +++ /dev/null @@ -1,122 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js_vue3/components/VersionChecker.vue b/resources/js_vue3/components/VersionChecker.vue deleted file mode 100644 index 6c3bc6ed..00000000 --- a/resources/js_vue3/components/VersionChecker.vue +++ /dev/null @@ -1,44 +0,0 @@ - - - diff --git a/resources/js_vue3/components/formElements/FormErrors.js b/resources/js_vue3/components/formElements/FormErrors.js deleted file mode 100644 index 12c1194f..00000000 --- a/resources/js_vue3/components/formElements/FormErrors.js +++ /dev/null @@ -1,141 +0,0 @@ - -export default class Errors { - /** - * Create a new error bag instance. - */ - constructor () { - this.errors = {} - } - - /** - * Set the errors object or field error messages. - * - * @param {Object|String} field - * @param {Array|String|undefined} messages - */ - set (field, messages) { - if (typeof field === 'object') { - this.errors = field - } else { - this.set({ ...this.errors, [field]: arrayWrap(messages) }) - } - } - - /** - * Get all the errors. - * - * @return {Object} - */ - all () { - return this.errors - } - - /** - * Determine if there is an error for the given field. - * - * @param {String} field - * @return {Boolean} - */ - has (field) { - return this.errors.hasOwnProperty(field) - } - - /** - * Determine if there are any errors for the given fields. - * - * @param {...String} fields - * @return {Boolean} - */ - hasAny (...fields) { - return fields.some(field => this.has(field)) - } - - /** - * Determine if there are any errors. - * - * @return {Boolean} - */ - any () { - return Object.keys(this.errors).length > 0 - } - - /** - * Get the first error message for the given field. - * - * @param String} field - * @return {String|undefined} - */ - get (field) { - if (this.has(field)) { - return this.getAll(field)[0] - } - } - - /** - * Get all the error messages for the given field. - * - * @param {String} field - * @return {Array} - */ - getAll (field) { - return arrayWrap(this.errors[field] || []) - } - - /** - * Get the error message for the given fields. - * - * @param {...String} fields - * @return {Array} - */ - only (...fields) { - const messages = [] - - fields.forEach(field => { - const message = this.get(field) - - if (message) { - messages.push(message) - } - }) - - return messages - } - - /** - * Get all the errors in a flat array. - * - * @return {Array} - */ - flatten () { - return Object.values(this.errors).reduce((a, b) => a.concat(b), []) - } - - /** - * Clear one or all error fields. - * - * @param {String|undefined} field - */ - clear (field) { - const errors = {} - - if (field) { - Object.keys(this.errors).forEach(key => { - if (key !== field) { - errors[key] = this.errors[key] - } - }) - } - - this.set(errors) - } -} - -/** - * If the given value is not an array, wrap it in one. - * - * @param {Any} value - * @return {Array} - */ -function arrayWrap (value) { - return Array.isArray(value) ? value : [value] -} \ No newline at end of file diff --git a/resources/js_vue3/services/webauthn/identifyAuthenticationError.js b/resources/js_vue3/services/webauthn/identifyAuthenticationError.js deleted file mode 100644 index 5fe7f59c..00000000 --- a/resources/js_vue3/services/webauthn/identifyAuthenticationError.js +++ /dev/null @@ -1,95 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth - * Copyright (c) 2020 Matthew Miller - https://github.com/MasterKale/SimpleWebAuthn - * - * 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: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * 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. - * - */ - -import { isValidDomain } from './isValidDomain'; - -/** - * Attempt to intuit _why_ an error was raised after calling `navigator.credentials.get()` - */ -export function identifyAuthenticationError(error, options) { - const { publicKey } = options; - - if (error.name === 'AbortError') { - if (options.signal instanceof AbortSignal) { - // Authentication ceremony was sent an abort signal - // https://www.w3.org/TR/webauthn-2/#sctn-createCredential (Step 16) - - return { - phrase: 'errors.aborted_by_user', - type: 'is-warning' - } - } - - } else if (error.name === 'NotAllowedError') { - /** - * Pass the error directly through. Platforms are overloading this error beyond what the spec - * defines and we don't want to overwrite potentially useful error messages. - */ - - return { - phrase: 'errors.not_allowed_operation', - type: 'is-danger' - } - - } else if (error.name === 'SecurityError') { - - const effectiveDomain = window.location.hostname; - - if (!isValidDomain(effectiveDomain)) { - // The current location domain is not a valid domain - // https://www.w3.org/TR/webauthn-2/#sctn-discover-from-external-source (Step 5) - - return { - phrase: 'errors.2fauth_has_not_a_valid_domain', - type: 'is-danger' - } - - } else if (publicKey.rpId !== effectiveDomain) { - // The RP ID "${publicKey.rpId}" is invalid for this domain - // // https://www.w3.org/TR/webauthn-2/#sctn-discover-from-external-source (Step 6) - - return { - phrase: 'errors.security_error_check_rpid', - type: 'is-danger' - } - } - - } else if (error.name === 'UnknownError') { - // The authenticator was unable to process the specified options, or could not create a new assertion signature - // https://www.w3.org/TR/webauthn-2/#sctn-op-get-assertion (Step 1) - // https://www.w3.org/TR/webauthn-2/#sctn-op-get-assertion (Step 12) - - return { - phrase: 'errors.unknown_error', - type: 'is-danger' - } - } - - return { - phrase: 'errors.unknown_error', - type: 'is-danger' - } -} \ No newline at end of file diff --git a/resources/js_vue3/services/webauthn/identifyRegistrationError.js b/resources/js_vue3/services/webauthn/identifyRegistrationError.js deleted file mode 100644 index f7691adb..00000000 --- a/resources/js_vue3/services/webauthn/identifyRegistrationError.js +++ /dev/null @@ -1,160 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth - * Copyright (c) 2020 Matthew Miller - https://github.com/MasterKale/SimpleWebAuthn - * - * 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: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * 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. - * - */ - -import { isValidDomain } from './isValidDomain'; - -/** - * Attempt to intuit _why_ an error was raised after calling `navigator.credentials.create()` - */ -export function identifyRegistrationError(error, options) { - const { publicKey } = options; - - if (error.name === 'AbortError') { - if (options.signal instanceof AbortSignal) { - // Registration ceremony was sent an abort signal - // https://www.w3.org/TR/webauthn-2/#sctn-createCredential (Step 16) - - return { - phrase: 'errors.aborted_by_user', - type: 'is-warning' - } - } - - } else if (error.name === 'ConstraintError') { - if (publicKey.authenticatorSelection?.requireResidentKey === true) { - // Discoverable credentials were required but no available authenticator supported it - // https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred (Step 4) - - return { - phrase: 'errors.authenticator_missing_discoverable_credential_support', - type: 'is-danger' - } - - } else if (publicKey.authenticatorSelection?.userVerification === 'required') { - // User verification was required but no available authenticator supported it - // https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred (Step 5) - - return { - phrase: 'errors.authenticator_missing_user_verification_support', - type: 'is-danger' - } - } - - } else if (error.name === 'InvalidStateError') { - // The authenticator was previously registered - // https://www.w3.org/TR/webauthn-2/#sctn-createCredential (Step 20) - // https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred (Step 3) - - return { - phrase: 'errors.security_device_already_registered', - type: 'is-danger' - } - - } else if (error.name === 'NotAllowedError') { - /** - * Pass the error directly through. Platforms are overloading this error beyond what the spec - * defines and we don't want to overwrite potentially useful error messages. - */ - - return { - phrase: 'errors.not_allowed_operation', - type: 'is-danger' - } - - } else if (error.name === 'NotSupportedError') { - - const validPubKeyCredParams = publicKey.pubKeyCredParams.filter( - (param) => param.type === 'public-key', - ); - - if (validPubKeyCredParams.length === 0) { - // No entry in pubKeyCredParams was of type "public-key" - // https://www.w3.org/TR/webauthn-2/#sctn-createCredential (Step 10) - - return { - phrase: 'errors.no_entry_was_of_type_public_key', - type: 'is-danger' - } - } - - // No available authenticator supported any of the specified pubKeyCredParams algorithms - // https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred (Step 2) - - return { - phrase: 'errors.no_authenticator_support_specified_algorithms', - type: 'is-danger' - } - - } else if (error.name === 'SecurityError') { - - const effectiveDomain = window.location.hostname; - - if (!isValidDomain(effectiveDomain)) { - // The current location domain is not a valid domain - // https://www.w3.org/TR/webauthn-2/#sctn-createCredential (Step 7) - - return { - phrase: 'errors.2fauth_has_not_a_valid_domain', - type: 'is-danger' - } - - } else if (publicKey.rp.id !== effectiveDomain) { - // The RP ID "${publicKey.rp.id}" is invalid for this domain - // https://www.w3.org/TR/webauthn-2/#sctn-createCredential (Step 8) - - return { - phrase: 'errors.security_error_check_rpid', - type: 'is-danger' - } - } - - } else if (error.name === 'TypeError') { - if (publicKey.user.id.byteLength < 1 || publicKey.user.id.byteLength > 64) { - // User ID was not between 1 and 64 characters - // https://www.w3.org/TR/webauthn-2/#sctn-createCredential (Step 5) - - return { - phrase: 'errors.user_id_not_between_1_64', - type: 'is-danger' - } - } - - } else if (error.name === 'UnknownError') { - // The authenticator was unable to process the specified options, or could not create a new credential - // https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred (Step 1) - // https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred (Step 8) - - return { - phrase: 'errors.unknown_error', - type: 'is-danger' - } - } - - return { - phrase: 'errors.unknown_error', - type: 'is-danger' - } -} diff --git a/resources/js_vue3/services/webauthn/isValidDomain.js b/resources/js_vue3/services/webauthn/isValidDomain.js deleted file mode 100644 index b02e8522..00000000 --- a/resources/js_vue3/services/webauthn/isValidDomain.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * A simple test to determine if a hostname is a properly-formatted domain name - * - * A "valid domain" is defined here: https://url.spec.whatwg.org/#valid-domain - * - * Regex sourced from here: - * https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch08s15.html - */ -export function isValidDomain(hostname) { - return ( - // Consider localhost valid as well since it's okay wrt Secure Contexts - hostname === 'localhost' || - /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i.test(hostname) - ); -} \ No newline at end of file diff --git a/resources/js_vue3/services/webauthn/webauthnAbortService.js b/resources/js_vue3/services/webauthn/webauthnAbortService.js deleted file mode 100644 index b8aa9ad0..00000000 --- a/resources/js_vue3/services/webauthn/webauthnAbortService.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2020 Matthew Miller - https://github.com/MasterKale/SimpleWebAuthn - * - * 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: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * 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. - * - * A way to cancel an existing WebAuthn request, for example to cancel a - * WebAuthn autofill authentication request for a manual authentication attempt. - */ -class WebauthnAbortService { - controller; - - /** - * Prepare an abort signal that will help support multiple auth attempts without needing to - * reload the page - */ - createNewAbortSignal() { - // Abort any existing calls to navigator.credentials.create() or navigator.credentials.get() - if (this.controller) { - const abortError = new Error( - 'Cancelling existing WebAuthn API call for new one', - ); - abortError.name = 'AbortError'; - this.controller.abort(abortError); - } - - const newController = new AbortController(); - - this.controller = newController; - return newController.signal; - } -} - -export const webauthnAbortService = new WebauthnAbortService(); \ No newline at end of file diff --git a/resources/js_vue3/views/About.vue b/resources/js_vue3/views/About.vue deleted file mode 100644 index ec3b514d..00000000 --- a/resources/js_vue3/views/About.vue +++ /dev/null @@ -1,138 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js_vue3/views/Error.vue b/resources/js_vue3/views/Error.vue deleted file mode 100644 index 70c7ccb2..00000000 --- a/resources/js_vue3/views/Error.vue +++ /dev/null @@ -1,51 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js_vue3/views/Start.vue b/resources/js_vue3/views/Start.vue deleted file mode 100644 index dbdde8b6..00000000 --- a/resources/js_vue3/views/Start.vue +++ /dev/null @@ -1,117 +0,0 @@ - - - diff --git a/resources/js_vue3/views/auth/Login.vue b/resources/js_vue3/views/auth/Login.vue deleted file mode 100644 index e27c1b33..00000000 --- a/resources/js_vue3/views/auth/Login.vue +++ /dev/null @@ -1,156 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js_vue3/views/auth/Register.vue b/resources/js_vue3/views/auth/Register.vue deleted file mode 100644 index f6279138..00000000 --- a/resources/js_vue3/views/auth/Register.vue +++ /dev/null @@ -1,115 +0,0 @@ - - - diff --git a/resources/js_vue3/views/auth/password/Reset.vue b/resources/js_vue3/views/auth/password/Reset.vue deleted file mode 100644 index 48e384c6..00000000 --- a/resources/js_vue3/views/auth/password/Reset.vue +++ /dev/null @@ -1,62 +0,0 @@ - - - diff --git a/resources/js_vue3/views/auth/webauthn/Recover.vue b/resources/js_vue3/views/auth/webauthn/Recover.vue deleted file mode 100644 index 6b91f6a0..00000000 --- a/resources/js_vue3/views/auth/webauthn/Recover.vue +++ /dev/null @@ -1,71 +0,0 @@ - - - diff --git a/resources/js_vue3/views/settings/Account.vue b/resources/js_vue3/views/settings/Account.vue deleted file mode 100644 index cd707e4c..00000000 --- a/resources/js_vue3/views/settings/Account.vue +++ /dev/null @@ -1,145 +0,0 @@ - - - diff --git a/resources/js_vue3/views/settings/Credentials/Edit.vue b/resources/js_vue3/views/settings/Credentials/Edit.vue deleted file mode 100644 index f3b3acea..00000000 --- a/resources/js_vue3/views/settings/Credentials/Edit.vue +++ /dev/null @@ -1,41 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js_vue3/views/settings/OAuth.vue b/resources/js_vue3/views/settings/OAuth.vue deleted file mode 100644 index e08a6fbb..00000000 --- a/resources/js_vue3/views/settings/OAuth.vue +++ /dev/null @@ -1,210 +0,0 @@ - - - diff --git a/resources/js_vue3/views/settings/Options.vue b/resources/js_vue3/views/settings/Options.vue deleted file mode 100644 index ee9beb56..00000000 --- a/resources/js_vue3/views/settings/Options.vue +++ /dev/null @@ -1,199 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js_vue3/views/settings/WebAuthn.vue b/resources/js_vue3/views/settings/WebAuthn.vue deleted file mode 100644 index 9f582cc8..00000000 --- a/resources/js_vue3/views/settings/WebAuthn.vue +++ /dev/null @@ -1,174 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/js_vue3/views/twofaccounts/Import.vue b/resources/js_vue3/views/twofaccounts/Import.vue deleted file mode 100644 index 096b9149..00000000 --- a/resources/js_vue3/views/twofaccounts/Import.vue +++ /dev/null @@ -1,433 +0,0 @@ - - - diff --git a/resources/js_vue3/views/twofaccounts/QRcode.vue b/resources/js_vue3/views/twofaccounts/QRcode.vue deleted file mode 100644 index c0025fe8..00000000 --- a/resources/js_vue3/views/twofaccounts/QRcode.vue +++ /dev/null @@ -1,35 +0,0 @@ - - - \ No newline at end of file diff --git a/resources/views/landing_v3.blade.php b/resources/views/landing_v3.blade.php index 9afe1353..c8de140f 100644 --- a/resources/views/landing_v3.blade.php +++ b/resources/views/landing_v3.blade.php @@ -31,6 +31,6 @@ var isTestingApp = {!! $isTestingApp !!}; var appLocales = {!! $locales !!}; - @vite('resources/js_vue3/app.js') + @vite('resources/js/app.js') \ No newline at end of file diff --git a/vite.config.js b/vite.config.js index 70ed6e58..e41a8f2a 100644 --- a/vite.config.js +++ b/vite.config.js @@ -8,7 +8,7 @@ import version from './vite.version' export default defineConfig({ plugins: [ laravel([ - 'resources/js_vue3/app.js', + 'resources/js/app.js', ]), vue({ template: { @@ -65,7 +65,7 @@ export default defineConfig({ ], resolve: { alias: { - '@': '/resources/js_vue3', + '@': '/resources/js', }, }, build: {