diff --git a/src/assets/js/iframe-recaptcha.js b/src/assets/js/iframe-recaptcha.js
deleted file mode 100644
index f1ff5e843..000000000
--- a/src/assets/js/iframe-recaptcha.js
+++ /dev/null
@@ -1,42 +0,0 @@
-// (C) Copyright 2015 Moodle Pty Ltd.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-(function () {
- var url = location.href;
-
- if (!url.match(/^https?:\/\//i) || !url.match(/\/webservice\/recaptcha\.php/i)) {
- // Not the recaptcha script, stop.
- return;
- }
-
- // Define recaptcha callbacks.
- window.recaptchacallback = function(value) {
- window.parent.postMessage({
- environment: 'moodleapp',
- context: 'recaptcha',
- action: 'callback',
- frameUrl: location.href,
- value: value,
- }, '*');
- };
-
- window.recaptchaexpiredcallback = function() {
- window.parent.postMessage({
- environment: 'moodleapp',
- context: 'recaptcha',
- action: 'expired',
- frameUrl: location.href,
- }, '*');
- };
-})();
\ No newline at end of file
diff --git a/src/core/components/components.module.ts b/src/core/components/components.module.ts
index f1a876dc6..93c356a3c 100644
--- a/src/core/components/components.module.ts
+++ b/src/core/components/components.module.ts
@@ -44,7 +44,6 @@ import { CoreNavBarButtonsComponent } from './navbar-buttons/navbar-buttons';
import { CoreNavigationBarComponent } from './navigation-bar/navigation-bar';
import { CoreProgressBarComponent } from './progress-bar/progress-bar';
import { CoreRecaptchaComponent } from './recaptcha/recaptcha';
-import { CoreRecaptchaModalComponent } from './recaptcha/recaptcha-modal';
import { CoreSendMessageFormComponent } from './send-message-form/send-message-form';
import { CoreShowPasswordComponent } from './show-password/show-password';
import { CoreSitePickerComponent } from './site-picker/site-picker';
@@ -84,7 +83,6 @@ import { CoreHorizontalScrollControlsComponent } from './horizontal-scroll-contr
CoreNavigationBarComponent,
CoreProgressBarComponent,
CoreRecaptchaComponent,
- CoreRecaptchaModalComponent,
CoreSendMessageFormComponent,
CoreShowPasswordComponent,
CoreSitePickerComponent,
@@ -131,7 +129,6 @@ import { CoreHorizontalScrollControlsComponent } from './horizontal-scroll-contr
CoreNavigationBarComponent,
CoreProgressBarComponent,
CoreRecaptchaComponent,
- CoreRecaptchaModalComponent,
CoreSendMessageFormComponent,
CoreShowPasswordComponent,
CoreSitePickerComponent,
diff --git a/src/core/components/recaptcha/core-recaptcha-modal.html b/src/core/components/recaptcha/core-recaptcha-modal.html
deleted file mode 100644
index 9d8f70aa6..000000000
--- a/src/core/components/recaptcha/core-recaptcha-modal.html
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
- {{ 'core.login.security_question' | translate }}
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/core/components/recaptcha/recaptcha-modal.ts b/src/core/components/recaptcha/recaptcha-modal.ts
deleted file mode 100644
index 0cb9215cf..000000000
--- a/src/core/components/recaptcha/recaptcha-modal.ts
+++ /dev/null
@@ -1,126 +0,0 @@
-// (C) Copyright 2015 Moodle Pty Ltd.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import { Component, Input, OnDestroy } from '@angular/core';
-
-import { ModalController } from '@singletons';
-
-/**
- * Component to display a the recaptcha in a modal.
- */
-@Component({
- selector: 'core-recaptcha-modal',
- templateUrl: 'core-recaptcha-modal.html',
-})
-export class CoreRecaptchaModalComponent implements OnDestroy {
-
- @Input() recaptchaUrl?: string;
-
- expired = false;
- value = '';
-
- protected messageListenerFunction: (event: MessageEvent) => Promise;
-
- constructor() {
- // Listen for messages from the iframe.
- this.messageListenerFunction = this.onIframeMessage.bind(this);
- window.addEventListener('message', this.messageListenerFunction);
- }
-
- /**
- * Close modal.
- */
- closeModal(): void {
- ModalController.dismiss({
- expired: this.expired,
- value: this.value,
- });
- }
-
- /**
- * The iframe with the recaptcha was loaded.
- *
- * @param iframe Iframe element.
- */
- loaded(iframe: HTMLIFrameElement): void {
- // Search the iframe content.
- const contentWindow = iframe?.contentWindow;
-
- if (contentWindow) {
- try {
- // Set the callbacks we're interested in.
- contentWindow['recaptchacallback'] = this.onRecaptchaCallback.bind(this);
- contentWindow['recaptchaexpiredcallback'] = this.onRecaptchaExpiredCallback.bind(this);
- } catch (error) {
- // Cannot access the window.
- }
- }
- }
-
- /**
- * Treat an iframe message event.
- *
- * @param event Event.
- * @return Promise resolved when done.
- */
- protected async onIframeMessage(event: MessageEvent): Promise {
- if (!event.data || event.data.environment != 'moodleapp' || event.data.context != 'recaptcha') {
- return;
- }
-
- switch (event.data.action) {
- case 'callback':
- this.onRecaptchaCallback(event.data.value);
- break;
- case 'expired':
- this.onRecaptchaExpiredCallback();
- break;
-
- default:
- break;
- }
- }
-
- /**
- * Recapcha callback called.
- *
- * @param value Value received.
- */
- protected onRecaptchaCallback(value: string): void {
- this.expired = false;
- this.value = value;
- this.closeModal();
- }
-
- /**
- * Recapcha expired callback called.
- */
- protected onRecaptchaExpiredCallback(): void {
- this.expired = true;
- this.value = '';
- }
-
- /**
- * Component destroyed.
- */
- ngOnDestroy(): void {
- window.removeEventListener('message', this.messageListenerFunction);
- }
-
-}
-
-export type CoreRecaptchaModalReturn = {
- expired: boolean;
- value: string;
-};
diff --git a/src/core/components/recaptcha/recaptcha.ts b/src/core/components/recaptcha/recaptcha.ts
index 7d58839d4..987acf1b0 100644
--- a/src/core/components/recaptcha/recaptcha.ts
+++ b/src/core/components/recaptcha/recaptcha.ts
@@ -16,9 +16,8 @@ import { Component, Input, OnInit } from '@angular/core';
import { CoreLang } from '@services/lang';
import { CoreSites } from '@services/sites';
-import { CoreDomUtils } from '@services/utils/dom';
import { CoreTextUtils } from '@services/utils/text';
-import { CoreRecaptchaModalComponent, CoreRecaptchaModalReturn } from './recaptcha-modal';
+import { CoreUtils } from '@services/utils/utils';
/**
* Component that allows answering a recaptcha.
@@ -57,27 +56,49 @@ export class CoreRecaptchaComponent implements OnInit {
}
/**
- * Open the recaptcha modal.
+ * Let the user answer the recaptcha.
*/
async answerRecaptcha(): Promise {
- // Set the iframe src. We use an iframe because reCaptcha V2 doesn't work with file:// protocol.
+ // Open the recaptcha challenge in an InAppBrowser.
+ // The app used to use an iframe for this, but the app can no longer access the iframe to create the required callbacks.
+ // The app cannot render the recaptcha directly because it has problems with the local protocols and domains.
const src = CoreTextUtils.concatenatePaths(this.siteUrl!, 'webservice/recaptcha.php?lang=' + this.lang);
- // Modal to answer the recaptcha.
- // This is because the size of the recaptcha is dynamic, so it could cause problems if it was displayed inline.
+ const inAppBrowserWindow = CoreUtils.openInApp(src);
+ if (!inAppBrowserWindow) {
+ return;
+ }
- const modalData = await CoreDomUtils.openModal({
- component: CoreRecaptchaModalComponent,
- cssClass: 'core-modal-fullscreen',
- componentProps: {
- recaptchaUrl: src,
- },
+ // Set the callbacks once the page is loaded.
+ const loadStopSubscription = inAppBrowserWindow.on('loadstop').subscribe(() => {
+ inAppBrowserWindow.executeScript({
+ code:
+ 'window.recaptchacallback = (value) => webkit.messageHandlers.cordova_iab.postMessage(' +
+ 'JSON.stringify({ action: "callback", value }));' +
+ 'window.recaptchaexpiredcallback = () => webkit.messageHandlers.cordova_iab.postMessage(' +
+ 'JSON.stringify({ action: "expired" }));',
+ });
});
- if (modalData) {
- this.expired = modalData.expired;
- this.model![this.modelValueName] = modalData.value;
- }
+ // Listen for events.
+ const messageSubscription = inAppBrowserWindow.on('message').subscribe((event) => {
+ if (!event.data) {
+ return;
+ }
+
+ if (event.data.action == 'expired') {
+ this.expired = true;
+ this.model![this.modelValueName] = '';
+ } else if (event.data.action == 'callback') {
+ this.expired = false;
+ this.model![this.modelValueName] = event.data.value;
+
+ // Close the InAppBrowser now.
+ inAppBrowserWindow.close();
+ messageSubscription.unsubscribe();
+ loadStopSubscription.unsubscribe();
+ }
+ });
}
}
diff --git a/src/core/services/utils/iframe.ts b/src/core/services/utils/iframe.ts
index 1aa40105a..800883601 100644
--- a/src/core/services/utils/iframe.ts
+++ b/src/core/services/utils/iframe.ts
@@ -516,14 +516,8 @@ export class CoreIframeUtilsProvider {
private injectiOSScripts(userScriptWindow: WKUserScriptWindow) {
const wwwPath = CoreFile.getWWWAbsolutePath();
const linksPath = CoreTextUtils.concatenatePaths(wwwPath, 'assets/js/iframe-treat-links.js');
- const recaptchaPath = CoreTextUtils.concatenatePaths(wwwPath, 'assets/js/iframe-recaptcha.js');
userScriptWindow.WKUserScript?.addScript({ id: 'CoreIframeUtilsLinksScript', file: linksPath });
- userScriptWindow.WKUserScript?.addScript({
- id: 'CoreIframeUtilsRecaptchaScript',
- file: recaptchaPath,
- injectionTime: userScriptWindow.WKUserScript?.InjectionTime.END,
- });
// Handle post messages received by iframes.
window.addEventListener('message', this.handleIframeMessage.bind(this));