【问题标题】:Verify if a phone number exist in firebase app using firebase cloud function使用 Firebase 云功能验证 Firebase 应用中是否存在电话号码
【发布时间】:2020-04-18 07:04:59
【问题描述】:

我是 firebase(及其所有功能)领域的新手。我已经阅读了文档,并且已经能够正确使用 web sdk。我创建了一个文件,其中编写了我所有当前的 firebase 代码,如下面的 firebaseApi.js 所示。此外,下面是我如何使用registration.js 下的函数的示例(如果我做错了,请纠正),该示例有效。我试图实现

 admin.auth().getUserByPhoneNumber(phoneNumber),

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

firebaseApi.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;

registration.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);
              })
          }

【问题讨论】:

  • 请将 Stack Overflow 上的每个帖子限制为一个问题。现在,您有几个问题,但似乎只有一个主要问题。请编辑帖子以将焦点缩小到一个主要问题。请随时单独提出其他问题。
  • 谢谢,我已经完成了,请您查看问题

标签: node.js reactjs firebase firebase-authentication google-cloud-functions


【解决方案1】:

出于隐私和最佳做法的考虑,除非当前用户是管理员,否则我不会公开检查任何给定电话号码是否被任何个人使用和/或是否与您的应用程序相关联的能力。

包裹在云函数中

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

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 Phone Auth Reference,以下代码用于验证电话号码:

// '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 声明的人。

数据库结构:(请参阅this answer 了解更多信息)

"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)

【讨论】:

  • 谢谢@samthecodingman....我知道怎么做我想知道是否有机会使用admin sdk在云功能中完成?
  • @vincentO 我已经添加了一个合适的云函数供考虑
  • 非常感谢,我对 react native、firebase 和 nodejs 环境还很陌生,调用您在客户端编写的 api 函数的最佳方法是什么。
猜你喜欢
  • 1970-01-01
  • 2020-02-14
  • 2018-03-23
  • 1970-01-01
  • 2018-05-24
  • 2019-01-16
  • 2020-03-11
  • 2018-05-17
  • 2023-03-05
相关资源
最近更新 更多