diff --git a/admin/tool/mfa/factor/webauthn/amd/build/register.min.js b/admin/tool/mfa/factor/webauthn/amd/build/register.min.js
index 5407918eb3c..a1dd0d602be 100644
--- a/admin/tool/mfa/factor/webauthn/amd/build/register.min.js
+++ b/admin/tool/mfa/factor/webauthn/amd/build/register.min.js
@@ -6,6 +6,6 @@
  * @author     Alex Morris <alex.morris@catalyst.net.nz>
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-define("factor_webauthn/register",["factor_webauthn/utils","core/log"],(function(utils,Log){async function registerSecurityKey(createArgs){try{if(!navigator.credentials||!navigator.credentials.create)throw new Error("Browser not supported.");if(!1===createArgs.success)throw new Error(createArgs.msg||"unknown error occurred");utils.recursiveBase64StrToArrayBuffer(createArgs);const cred=await navigator.credentials.create(createArgs),authenticatorResponse={transports:cred.response.getTransports?cred.response.getTransports():null,clientDataJSON:cred.response.clientDataJSON?utils.arrayBufferToBase64(cred.response.clientDataJSON):null,attestationObject:cred.response.attestationObject?utils.arrayBufferToBase64(cred.response.attestationObject):null};document.getElementById("id_response_input").value=JSON.stringify(authenticatorResponse),document.getElementById("id_submitbutton").disabled=!1}catch(e){Log.debug("The request timed out or you have canceled the request. Please try again later.")}}return{init:function(createArgs){document.getElementById("id_submitbutton").disabled=!0,createArgs=JSON.parse(createArgs),document.getElementById("factor_webauthn-register").addEventListener("click",(function(){registerSecurityKey(createArgs)})),document.getElementById("factor_webauthn-register").addEventListener("keypress",(function(){registerSecurityKey(createArgs)}))}}}));
+define("factor_webauthn/register",["factor_webauthn/utils","core/log","core/prefetch","core/str","core/toast"],(function(utils,Log,Prefetch,Str,Toast){async function registerSecurityKey(createArgs){try{if(!navigator.credentials||!navigator.credentials.create)throw new Error("Browser not supported.");if(!1===createArgs.success)throw new Error(createArgs.msg||"unknown error occurred");utils.recursiveBase64StrToArrayBuffer(createArgs);const cred=await navigator.credentials.create(createArgs),authenticatorResponse={transports:cred.response.getTransports?cred.response.getTransports():null,clientDataJSON:cred.response.clientDataJSON?utils.arrayBufferToBase64(cred.response.clientDataJSON):null,attestationObject:cred.response.attestationObject?utils.arrayBufferToBase64(cred.response.attestationObject):null},registerSuccess=await Str.getString("registersuccess","factor_webauthn");await Toast.add(registerSuccess,{type:"success"}),document.getElementById("id_response_input").value=JSON.stringify(authenticatorResponse),document.getElementById("id_submitbutton").disabled=!1}catch(e){Log.debug("The request timed out or you have canceled the request. Please try again later.")}}return{init:function(createArgs){document.getElementById("id_submitbutton").disabled=!0,Prefetch.prefetchStrings("factor_webauthn",["registersuccess"]),createArgs=JSON.parse(createArgs),document.getElementById("factor_webauthn-register").addEventListener("click",(function(){registerSecurityKey(createArgs)})),document.getElementById("factor_webauthn-register").addEventListener("keypress",(function(){registerSecurityKey(createArgs)}))}}}));
 
 //# sourceMappingURL=register.min.js.map
