使用 Firebase 云功能验证 Firebase 应用中是否存在电话号码



我是火力基地(及其所有功能(领域的新手。我已经阅读了文档,并且我已经能够正确使用Web sdk。我创建了一个文件,其中编写了我当前的所有 firebase 代码,如下所示.js在 firebaseAPI 中所示。另外,下面是我如何使用注册下的功能的示例.js(如果我做错了,请纠正(,该示例有效。我试图实施

admin.auth().getUserByPhoneNumber(phoneNumber),

我想用它来检查应用程序中是否已存在当前输入的电话号码。但是我已经阅读了管理员SDK不能在客户端环境中使用,而只能在Firebase应用程序的开发人员拥有或管理的特权服务器环境中使用。我有点迷茫如何解决这个问题。 是否可以将Firebase云功能连接到客户端,例如 我正在用火力基地Api做什么? 我已经清理了代码,只保留了相关部分

火基地API.js

import firebase from 'firebase/app';
import 'firebase/firestore';
import 'firebase/auth';
import 'firebase/database';
import 'firebase/storage';
const config = {config};
firebase.initializeApp(config);
class Firebase {
register = ({ fullname, email, phone }) => {
const user = Firebase.auth.currentUser.uid;
const firestoreRef = Firebase.firestore.collection('Users').doc(user);
const settings = {
fullname,
email,
phone,
};
firestoreRef
.set(settings);
};
static init() {
Firebase.auth = firebase.auth();
Firebase.firestore = firebase.firestore();
Firebase.database = firebase.database();
Firebase.storage = firebase.storage();
Firebase.email = firebase.auth.EmailAuthProvider;
Firebase.google = firebase.auth.GoogleAuthProvider;
Firebase.phoneVerify = new firebase.auth.PhoneAuthProvider();
Firebase.phone = firebase.auth.PhoneAuthProvider;
}
}
Firebase.shared = new Firebase();
export default Firebase;

注册.js

import Firebase from './firebaseApi';
onCompleteReg() {
const { fullname, email, email } = this.state;
const settings = {
fullname,
email,
email
};
Firebase.shared
.registerSettings(settings)
.then(() => {
console.log('Successful');
}).catch((e) => {
console.log(e);
})
}

作为隐私和最佳实践问题,除非当前用户是管理员,否则我不会公开检查任何给定电话号码是否被任何个人使用和/或与您的应用程序相关联的功能。

包裹在云函数中

由于管理 SDK 只能在安全环境中使用,因此只能通过某些 API 公开其功能。在这种情况下,自动处理用户身份验证和 CORS 是有益的,因此我将使用可调用函数。基于此类 API 的敏感性,还建议对访问进行速率限制,这可以使用firebase-functions-rate-limiter包轻松实现。在下面的代码中,我们将 API 调用限制为每个用户 2 次使用,并将所有用户使用 10 次,每 15 秒,以防止滥用。

import * as admin from 'firebase-admin';
import * as functions from 'firebase-functions';
import { FirebaseFunctionsRateLimiter } from 'firebase-functions-rate-limiter';
admin.initializeApp();
const realtimeDb = admin.database();
const perUserLimiter = FirebaseFunctionsRateLimiter.withRealtimeDbBackend(
{
name: 'rate-limit-phone-check',
maxCalls: 2,
periodSeconds: 15,
},
realtimeDb
);
const globalLimiter = FirebaseFunctionsRateLimiter.withRealtimeDbBackend(
{
name: 'rate-limit-phone-check',
maxCalls: 10,
periodSeconds: 15,
},
realtimeDb
);
exports.phoneNumber = functions.https.onCall(async (data, context) => {
// assert required params
if (!data.phoneNumber) {
throw new functions.https.HttpsError(
'invalid-argument',
'Value for "phoneNumber" is required.'
);
} else if (!context.auth || !context.auth.uid) {
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called while authenticated.'
);
}
// rate limiter
const [userLimitExceeded, globalLimitExceeded] = await Promise.all(
perUserLimiter.isQuotaExceededOrRecordUsage('u_' + context.auth.uid),
globalLimiter.isQuotaExceededOrRecordUsage('global'));
if (userLimitExceeded || globalLimitExceeded) {
throw new functions.https.HttpsError(
'resource-exhausted',
'Call quota exceeded. Try again later',
);
}
let userRecord = await admin.auth.getUserByPhoneNumber(phoneNumber);
return userRecord.uid;
}

若要调用检查,请在客户端上使用以下代码:

let checkPhoneNumber = firebase.functions().httpsCallable('phoneNumber');
checkPhoneNumber({phoneNumber: "61123456789"})
.then(function (result) {
let userId = result.data;
// do something with userId
})
.catch(function (error) {
console.error('Failed to check phone number: ', error)
});

登录尝试

与其允许用户了解电话号码是否存在或具体存在于你的服务中,不如遵循电话号码身份验证流程,并允许他们证明他们拥有给定的电话号码。由于用户无法集体验证多个号码,因此这是最安全的方法。

在 Firebase 电话身份验证参考中,以下代码用于验证电话号码:

// 'recaptcha-container' is the ID of an element in the DOM.
var applicationVerifier = new firebase.auth.RecaptchaVerifier(
'recaptcha-container');
var provider = new firebase.auth.PhoneAuthProvider();
provider.verifyPhoneNumber('+16505550101', applicationVerifier)
.then(function(verificationId) {
var verificationCode = window.prompt('Please enter the verification ' +
'code that was sent to your mobile device.');
return firebase.auth.PhoneAuthProvider.credential(verificationId,
verificationCode);
})
.then(function(phoneCredential) {
return firebase.auth().signInWithCredential(phoneCredential);
});

特权电话搜索

如果希望具有适当特权的用户(无论他们具有管理员还是管理角色(能够通过电话号码查询用户,则可以使用以下基架。在这些代码示例中,我将访问权限限制为对其身份验证令牌具有isAdmin声明的用户。

数据库结构:(有关更多信息,请参阅此答案(

"phoneNumbers": {
"c011234567890": { // with CC for US
"userId1": true
},
"c611234567890": { // with CC for AU
"userId3": true
},
...
}

数据库规则:

{
"rules": {
...,
"phoneNumbers": {
"$phoneNumber": {
"$userId": {
".write": "auth.uid === $userId && (!newData.exists() || root.child('users').child(auth.uid).child('phoneNumber').val() == ($phoneNumber).replace('c', ''))" // only this user can edit their own record and only if it is their phone number or they are deleting this record
}
},
".read": "auth != null && auth.token.isAdmin == true", // admins may read/write everything under /phoneNumbers
".write": "auth != null && auth.token.isAdmin == true"
}
}
}

帮助程序函数:

function doesPhoneNumberExist(phoneNumber) {
return firebase.database.ref("phoneNumbers").child("c" + phoneNumber).once('value')
.then((snapshot) => snapshot.exists());
}
// usage: let exists = await doesPhoneNumberExist("611234567890")
function getUsersByPhoneNumber(phoneNumber) {
return firebase.database.ref("phoneNumbers").child("c" + phoneNumber).once('value')
.then((snapshot) => snapshot.exists() ? Object.keys(snapshot.val()) : []);
}
// usage: let usersArray = await getUsersByPhoneNumber("611234567890") - normally only one user
function searchPhoneNumbersThatStartWith(str) {
if (!str || str.length < 5) return Promise.reject(new Error('Search string is too short'));
return firebase.database.ref("phoneNumbers").startAt("c" + str).endAt("c" + str + "uf8ff").once('value')
.then((snapshot) => {
let phoneNumbers = [];
snapshot.forEach((phoneEntrySnapshot) => phoneNumbers.push(phoneEntrySnapshot.key));
return phoneNumbers;
});
}
// usage: let matches = await searchPhoneNumbersThatStartWith("61455")
// best handled by Cloud Function not client
function linkPhoneNumberWithUser(phoneNumber, userId) {
return firebase.database.ref("phoneNumbers").child("c" + phoneNumber).child(userId).set(true);
}
// usage: linkPhoneNumberWithUser("611234567890", firebase.auth().currentUser.uid)
// best handled by Cloud Function not client
function unlinkPhoneNumberWithUser(phoneNumber, userId) {
return firebase.database.ref("phoneNumbers").child("c" + phoneNumber).child(userId).remove();
}
// usage: unlinkPhoneNumberWithUser("611234567890", firebase.auth().currentUser.uid)

最新更新