diff --git a/composer.json b/composer.json index 6fca6f0..67f3785 100644 --- a/composer.json +++ b/composer.json @@ -7,10 +7,11 @@ ], "license": "MIT", "require": { - "php": ">=7.1.0", + "php": ">=8.0.0", "bacon/bacon-qr-code": "^2.0", - "pragmarx/recovery": "^0.1.0", - "pragmarx/google2fa-laravel": "^0.2.0" + "pragmarx/google2fa-laravel": "^2.0.1", + "pragmarx/google2fa-qrcode": "^3.0", + "pragmarx/recovery": "^0.2.0" }, "autoload": { "psr-4": { diff --git a/config/lifeonscreen2fa.php b/config/lifeonscreen2fa.php index d40dd13..6ae6413 100644 --- a/config/lifeonscreen2fa.php +++ b/config/lifeonscreen2fa.php @@ -41,13 +41,5 @@ * Number of characters in each block in recovery code. */ 'chars_in_block' => 16, - - /** - * The following algorithms are currently supported: - * - PASSWORD_DEFAULT - * - PASSWORD_BCRYPT - * - PASSWORD_ARGON2I // available from php 7.2 - */ - 'hashing_algorithm' => PASSWORD_BCRYPT, ], -]; \ No newline at end of file +]; diff --git a/dist/css/tool.css b/dist/css/tool.css new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/dist/css/tool.css @@ -0,0 +1 @@ + diff --git a/dist/js/tool.js b/dist/js/tool.js new file mode 100644 index 0000000..947b2e9 --- /dev/null +++ b/dist/js/tool.js @@ -0,0 +1,1297 @@ +/******/ (() => { // webpackBootstrap +/******/ var __webpack_modules__ = ({ + +/***/ "./node_modules/babel-loader/lib/index.js??clonedRuleSet-5[0].rules[0].use[0]!./node_modules/vue-loader/lib/index.js??vue-loader-options!./resources/js/components/RecoveryCodesModal.vue?vue&type=script&lang=js&": +/*!*************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/babel-loader/lib/index.js??clonedRuleSet-5[0].rules[0].use[0]!./node_modules/vue-loader/lib/index.js??vue-loader-options!./resources/js/components/RecoveryCodesModal.vue?vue&type=script&lang=js& ***! + \*************************************************************************************************************************************************************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ + name: "RecoveryCodesModal", + data: function data() { + return { + codes: null, + confirming: false, + password: '', + errors: {} + }; + }, + methods: { + handleClose: function handleClose() { + this.$emit('close'); + }, + handleConfirm: function handleConfirm() { + this.$emit('confirm'); + }, + handleSubmit: function handleSubmit() { + var _this = this; + + this.confirming = true; + this.errors = {}; + Nova.request().post('/los/2fa/unlocked-recovery-codes', { + password: this.password + }).then(function (_ref) { + var data = _ref.data; + _this.codes = data.recovery_codes; + })["catch"](function (_ref2) { + var response = _ref2.response; + _this.errors = response.data.errors; + })["finally"](function () { + _this.confirming = false; + }); + }, + hideCodes: function hideCodes() { + this.codes = null; + this.handleClose(); + } + }, + computed: { + codesText: function codesText() { + return this.codes.join("\n"); + } + }, + mounted: function mounted() { + this.$refs.passwordInput.focus(); + } +}); + +/***/ }), + +/***/ "./node_modules/babel-loader/lib/index.js??clonedRuleSet-5[0].rules[0].use[0]!./node_modules/vue-loader/lib/index.js??vue-loader-options!./resources/js/components/Tool.vue?vue&type=script&lang=js&": +/*!***********************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/babel-loader/lib/index.js??clonedRuleSet-5[0].rules[0].use[0]!./node_modules/vue-loader/lib/index.js??vue-loader-options!./resources/js/components/Tool.vue?vue&type=script&lang=js& ***! + \***********************************************************************************************************************************************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _RecoveryCodesModal_vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./RecoveryCodesModal.vue */ "./resources/js/components/RecoveryCodesModal.vue"); +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ + components: { + RecoveryCodesModal: _RecoveryCodesModal_vue__WEBPACK_IMPORTED_MODULE_0__["default"] + }, + metaInfo: function metaInfo() { + return { + title: 'Nova Two Factor' + }; + }, + data: function data() { + return { + showRecoveryCodesModal: false + }; + }, + mounted: function mounted() {// + }, + methods: { + openModal: function openModal() { + this.showRecoveryCodesModal = true; + }, + closeModal: function closeModal() { + this.showRecoveryCodesModal = false; + }, + confirmModal: function confirmModal() { + this.closeModal(); + } + } +}); + +/***/ }), + +/***/ "./resources/js/tool.js": +/*!******************************!*\ + !*** ./resources/js/tool.js ***! + \******************************/ +/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { + +Nova.booting(function (Vue, router, store) { + router.addRoutes([{ + name: '2fa-recovery-codes', + path: '/recovery-codes', + component: (__webpack_require__(/*! ./components/Tool.vue */ "./resources/js/components/Tool.vue")["default"]) + }]); +}); + +/***/ }), + +/***/ "./node_modules/css-loader/dist/cjs.js??clonedRuleSet-9[0].rules[0].use[1]!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-9[0].rules[0].use[2]!./node_modules/vue-loader/lib/index.js??vue-loader-options!./resources/js/components/Tool.vue?vue&type=style&index=0&lang=css&": +/*!*******************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/css-loader/dist/cjs.js??clonedRuleSet-9[0].rules[0].use[1]!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-9[0].rules[0].use[2]!./node_modules/vue-loader/lib/index.js??vue-loader-options!./resources/js/components/Tool.vue?vue&type=style&index=0&lang=css& ***! + \*******************************************************************************************************************************************************************************************************************************************************************************************************************************************************/ +/***/ ((module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js"); +/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__); +// Imports + +var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()(function(i){return i[1]}); +// Module +___CSS_LOADER_EXPORT___.push([module.id, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n/* Scoped Styles */\n", ""]); +// Exports +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); + + +/***/ }), + +/***/ "./node_modules/css-loader/dist/runtime/api.js": +/*!*****************************************************!*\ + !*** ./node_modules/css-loader/dist/runtime/api.js ***! + \*****************************************************/ +/***/ ((module) => { + +"use strict"; + + +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +// css base code, injected by the css-loader +// eslint-disable-next-line func-names +module.exports = function (cssWithMappingToString) { + var list = []; // return the list of modules as css string + + list.toString = function toString() { + return this.map(function (item) { + var content = cssWithMappingToString(item); + + if (item[2]) { + return "@media ".concat(item[2], " {").concat(content, "}"); + } + + return content; + }).join(""); + }; // import a list of modules into the list + // eslint-disable-next-line func-names + + + list.i = function (modules, mediaQuery, dedupe) { + if (typeof modules === "string") { + // eslint-disable-next-line no-param-reassign + modules = [[null, modules, ""]]; + } + + var alreadyImportedModules = {}; + + if (dedupe) { + for (var i = 0; i < this.length; i++) { + // eslint-disable-next-line prefer-destructuring + var id = this[i][0]; + + if (id != null) { + alreadyImportedModules[id] = true; + } + } + } + + for (var _i = 0; _i < modules.length; _i++) { + var item = [].concat(modules[_i]); + + if (dedupe && alreadyImportedModules[item[0]]) { + // eslint-disable-next-line no-continue + continue; + } + + if (mediaQuery) { + if (!item[2]) { + item[2] = mediaQuery; + } else { + item[2] = "".concat(mediaQuery, " and ").concat(item[2]); + } + } + + list.push(item); + } + }; + + return list; +}; + +/***/ }), + +/***/ "./resources/sass/tool.scss": +/*!**********************************!*\ + !*** ./resources/sass/tool.scss ***! + \**********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +// extracted by mini-css-extract-plugin + + +/***/ }), + +/***/ "./node_modules/style-loader/dist/cjs.js!./node_modules/css-loader/dist/cjs.js??clonedRuleSet-9[0].rules[0].use[1]!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-9[0].rules[0].use[2]!./node_modules/vue-loader/lib/index.js??vue-loader-options!./resources/js/components/Tool.vue?vue&type=style&index=0&lang=css&": +/*!***********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/style-loader/dist/cjs.js!./node_modules/css-loader/dist/cjs.js??clonedRuleSet-9[0].rules[0].use[1]!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-9[0].rules[0].use[2]!./node_modules/vue-loader/lib/index.js??vue-loader-options!./resources/js/components/Tool.vue?vue&type=style&index=0&lang=css& ***! + \***********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! !../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js"); +/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _node_modules_css_loader_dist_cjs_js_clonedRuleSet_9_0_rules_0_use_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_dist_cjs_js_clonedRuleSet_9_0_rules_0_use_2_node_modules_vue_loader_lib_index_js_vue_loader_options_Tool_vue_vue_type_style_index_0_lang_css___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! !!../../../node_modules/css-loader/dist/cjs.js??clonedRuleSet-9[0].rules[0].use[1]!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-9[0].rules[0].use[2]!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Tool.vue?vue&type=style&index=0&lang=css& */ "./node_modules/css-loader/dist/cjs.js??clonedRuleSet-9[0].rules[0].use[1]!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-9[0].rules[0].use[2]!./node_modules/vue-loader/lib/index.js??vue-loader-options!./resources/js/components/Tool.vue?vue&type=style&index=0&lang=css&"); + + + +var options = {}; + +options.insert = "head"; +options.singleton = false; + +var update = _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default()(_node_modules_css_loader_dist_cjs_js_clonedRuleSet_9_0_rules_0_use_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_dist_cjs_js_clonedRuleSet_9_0_rules_0_use_2_node_modules_vue_loader_lib_index_js_vue_loader_options_Tool_vue_vue_type_style_index_0_lang_css___WEBPACK_IMPORTED_MODULE_1__["default"], options); + + + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_node_modules_css_loader_dist_cjs_js_clonedRuleSet_9_0_rules_0_use_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_dist_cjs_js_clonedRuleSet_9_0_rules_0_use_2_node_modules_vue_loader_lib_index_js_vue_loader_options_Tool_vue_vue_type_style_index_0_lang_css___WEBPACK_IMPORTED_MODULE_1__["default"].locals || {}); + +/***/ }), + +/***/ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js": +/*!****************************************************************************!*\ + !*** ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js ***! + \****************************************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; + + +var isOldIE = function isOldIE() { + var memo; + return function memorize() { + if (typeof memo === 'undefined') { + // Test for IE <= 9 as proposed by Browserhacks + // @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805 + // Tests for existence of standard globals is to allow style-loader + // to operate correctly into non-standard environments + // @see https://github.com/webpack-contrib/style-loader/issues/177 + memo = Boolean(window && document && document.all && !window.atob); + } + + return memo; + }; +}(); + +var getTarget = function getTarget() { + var memo = {}; + return function memorize(target) { + if (typeof memo[target] === 'undefined') { + var styleTarget = document.querySelector(target); // Special case to return head of iframe instead of iframe itself + + if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) { + try { + // This will throw an exception if access to iframe is blocked + // due to cross-origin restrictions + styleTarget = styleTarget.contentDocument.head; + } catch (e) { + // istanbul ignore next + styleTarget = null; + } + } + + memo[target] = styleTarget; + } + + return memo[target]; + }; +}(); + +var stylesInDom = []; + +function getIndexByIdentifier(identifier) { + var result = -1; + + for (var i = 0; i < stylesInDom.length; i++) { + if (stylesInDom[i].identifier === identifier) { + result = i; + break; + } + } + + return result; +} + +function modulesToDom(list, options) { + var idCountMap = {}; + var identifiers = []; + + for (var i = 0; i < list.length; i++) { + var item = list[i]; + var id = options.base ? item[0] + options.base : item[0]; + var count = idCountMap[id] || 0; + var identifier = "".concat(id, " ").concat(count); + idCountMap[id] = count + 1; + var index = getIndexByIdentifier(identifier); + var obj = { + css: item[1], + media: item[2], + sourceMap: item[3] + }; + + if (index !== -1) { + stylesInDom[index].references++; + stylesInDom[index].updater(obj); + } else { + stylesInDom.push({ + identifier: identifier, + updater: addStyle(obj, options), + references: 1 + }); + } + + identifiers.push(identifier); + } + + return identifiers; +} + +function insertStyleElement(options) { + var style = document.createElement('style'); + var attributes = options.attributes || {}; + + if (typeof attributes.nonce === 'undefined') { + var nonce = true ? __webpack_require__.nc : 0; + + if (nonce) { + attributes.nonce = nonce; + } + } + + Object.keys(attributes).forEach(function (key) { + style.setAttribute(key, attributes[key]); + }); + + if (typeof options.insert === 'function') { + options.insert(style); + } else { + var target = getTarget(options.insert || 'head'); + + if (!target) { + throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid."); + } + + target.appendChild(style); + } + + return style; +} + +function removeStyleElement(style) { + // istanbul ignore if + if (style.parentNode === null) { + return false; + } + + style.parentNode.removeChild(style); +} +/* istanbul ignore next */ + + +var replaceText = function replaceText() { + var textStore = []; + return function replace(index, replacement) { + textStore[index] = replacement; + return textStore.filter(Boolean).join('\n'); + }; +}(); + +function applyToSingletonTag(style, index, remove, obj) { + var css = remove ? '' : obj.media ? "@media ".concat(obj.media, " {").concat(obj.css, "}") : obj.css; // For old IE + + /* istanbul ignore if */ + + if (style.styleSheet) { + style.styleSheet.cssText = replaceText(index, css); + } else { + var cssNode = document.createTextNode(css); + var childNodes = style.childNodes; + + if (childNodes[index]) { + style.removeChild(childNodes[index]); + } + + if (childNodes.length) { + style.insertBefore(cssNode, childNodes[index]); + } else { + style.appendChild(cssNode); + } + } +} + +function applyToTag(style, options, obj) { + var css = obj.css; + var media = obj.media; + var sourceMap = obj.sourceMap; + + if (media) { + style.setAttribute('media', media); + } else { + style.removeAttribute('media'); + } + + if (sourceMap && typeof btoa !== 'undefined') { + css += "\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), " */"); + } // For old IE + + /* istanbul ignore if */ + + + if (style.styleSheet) { + style.styleSheet.cssText = css; + } else { + while (style.firstChild) { + style.removeChild(style.firstChild); + } + + style.appendChild(document.createTextNode(css)); + } +} + +var singleton = null; +var singletonCounter = 0; + +function addStyle(obj, options) { + var style; + var update; + var remove; + + if (options.singleton) { + var styleIndex = singletonCounter++; + style = singleton || (singleton = insertStyleElement(options)); + update = applyToSingletonTag.bind(null, style, styleIndex, false); + remove = applyToSingletonTag.bind(null, style, styleIndex, true); + } else { + style = insertStyleElement(options); + update = applyToTag.bind(null, style, options); + + remove = function remove() { + removeStyleElement(style); + }; + } + + update(obj); + return function updateStyle(newObj) { + if (newObj) { + if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap) { + return; + } + + update(obj = newObj); + } else { + remove(); + } + }; +} + +module.exports = function (list, options) { + options = options || {}; // Force single-tag solution on IE6-9, which has a hard limit on the # of diff --git a/resources/js/components/Tool.vue b/resources/js/components/Tool.vue new file mode 100644 index 0000000..fde2224 --- /dev/null +++ b/resources/js/components/Tool.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/resources/js/tool.js b/resources/js/tool.js new file mode 100644 index 0000000..48aeb70 --- /dev/null +++ b/resources/js/tool.js @@ -0,0 +1,9 @@ +Nova.booting((Vue, router, store) => { + router.addRoutes([ + { + name: '2fa-recovery-codes', + path: '/recovery-codes', + component: require('./components/Tool.vue').default, + }, + ]) +}) diff --git a/resources/sass/tool.scss b/resources/sass/tool.scss new file mode 100644 index 0000000..f85ad40 --- /dev/null +++ b/resources/sass/tool.scss @@ -0,0 +1 @@ +// Nova Tool CSS diff --git a/resources/views/authenticate.blade.php b/resources/views/authenticate.blade.php index b93f525..38b2493 100644 --- a/resources/views/authenticate.blade.php +++ b/resources/views/authenticate.blade.php @@ -10,24 +10,32 @@ - @@ -48,38 +56,55 @@ function checkAutoSubmit(el) { Two factor authentication protects against phishing, social engineering and password brute force attacks and secures your logins from attackers exploiting weak or stolen credentials.

-

Enter the pin from Google Authenticator Enable 2FA

+

Enter the pin from your Authenticator app

@if (isset($error))

{{ $error }} -

@endif +
- +
+
- +
+ + + +
diff --git a/resources/views/navigation.blade.php b/resources/views/navigation.blade.php new file mode 100644 index 0000000..2fdca84 --- /dev/null +++ b/resources/views/navigation.blade.php @@ -0,0 +1,6 @@ + + + + Recovery Codes + + diff --git a/resources/views/recovery.blade.php b/resources/views/recovery.blade.php index 40f9e33..f1d54dd 100644 --- a/resources/views/recovery.blade.php +++ b/resources/views/recovery.blade.php @@ -9,25 +9,6 @@ - -
diff --git a/resources/views/register.blade.php b/resources/views/register.blade.php index 6b7ba8a..0200970 100644 --- a/resources/views/register.blade.php +++ b/resources/views/register.blade.php @@ -10,17 +10,6 @@ -