\ No newline at end of file
diff --git a/admin/tool/mfa/factor/webauthn/amd/build/register.min.js.map b/admin/tool/mfa/factor/webauthn/amd/build/register.min.js.map
index 4e97dbce344..c2a40cc4c9e 100644
--- a/admin/tool/mfa/factor/webauthn/amd/build/register.min.js.map
+++ b/admin/tool/mfa/factor/webauthn/amd/build/register.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"register.min.js","sources":["../src/register.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * For collecting WebAuthn authenticator details on factor setup\n *\n * @module     factor_webauthn/register\n * @copyright  Catalyst IT\n * @author     Alex Morris <alex.morris@catalyst.net.nz>\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['factor_webauthn/utils', 'core/log'], function(utils, Log) {\n    /**\n     * Register the security key.\n     *\n     * @param {*} createArgs\n     */\n    async function registerSecurityKey(createArgs) {\n        try {\n            if (!navigator.credentials || !navigator.credentials.create) {\n                throw new Error('Browser not supported.');\n            }\n\n            if (createArgs.success === false) {\n                throw new Error(createArgs.msg || 'unknown error occurred');\n            }\n\n            utils.recursiveBase64StrToArrayBuffer(createArgs);\n            const cred = await navigator.credentials.create(createArgs);\n            const authenticatorResponse = {\n                transports: cred.response.getTransports ? cred.response.getTransports() : null,\n                clientDataJSON: cred.response.clientDataJSON ?\n                    utils.arrayBufferToBase64(cred.response.clientDataJSON) : null,\n                attestationObject: cred.response.attestationObject ?\n                    utils.arrayBufferToBase64(cred.response.attestationObject) : null,\n            };\n            document.getElementById('id_response_input').value = JSON.stringify(authenticatorResponse);\n            // Enable the submit button so that we can proceed.\n            document.getElementById('id_submitbutton').disabled = false;\n        } catch (e) {\n            Log.debug('The request timed out or you have canceled the request. Please try again later.');\n        }\n    }\n\n    return {\n        init: function(createArgs) {\n            // Disable the submit button until we have registered a security key.\n            document.getElementById('id_submitbutton').disabled = true;\n            createArgs = JSON.parse(createArgs);\n            // Register event listeners.\n            document.getElementById('factor_webauthn-register').addEventListener('click', function() {\n                registerSecurityKey(createArgs);\n            });\n            document.getElementById('factor_webauthn-register').addEventListener('keypress', function() {\n                registerSecurityKey(createArgs);\n            });\n        }\n    };\n});\n"],"names":["define","utils","Log","registerSecurityKey","createArgs","navigator","credentials","create","Error","success","msg","recursiveBase64StrToArrayBuffer","cred","authenticatorResponse","transports","response","getTransports","clientDataJSON","arrayBufferToBase64","attestationObject","document","getElementById","value","JSON","stringify","disabled","e","debug","init","parse","addEventListener"],"mappings":";;;;;;;;AAwBAA,kCAAO,CAAC,wBAAyB,aAAa,SAASC,MAAOC,oBAM3CC,oBAAoBC,oBAEtBC,UAAUC,cAAgBD,UAAUC,YAAYC,aAC3C,IAAIC,MAAM,8BAGO,IAAvBJ,WAAWK,cACL,IAAID,MAAMJ,WAAWM,KAAO,0BAGtCT,MAAMU,gCAAgCP,kBAChCQ,WAAaP,UAAUC,YAAYC,OAAOH,YAC1CS,sBAAwB,CAC1BC,WAAYF,KAAKG,SAASC,cAAgBJ,KAAKG,SAASC,gBAAkB,KAC1EC,eAAgBL,KAAKG,SAASE,eAC1BhB,MAAMiB,oBAAoBN,KAAKG,SAASE,gBAAkB,KAC9DE,kBAAmBP,KAAKG,SAASI,kBAC7BlB,MAAMiB,oBAAoBN,KAAKG,SAASI,mBAAqB,MAErEC,SAASC,eAAe,qBAAqBC,MAAQC,KAAKC,UAAUX,uBAEpEO,SAASC,eAAe,mBAAmBI,UAAW,EACxD,MAAOC,GACLxB,IAAIyB,MAAM,0FAIX,CACHC,KAAM,SAASxB,YAEXgB,SAASC,eAAe,mBAAmBI,UAAW,EACtDrB,WAAamB,KAAKM,MAAMzB,YAExBgB,SAASC,eAAe,4BAA4BS,iBAAiB,SAAS,WAC1E3B,oBAAoBC,eAExBgB,SAASC,eAAe,4BAA4BS,iBAAiB,YAAY,WAC7E3B,oBAAoBC"}
\ No newline at end of file
+{"version":3,"file":"register.min.js","sources":["../src/register.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * For collecting WebAuthn authenticator details on factor setup\n *\n * @module     factor_webauthn/register\n * @copyright  Catalyst IT\n * @author     Alex Morris <alex.morris@catalyst.net.nz>\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine([\n    'factor_webauthn/utils',\n    'core/log',\n    'core/prefetch',\n    'core/str',\n    'core/toast',\n], function(\n    utils,\n    Log,\n    Prefetch,\n    Str,\n    Toast,\n) {\n\n    /**\n     * Register the security key.\n     *\n     * @param {*} createArgs\n     */\n    async function registerSecurityKey(createArgs) {\n        try {\n            if (!navigator.credentials || !navigator.credentials.create) {\n                throw new Error('Browser not supported.');\n            }\n\n            if (createArgs.success === false) {\n                throw new Error(createArgs.msg || 'unknown error occurred');\n            }\n\n            utils.recursiveBase64StrToArrayBuffer(createArgs);\n            const cred = await navigator.credentials.create(createArgs);\n            const authenticatorResponse = {\n                transports: cred.response.getTransports ? cred.response.getTransports() : null,\n                clientDataJSON: cred.response.clientDataJSON ?\n                    utils.arrayBufferToBase64(cred.response.clientDataJSON) : null,\n                attestationObject: cred.response.attestationObject ?\n                    utils.arrayBufferToBase64(cred.response.attestationObject) : null,\n            };\n\n            const registerSuccess = await Str.getString('registersuccess', 'factor_webauthn');\n            await Toast.add(registerSuccess, {type: 'success'});\n\n            document.getElementById('id_response_input').value = JSON.stringify(authenticatorResponse);\n            // Enable the submit button so that we can proceed.\n            document.getElementById('id_submitbutton').disabled = false;\n        } catch (e) {\n            Log.debug('The request timed out or you have canceled the request. Please try again later.');\n        }\n    }\n\n    return {\n        init: function(createArgs) {\n            // Disable the submit button until we have registered a security key.\n            document.getElementById('id_submitbutton').disabled = true;\n\n            Prefetch.prefetchStrings('factor_webauthn', [\n                'registersuccess',\n            ]);\n\n            // Register event listeners.\n            createArgs = JSON.parse(createArgs);\n            document.getElementById('factor_webauthn-register').addEventListener('click', function() {\n                registerSecurityKey(createArgs);\n            });\n            document.getElementById('factor_webauthn-register').addEventListener('keypress', function() {\n                registerSecurityKey(createArgs);\n            });\n        }\n    };\n});\n"],"names":["define","utils","Log","Prefetch","Str","Toast","registerSecurityKey","createArgs","navigator","credentials","create","Error","success","msg","recursiveBase64StrToArrayBuffer","cred","authenticatorResponse","transports","response","getTransports","clientDataJSON","arrayBufferToBase64","attestationObject","registerSuccess","getString","add","type","document","getElementById","value","JSON","stringify","disabled","e","debug","init","prefetchStrings","parse","addEventListener"],"mappings":";;;;;;;;AAwBAA,kCAAO,CACH,wBACA,WACA,gBACA,WACA,eACD,SACCC,MACAC,IACAC,SACAC,IACAC,sBAQeC,oBAAoBC,oBAEtBC,UAAUC,cAAgBD,UAAUC,YAAYC,aAC3C,IAAIC,MAAM,8BAGO,IAAvBJ,WAAWK,cACL,IAAID,MAAMJ,WAAWM,KAAO,0BAGtCZ,MAAMa,gCAAgCP,kBAChCQ,WAAaP,UAAUC,YAAYC,OAAOH,YAC1CS,sBAAwB,CAC1BC,WAAYF,KAAKG,SAASC,cAAgBJ,KAAKG,SAASC,gBAAkB,KAC1EC,eAAgBL,KAAKG,SAASE,eAC1BnB,MAAMoB,oBAAoBN,KAAKG,SAASE,gBAAkB,KAC9DE,kBAAmBP,KAAKG,SAASI,kBAC7BrB,MAAMoB,oBAAoBN,KAAKG,SAASI,mBAAqB,MAG/DC,sBAAwBnB,IAAIoB,UAAU,kBAAmB,yBACzDnB,MAAMoB,IAAIF,gBAAiB,CAACG,KAAM,YAExCC,SAASC,eAAe,qBAAqBC,MAAQC,KAAKC,UAAUf,uBAEpEW,SAASC,eAAe,mBAAmBI,UAAW,EACxD,MAAOC,GACL/B,IAAIgC,MAAM,0FAIX,CACHC,KAAM,SAAS5B,YAEXoB,SAASC,eAAe,mBAAmBI,UAAW,EAEtD7B,SAASiC,gBAAgB,kBAAmB,CACxC,oBAIJ7B,WAAauB,KAAKO,MAAM9B,YACxBoB,SAASC,eAAe,4BAA4BU,iBAAiB,SAAS,WAC1EhC,oBAAoBC,eAExBoB,SAASC,eAAe,4BAA4BU,iBAAiB,YAAY,WAC7EhC,oBAAoBC"}
\ No newline at end of file
diff --git a/admin/tool/mfa/factor/webauthn/amd/src/register.js b/admin/tool/mfa/factor/webauthn/amd/src/register.js
index 7a23a392377..89e26913e8d 100644
--- a/admin/tool/mfa/factor/webauthn/amd/src/register.js
+++ b/admin/tool/mfa/factor/webauthn/amd/src/register.js
@@ -22,7 +22,20 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-define(['factor_webauthn/utils', 'core/log'], function(utils, Log) {
+define([
+    'factor_webauthn/utils',
+    'core/log',
+    'core/prefetch',
+    'core/str',
+    'core/toast',
+], function(
+    utils,
+    Log,
+    Prefetch,
+    Str,
+    Toast,
+) {
+
     /**
      * Register the security key.
      *
@@ -47,6 +60,10 @@ define(['factor_webauthn/utils', 'core/log'], function(utils, Log) {
                 attestationObject: cred.response.attestationObject ?
                     utils.arrayBufferToBase64(cred.response.attestationObject) : null,
             };
+
+            const registerSuccess = await Str.getString('registersuccess', 'factor_webauthn');
+            await Toast.add(registerSuccess, {type: 'success'});
+
             document.getElementById('id_response_input').value = JSON.stringify(authenticatorResponse);
             // Enable the submit button so that we can proceed.
             document.getElementById('id_submitbutton').disabled = false;
@@ -59,8 +76,13 @@ define(['factor_webauthn/utils', 'core/log'], function(utils, Log) {
         init: function(createArgs) {
             // Disable the submit button until we have registered a security key.
             document.getElementById('id_submitbutton').disabled = true;
-            createArgs = JSON.parse(createArgs);
+
+            Prefetch.prefetchStrings('factor_webauthn', [
+                'registersuccess',
+            ]);
+
             // Register event listeners.
+            createArgs = JSON.parse(createArgs);
             document.getElementById('factor_webauthn-register').addEventListener('click', function() {
                 registerSecurityKey(createArgs);
             });
diff --git a/admin/tool/mfa/factor/webauthn/lang/en/factor_webauthn.php b/admin/tool/mfa/factor/webauthn/lang/en/factor_webauthn.php
index 146e33572f4..b8f370f8942 100644
--- a/admin/tool/mfa/factor/webauthn/lang/en/factor_webauthn.php
+++ b/admin/tool/mfa/factor/webauthn/lang/en/factor_webauthn.php
@@ -42,6 +42,7 @@ $string['logintitle'] = 'Verify it\'s you by security key';
 $string['pluginname'] = 'Security key';
 $string['privacy:metadata'] = 'The Security key factor plugin does not store any personal data.';
 $string['register'] = 'Register security key';
+$string['registersuccess'] = 'Registered security key';
 $string['replacefactor'] = 'Replace security key';
 $string['replacefactorconfirmation'] = 'Replace \'{$a}\' security key?';
 $string['revokefactorconfirmation'] = 'Remove \'{$a}\' security key?';