下面是Ricardo's answer 的实现(使用 REST)。目标是允许备用登录系统与电子邮件登录并行。这是做什么的:
- 输入用户名
- 在数据库中查找匹配的电子邮件
- 根据该电子邮件验证提供的密码
- 返回电子邮件,在客户端使用
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);
});
});