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

goucqfw6  于 2023-06-24  发布在  其他
关注(0)|答案(2)|浏览(122)

我有一个要求,接受自定义用户名到我的网站(要求从biller与字母数字的严格限制。),这些用户名应该与用户的电子邮件地址可互换的登录目的。
我允许用户通过标准的firebase电子邮件密码验证,用他们的电子邮件和密码注册和登录。用户在记帐者处注册,然后记帐者通过回发将自定义生成的用户名返回到应用程序。
我已经创建了一个用户名表,其中包含每个用户名所属的用户的UID(最初有电子邮件和biller生成的用户名)
当用户尝试登录时,我转到用户名表并查找UID。此时,我想使用刚才查找的UID和用户提供的密码,通过标准firebase身份验证系统登录用户。
我一直无法找到任何方法来验证用户的密码是否有效,以便在云函数中查找用户帐户,以便我可以生成自定义令牌。
我可以通过用户名查找用户,找到电子邮件,将其发送回客户端,并允许使用该电子邮件和用户提供的密码进行登录,但我宁愿避免这样做,因为这将允许不良行为者将用户名和电子邮件地址相互关联。

mmvthczy

mmvthczy1#

在云函数中,除了firebase-admin之外,还可以安装和使用firebase包,并将其初始化,就像初始化网页一样。这样你就可以使用admin SDK找到用户名的电子邮件,然后使用云函数中的firebase进行身份验证,使用signInWithEmailAndPassword。如果成功,您可以生成一个自定义令牌并将其发送到客户端。
我不知道这是不是最好的方法,但它是有效的。

aurhwmvo

aurhwmvo2#

下面是Ricardo's answer的实现(使用REST)。目标是允许一个备用登录系统与电子邮件登录并行。这样做的是:
1.取一个用户名
1.在数据库中查找匹配的电子邮件
1.根据该电子邮件验证提供的密码
1.返回邮件,用于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 ms),但它有效。另一种方法是使用admin.auth().listUsers执行步骤1-2,它也提供了salt/hash,然后使用Firebase's custom scrypt根据hash检查提供的密码。这将避免对REST调用的需要,这是大部分时间损失,但这将是困难的,因为自定义scrypt不在JS中。
我也尝试过用Firebase客户端SDK而不是REST来实现,但它的速度差不多,而且依赖性要大得多(90 MB和6400个文件,而Axios是500 KB/67个文件)。我也会把这个解决方案复制到下面,以防有人好奇:

// Firebase dependencies.
const functions = require('firebase-functions');
const admin = require('firebase-admin/app'); // Legacy was 'firebase'
const firebaseClient = require('firebase/app'); // Legacy was '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);
  });
});

相关问题