diff --git a/app/Classes/General/Abstracts/AbstractControllerLogic.php b/app/Classes/General/Abstracts/AbstractControllerLogic.php index 2fdd0dab..3bae53c1 100644 --- a/app/Classes/General/Abstracts/AbstractControllerLogic.php +++ b/app/Classes/General/Abstracts/AbstractControllerLogic.php @@ -14,6 +14,8 @@ use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Http\Resources\Json\ResourceCollection; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; +use TimeHunter\LaravelGoogleReCaptchaV3\Facades\GoogleReCaptchaV3; abstract class AbstractControllerLogic { @@ -50,6 +52,7 @@ abstract class AbstractControllerLogic */ public function execute(Request $request) : JsonResponse { + Log::info(GoogleReCaptchaV3::verifyResponse($request->header('captcha-token'))); try { DB::beginTransaction(); diff --git a/app/Http/Controllers/Accounts/UserAuthenticationController.php b/app/Http/Controllers/Accounts/UserAuthenticationController.php index c64cde70..1ce36699 100644 --- a/app/Http/Controllers/Accounts/UserAuthenticationController.php +++ b/app/Http/Controllers/Accounts/UserAuthenticationController.php @@ -17,7 +17,6 @@ class UserAuthenticationController * @return JsonResponse */ public function authenticate(Request $request, AuthenticateUserLogic $logic): JsonResponse { -// dd(GoogleReCaptchaV3::verifyResponse($request->input('gRecaptchaResponse'))); return $logic->execute($request); } diff --git a/config/googlerecaptchav3.php b/config/googlerecaptchav3.php index 11e0232b..29f3b1aa 100644 --- a/config/googlerecaptchav3.php +++ b/config/googlerecaptchav3.php @@ -76,7 +76,7 @@ return [ | - false: the background badge will be invisible | */ - 'background_badge_display' => true, + 'background_badge_display' => false, /* |-------------------------------------------------------------------------- | Background Mode @@ -87,7 +87,7 @@ return [ | - false: the script will only be running if there is action defined | */ - 'background_mode' => false, + 'background_mode' => true, /* |-------------------------------------------------------------------------- diff --git a/resources/assets/vue/app.js b/resources/assets/vue/app.js index d9e2e465..53ff7fc2 100644 --- a/resources/assets/vue/app.js +++ b/resources/assets/vue/app.js @@ -24,6 +24,8 @@ import crudHandler from './general/mixins/crudHandler'; /** Directives */ import closable from './general/directives/closable'; +import { debounce } from 'vue-debounce' + import Avatar from 'vue-avatar'; /** Application Injections */ @@ -37,7 +39,7 @@ Vue.mixin({ route: route }, mixins: [request, crudHandler] - }); +}); /** Components Registrations */ Vue.component(Avatar); @@ -48,7 +50,14 @@ files.keys().map(key => Vue.component(key.split('/').pop().split('.')[0], files( const app = new Vue({ el: '#app', store, + data(){ + return { + captchaId: '', + reCaptchaToken: null + } + }, created(){ + this.init(); this.routesGuard(); if (!this.$store.getters.isAdmin && !('company_marking' in this.$store.getters.getDecodedAccessToken.user) && this.isProtectedRoute() && !this.isWithTokenRoute()) { this.$store.dispatch('crudRequest', {endpoint: this.route('api.account.authentication.refresh'), method: 'get'}).then(response => { @@ -65,7 +74,53 @@ const app = new Vue({ } }, methods: { - route: route + route: route, + init() { + if (!document.getElementById('gRecaptchaScript')) { + + window.gRecaptchaOnLoadCallbacks = [this.render]; + window.gRecaptchaOnLoad = function () { + for (let i = 0; i < window.gRecaptchaOnLoadCallbacks.length; i++) { + window.gRecaptchaOnLoadCallbacks[i](); + } + delete window.gRecaptchaOnLoadCallbacks; + delete window.gRecaptchaOnLoad; + }; + + let recaptchaScript = document.createElement('script'); + recaptchaScript.setAttribute('src', 'https://www.google.com/recaptcha/api.js?render=explicit&onload=gRecaptchaOnLoad'); + recaptchaScript.setAttribute('id', 'gRecaptchaScript'); + recaptchaScript.async = true; + recaptchaScript.defer = true; + document.head.appendChild(recaptchaScript); + + } else if (!window.grecaptcha || !window.grecaptcha.render) { + window.gRecaptchaOnLoadCallbacks.push(this.render); + } else { + this.render(); + } + }, + render() { + this.captchaId = window.grecaptcha.render('grecaptcha_container', { + sitekey: '6LfypGghAAAAAHGmRJrbgwE9v8mgHBINnW6irZx3', + badge: '', + size: 'invisible', + 'expired-callback': this.execute + }); + this.execute(); + }, + execute: debounce(function() { + this.updateCaptchaToken(); + }, 3000), + updateCaptchaToken() { + if(this.$store.getters.getReCaptcha === this.reCaptchaToken){ + window.grecaptcha.execute(this.captchaId).then((token) => { + this.reCaptchaToken = token; + this.$store.dispatch('reCaptcha', {token: token}); + }); + } + + } }, mixins: [guards] }); diff --git a/resources/assets/vue/general/GoogleReCaptchaV3.vue b/resources/assets/vue/general/GoogleReCaptchaV3.vue deleted file mode 100644 index 9ceddc6f..00000000 --- a/resources/assets/vue/general/GoogleReCaptchaV3.vue +++ /dev/null @@ -1,85 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/assets/vue/general/mixins/request.js b/resources/assets/vue/general/mixins/request.js index 731d8ebe..a3c212e8 100644 --- a/resources/assets/vue/general/mixins/request.js +++ b/resources/assets/vue/general/mixins/request.js @@ -30,7 +30,7 @@ export default { console.log(error); this.$store.dispatch('createNotification', {title: 'Unexpected Error', message: 'An unexpected error has occurred. Try again!', type: 'error'}); }).then(() => { - // this.$refs.captcha.execute(); + this.$root.execute(); if (section) { this.$store.dispatch('toggleLoading', {name: section, status: false}) } @@ -46,6 +46,5 @@ export default { }, successHandler(response){}, errorHandler(response){} - } - + }, } \ No newline at end of file diff --git a/resources/assets/vue/vuex/modules/authentication.js b/resources/assets/vue/vuex/modules/authentication.js index f822d26b..dd45f936 100644 --- a/resources/assets/vue/vuex/modules/authentication.js +++ b/resources/assets/vue/vuex/modules/authentication.js @@ -2,6 +2,7 @@ import jwt_decode from "jwt-decode"; export default { state: { resetPasswordEmail: '', + gRecaptchaResponse: null, updatedFullname:localStorage.getItem('updated-fullname') || '', authentication: { access_token: localStorage.getItem('user-token') || '' @@ -56,12 +57,17 @@ export default { }, getResetPasswordEmail: state => state.resetPasswordEmail, - getUpdatedFullname: state => state.updatedFullname + getUpdatedFullname: state => state.updatedFullname, + getReCaptcha: state => state.gRecaptchaResponse + }, mutations: { SET_AUTHENTICATION(state, {access_token}){ state.authentication.access_token = access_token; }, + SET_GOOGLE_RE_CAPTCHA(state, {token}){ + state.gRecaptchaResponse = token; + }, SET_PASSWORD_RESET_EMAIL(state, {email}){ state.resetPasswordEmail = email; }, @@ -83,6 +89,17 @@ export default { window.location.href = redirect_url; }, 2000); }, + reCaptcha(store, {token}){ + store.commit('SET_GOOGLE_RE_CAPTCHA', {token}); + }, + async ensureReCaptchaIsSet(store) { + return new Promise(wait); + + function wait(resolve){ + if (store.state.gRecaptchaResponse) return resolve(store.state.gRecaptchaResponse); + else setTimeout(wait.bind(this, resolve), 30); + } + }, updateToken(store, {access_token}){ localStorage.setItem('user-token', access_token); diff --git a/resources/assets/vue/vuex/modules/crudRequest.js b/resources/assets/vue/vuex/modules/crudRequest.js index f4f6c37f..67df100e 100644 --- a/resources/assets/vue/vuex/modules/crudRequest.js +++ b/resources/assets/vue/vuex/modules/crudRequest.js @@ -1,23 +1,26 @@ export default { actions: { crudRequest({getters, dispatch}, {endpoint, method, parameters}){ - return fetch(endpoint, { - method: method, - responseType: 'json', - body: parameters ? JSON.stringify(parameters):null, - headers: { - 'content-type': 'application/json', - 'Authorization': 'Bearer '+getters.getAccessToken - } - }).then(response => { + return dispatch('ensureReCaptchaIsSet').then(function () { + return fetch(endpoint, { + method: method, + responseType: 'json', + body: parameters ? JSON.stringify(parameters):null, + headers: { + 'content-type': 'application/json', + 'Authorization': 'Bearer '+getters.getAccessToken, + 'captcha-token': getters.getReCaptcha + } + }).then(response => { - if(response.status === 401 && window.location.href !== route('login')){ - dispatch('userAuthentication', {access_token: '', redirect_url: '/'}); - } + if(response.status === 401 && window.location.href !== route('login')){ + dispatch('userAuthentication', {access_token: '', redirect_url: '/'}); + } - return response; + return response; - }) + }) + }); } } } \ No newline at end of file diff --git a/resources/views/layouts/base.blade.php b/resources/views/layouts/base.blade.php index ebc726e3..c820242e 100644 --- a/resources/views/layouts/base.blade.php +++ b/resources/views/layouts/base.blade.php @@ -3,12 +3,18 @@
@include('vendor/head') +