我有一个奇怪的问题。我的用户需要先注销然后登录才能在首次注册时正确“触发”Firebase 安全角色。这是流程: 用户向云功能提交详细信息 云功能使用管理员身份验证创建用户并返回响应。 在客户端,当收到响应时,用户使用提供的凭据登录,然后收到需要验证电子邮件的通知。 在他们单击验证链接后,它会刷新客户端,直到它识别出电子邮件已验证。 那时它会调用我的实时数据库,这是拒绝许可的地方。但是,如果他们注销并重新登录,安全规则会正确识别所有内容。这是我的规则:
"rules": {
"sendRequests":{
".read": false,
"$requests":{
".write": "auth!=null && data.exists()",
}
},
"Notify":{
".read":false,
"$userCardID":{
".write": "auth!=null"
}
},
"$users":{
".read": "auth != null && auth.token.email_verified===true",
".write": "auth.uid === $users",
".indexOn":["Name"],
"Roster":{
"$cardID":{
".validate": "root.child(auth.uid+'-admin').child('Collection').child(newData.child('ID').val()).exists()"
}
},
"Completed":{
"$cardID":{
".validate": "root.child(auth.uid+'-admin').child('Collection').child(newData.child('ID').val()).exists()"
}
},
"ToDos":{
"$cardID":{
".validate": "root.child(auth.uid+'-admin').child('Collection').child(newData.child('ID').val()).exists()"
}
}
}
}
}
很好奇这是设计使然还是我在这里遗漏了一步。在电子邮件验证之前,我已检查是否正在创建用户并将数据添加到正确的用户 ID 下的数据库中。
编辑:我还应该提到,在收到来自云功能的响应后,用户使用存储的电子邮件和密码以编程方式登录,他们实际上并没有“单击”登录按钮。不确定这是否会改变什么。
编辑:这是客户端代码:
//This function is trigger when the user clicks on the 'Submit' button on the client form.
//searchString(username),eml(email),and pwd(password) or all grabbed from the 'input' values of the client form.
//It generates a random 'code' and then submits the data to a firestore doc, this triggers the cloud function to pull the data.
const usersRef = db2.ref();
function createRequest() {
return new Promise((resolve, reject) => {
let prefix = "Req"
let code = prefix;
for (let it = 0; it < 2; it++) {
let number = Math.floor(Math.random() * (9999 - 1000) + 1000);
code = code.concat(number)
if (it === 1) {
return resolve(code);
}
}
})
}
createRequest().then((code) => {
dataStore.collection('NewUserRequests').doc(code).set({
displayName: searchString,
email: eml,
password: pwd,
ReqID: code
});
//Then begin listening for the response ('true' or 'error message'). On 'true' it logs in the user.
dataStore.collection('NewUserResponses').doc(code).onSnapshot((doc) => {
log.debug("New user response is: " + doc.data());
//I have verified on the console this is being received correctly.
if (doc.data().Response === true) {
login_user(eml, pwd);
} else {
alertNative(doc.data().Response);
}
})
});
//the login function for firebase
function login_user(email, pwd) {
firebase.auth().signInWithEmailAndPassword(email, pwd).then(function(user) {
newLogin = true;
}, function(error) {
log.error("User login error: " + errorMessage);
});
}
//After login, the firebase auth state change gets triggered:
firebase.auth().onAuthStateChanged((user) => {
if (user) {
log.info("User Auth Detected")
//I use the photoURL field to determine if a user is 'new'. I know its not elegant but it works.
let newNameCheck = firebase.auth().currentUser.photoURL
if (newNameCheck !== null) {
if (newNameCheck.indexOf("http://www.example.com/12345678/newUser.png") !== (-1)) {
createNewUser = true;
firebase.auth().currentUser.sendEmailVerification().then(function() {
log.info('Email Verification sent.') // Email sent.
}).catch(function(error) {
log.error('There was an issue sending the verification email: ' + error)
});
}
}
if (user.emailVerified === false) {
log.warn('This user is not verified!')
let checkEmail = setInterval(function() {
firebase.auth().currentUser.reload().then(check => {
if (firebase.auth().currentUser.emailVerified === true) {
//FINISH LOGGING IN/BUILDING NEW USER
clearInterval(checkEmail);
finishLoginSequence();
}
})
}, 2000)
} else {
log.warn('This user is verified!')
finishLoginSequence()
}
}
//ALL code up to this point triggers normally as expected. When it hits finishLogin Sequence is when permission is denied.
function finishLoginSequence(){
log.warn(firebase.auth().currentUser.emailVerified);
firebase.auth().currentUser.updateProfile({
photoURL: ""
}).then(function() {
log.info('Succesfully update new user profile.')
}).catch(function(error) {
log.error('There was an error updating the new user profile: '+error)
});
//THIS is approved, and I see the in the console that the profile has been updated.
currentUser = user.uid;
log.warn(firebase.auth().currentUser.emailVerified);
usersRef.child(currentUser+'-admin').child('Collection').once('value',function(snapshot){
let collection = snapshot.val()
fullCollect.push(collection)
//It never gets here as permission is denied!!
})
}
});
日志文件输出
[2021-05-13 19:06:00] Sess7967g4164k [info] User Auth Detected Caller: lobby.js:1590:11)
[2021-05-13 19:06:00] Sess7967g4164k [warn] This user is not verified! Caller: lobby.js:1637:13)
[2021-05-13 19:06:00] Sess7967g4164k [info] Email Verification sent. Caller: lobby.js:1597:17
[2021-05-13 19:06:08] Sess7967g4164k [info] Email verification has been received. Caller: lobby.js:1642:19
[2021-05-13 19:06:11] Sess7967g4164k [warn] true Caller: lobby.js:1676:15)
[2021-05-13 19:06:11] Sess7967g4164k [info] New user detected. Begin building new user account. Caller: lobby.js:1698:17)
[2021-05-13 19:06:11] Sess7967g4164k [warn] true Caller: lobby.js:1734:17)
[2021-05-13 19:06:11] Sess7967g4164k [error] UnhandledRejection Error: Permission denied
at https://www.gstatic.com/firebasejs/8.3.2/firebase.js:1:394396 Caller: catchErrors.js:35:15)
[2021-05-13 19:06:12] Sess7967g4164k [info] Succesfully update new user profile. Caller: lobby.js:1702:19
小提琴测试数据库的规则:
{
"rules": {
"UserID999":{
".read": "auth != null && auth.token.email_verified===true",
".write": "false"
},
"UserID888":{
".read": "auth != null",
".write": "false"
}
}
}