Я работаю над проектом, использующим Laravel, Inertia.js и Vue 3, и хочу реализовать двухфакторную аутентификацию (2FA) для входа в систему пользователя. Моя установка включает базу данных MySQL с таблицей пользователей, которая содержит столбец 2fa_enabled, указывающий, требуется ли 2FA для пользователя.
Вот рабочий процесс, которого я хотел бы достичь:
Во время реализации я столкнулся с некоторыми проблемами и не знаю, как правильно настроить процесс создания и обработки кода проверки. Я пробовал использовать Firebase для отправки SMS-кодов и проверки ReCaptcha, но столкнулся со следующими проблемами:
Непойманный (в обещании) ReferenceError: Firebase не определена Сбои проверки рекапчи
Обработка проверки кода в моем компоненте Vue 3
Буду признателен за любые рекомендации о том, как:
Вот как я настраиваю верификатор ReCaptcha и инициализацию Firebase:
import { initializeApp } from 'firebase/app';
import { getAuth, RecaptchaVerifier } from 'firebase/auth';
const firebaseConfig = {
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_AUTH_DOMAIN",
projectId: "YOUR_PROJECT_ID",
storageBucket: "YOUR_STORAGE_BUCKET",
messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
appId: "YOUR_APP_ID",
measurementId: "YOUR_MEASUREMENT_ID"
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const recaptchaContainer = document.getElementById('recaptcha-container');
const recaptchaVerifier = new RecaptchaVerifier(recaptchaContainer, {
size: 'invisible',
callback: (response) => {
console.info('reCAPTCHA resolved');
},
'expired-callback': () => {
console.info('reCAPTCHA expired');
}
}, auth);
async function getCode(phoneNumber) {
try {
const confirmationResult = await auth.signInWithPhoneNumber(phoneNumber, recaptchaVerifier);
console.info('Code sent:', confirmationResult);
} catch (error) {
console.error('Error during sign-in with phone number:', error);
console.error('Error code:', error.code);
}
}
// Example usage
getCode('+91112223334');





Я следую Официальным документам Firebase, поэтому нашел несколько ошибок, теперь все работает нормально.
import { getAuth, RecaptchaVerifier, signInWithPhoneNumber} from "firebase/auth";
initializeApp(firebaseConfig);
const auth = getAuth();
onMounted(() => {
const recaptchaVerifier = new RecaptchaVerifier(auth, 'recaptcha-container', {
в этой строке
<script setup>
import { initializeApp } from 'firebase/app';
import { getAuth, RecaptchaVerifier, signInWithPhoneNumber } from "firebase/auth";
import { computed, ref, onMounted } from 'vue';
import { router } from "@inertiajs/vue3";
const props = defineProps({
phoneNumber: {
type: String,
required: true,
},
});
const verificationCode = ref('');
let confirmationResult = null;
const otpPart1 = ref('');
const otpPart2 = ref('');
const otpPart3 = ref('');
const otpPart4 = ref('');
const otpPart5 = ref('');
const otpPart6 = ref('');
const firebaseConfig = {
apiKey: "****",
authDomain: "****.firebaseapp.com",
projectId: "****",
storageBucket: "****.appspot.com",
messagingSenderId: "****",
appId: "5:****:web:****",
measurementId: "****"
};
initializeApp(firebaseConfig);
const auth = getAuth();
onMounted(() => {
const recaptchaVerifier = new RecaptchaVerifier(auth, 'recaptcha-container', {
size: 'invisible',
callback: (response) => {
console.info('reCAPTCHA resolved');
},
'expired-callback': () => {
console.info('reCAPTCHA expired, please resolve reCAPTCHA again.');
}
}, auth);
window.recaptchaVerifier = recaptchaVerifier;
sendVerificationCode(props.phoneNumber);
});
function sendVerificationCode(phoneNumber) {
const appVerifier = window.recaptchaVerifier;
signInWithPhoneNumber(auth, phoneNumber, appVerifier)
.then((result) => {
confirmationResult = result;
})
.catch((error) => {
console.error('Error sending verification code:', error);
});
}
function handleFormSubmit() {
const otpCode = `${otpPart1.value}${otpPart2.value}${otpPart3.value}${otpPart4.value}${otpPart5.value}${otpPart6.value}`;
verifyCode(otpCode);
}
function verifyCode(verificationCode) {
if (confirmationResult) {
confirmationResult.confirm(verificationCode)
.then((result) => {
router.post(route('admin.sendverificationcode'), { 'verificationCode': verificationCode })
})
.catch((error) => {
console.error('Error verifying code:', error);
});
} else {
console.error('Confirmation result not available.');
}
}
const maskedPhoneNumber = computed(() => {
const phoneNumberLength = props.phoneNumber.length;
const digitsToShow = 4;
const startIndex = phoneNumberLength - digitsToShow;
const maskedPart = '*'.repeat(startIndex);
const unmaskedPart = props.phoneNumber.slice(startIndex);
return maskedPart + unmaskedPart;
});
</script><style scoped>
.login-card .card-body {
padding: 2.5rem !important;
}
.signIn-heading {
margin-bottom: 20px;
}
.twoStepArea {
display: flex;
align-items: center;
justify-content: center;
gap: 15px;
}
.three-input-groups {
display: flex;
}
.three-input-groups input[type = "number"] {
width: 36px;
height: 49px;
border-radius: 0;
border: 1px solid #e2e2e2;
outline: 0;
padding: 10px 5px;
text-align: center;
}
.three-input-groups input:first-child {
border-top-left-radius: 6px;
border-bottom-left-radius: 6px;
}
.three-input-groups input[type = "number"]+input[type = "number"] {
border-left: 0;
}
.three-input-groups input:last-child {
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
}
.twoStep-spacer {
font-size: 34px;
font-weight: 700;
color: #e2e2e2;
line-height: 1;
margin-top: -10px;
}
</style><template>
<div class = "row justify-content-center">
<div class = "col-md-8 col-lg-6 col-xl-5">
<div class = "card mt-4 login-card">
<div class = "card-body p-4">
<div>
<h5 class = "text-primary signIn-heading">Two-step authentication</h5>
<p>Enter the 6-digit verification code sent to <b>your mobile
number {{ maskedPhoneNumber }}.</b></p>
</div>
<div>
<form @submit.prevent = "handleFormSubmit">
<div id = "recaptcha-container"></div>
<div class = "otp-container mt-4">
<div class = "twoStepArea">
<div class = "three-input-groups">
<input type = "number" v-model = "otpPart1" maxlength = "1"
class = "otp-input" />
<input type = "number" v-model = "otpPart2" maxlength = "1"
class = "otp-input" />
<input type = "number" v-model = "otpPart3" maxlength = "1"
class = "otp-input" />
</div>
<div class = "twoStep-spacer">-</div>
<div class = "three-input-groups">
<input type = "number" v-model = "otpPart4" maxlength = "1"
class = "otp-input" />
<input type = "number" v-model = "otpPart5" maxlength = "1"
class = "otp-input" />
<input type = "number" v-model = "otpPart6" maxlength = "1"
class = "otp-input" />
</div>
</div>
</div>
<div class = "mt-4">
<button class = "btn btn-primary w-100" type = "submit"
>Verify Phone Number</button>
</div>
</form>
</div>
</div>
<!-- end card body -->
</div>
<!-- end card -->
</div>
</div>
</template>