Firebase在云功能中验证电子邮件密码

时间:2018-08-08 17:29:27

标签: firebase firebase-authentication

我需要接受自定义用户名进入我的网站(严格按照字母数字限制,这是发单方的要求。)并且这些用户名应与用户的电子邮件地址互换,以进行登录。

我允许用户通过标准的Firebase电子邮件密码身份验证来注册并使用其电子邮件和密码登录。用户在记帐员处注册,然后通过回发将自定义生成的用户名返回给应用程序。

我创建了一个用户名表,其中包含每个用户名所属的用户的UID(最初是电子邮件,而记帐员生成的用户名)

当用户尝试登录时,我转到用户名表并查找UID。此时,我想使用刚刚查找的UID,以及用户提供的密码,以通过标准 Firebase身份验证系统登录用户。

我一直无法找到任何方法来针对在云功能内部查找的用户帐户验证用户密码是否有效,从而无法生成自定义令牌。

我可以通过用户名查找用户,找到电子邮件,将其发送回客户端,并允许使用该电子邮件和用户提供的密码进行登录,但是最好避免使用该密码,因为它将允许用户名以及与不良行为者相关联的电子邮件地址。

2 个答案:

答案 0 :(得分:4)

在云功能中,您可以安装和使用firebase之外的firebase-admin软件包,并像初始化网页一样对其进行初始化。这样,您可以使用admin SDK查找用户名的电子邮件,然后使用firebase通过云功能使用signInWithEmailAndPassword进行身份验证。如果成功,则可以生成自定义令牌并将其发送给客户端。

我不知道这是否是最好的方法,但是它可行。

答案 1 :(得分:0)

以下是Ricardo's answer的实现(使用REST)。目标是允许与电子邮件登录并行的备用登录系统。这是什么?

  1. 使用用户名
  2. 在数据库中查找匹配的电子邮件
  3. 根据该电子邮件验证提供的密码
  4. 返回电子邮件,以与signInWithEmailAndPassword()一起在客户端使用

它需要一个名为users的数据库集合,该集合由用户名键入并包含用户的电子邮件地址。我在内部将用户名称为code(可以更改)。确保更新API密钥:

// Firebase dependencies.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();

// Axios, for REST calls.
const axios = require('axios');
const apiKey = '[YOUR API KEY]';
const signInURL = 'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=' + apiKey;


exports.getEmailWithCodeAndPassword = functions.https.onCall((data, context) => {
  // Require code and passowrd.
  data = data || {};
  if (!(data.code && data.password)) {
    throw new functions.https.HttpsError('failed-precondition', 'The function must be called with fields: code and password.');
  }

  // Search for user's email, sign in to verify email, and return the email for client-side login.
  return db.collection('users').doc(data.code).get().then(doc => {
    // Throw if the code is not in the users DB.
    if (!doc.data()) {
      throw {
        code: 'auth/user-not-found',
        message: 'There is no user record corresponding to this identifier. The user may have been deleted.',
      };
    }

    // Retrieve the email and attempt sign-in via REST.
    const email = doc.data().email;
    return axios.post(signInURL, {
      email: email,
      password: data.password,
      returnSecureToken: true,
    }).catch(e => {
      throw {
        code: 'auth/wrong-password',
        message: 'The password is invalid or the user does not have a password.',
      };
    });
  }).then(res => {
    // Return the email after having validated the login details.
    return res.data.email;
  }).catch(e => {
    // Throw errors.
    throw new functions.https.HttpsError('unknown', e.message);
  });
});

它不是最有效的(在我的测试中约为500毫秒),但它可以工作。一种替代方法是使用admin.auth().listUsers执行步骤1-2,该操作还会给出盐/哈希值,然后使用Firebase's custom scrypt对照哈希值检查提供的密码。这样可以避免进行REST调用,而这是浪费的大部分时间,但是由于自定义scrypt不在JS中,因此很难。

我还尝试使用Firebase客户端SDK而不是REST来实现,但是它的运行速度很慢,并且具有更大的依赖性(90MB和6400文件,而Axios为500KB / 67文件)。如果有人好奇,我也会在下面复制该解决方案:

// Firebase dependencies.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const firebaseClient = require('firebase');
admin.initializeApp();
const db = admin.firestore();

// Configure and initialise Firebase client SDK.
var firebaseConfig = {
  // [COPY YOUR CLIENT CONFIG HERE (apiKey, authDomain, databaseURL, etc)]
};
firebaseClient.initializeApp(firebaseConfig);

exports.getEmailWithCodeAndPassword = functions.https.onCall((data, context) => {
  // Require code and passowrd.
  data = data || {};
  if (!(data.code && data.password)) {
    throw new functions.https.HttpsError('failed-precondition', 'The function must be called with fields: code and password.');
  }

  // Search for user's email, sign in to verify email, and return the email for client-side login.
  let email;
  return db.collection('users').doc(data.code).get().then(doc => {
    if (!doc.data()) {
      throw {
        code: 'auth/user-not-found',
        message: 'There is no user record corresponding to this identifier. The user may have been deleted.',
      };
    }

    // Retrieve the email and attempt sign-in.
    email = doc.data().email;
    return firebaseClient.auth().signInWithEmailAndPassword(email, data.password);
  }).then(res => email).catch(e => {
    throw new functions.https.HttpsError('unknown', e.message);
  });
});