firebase-tools
firebase-tools copied to clipboard
RTDB emulator doesn't work properly with databaseAuthVariableOverride
[REQUIRED] Environment info
firebase-tools: 8.7.0 Platform: macOS node: v10.22.0 firebase-admin: 9.1.0 firebase-database-emulator: v4.5.0
[REQUIRED] Test case
Add some rules like the following:
{
"rules": {
"$userId": {
"publicAccess": {
".validate": "newData.isBoolean()"
},
"data": {
".read": "$userId === auth.uid || (auth.token.auth_time >= ((now / 1000) - 60 * 60) && auth.token.customClaimAccess === $userId) || data.parent().child('publicAccess').val() === true"
}
}
}
}
[REQUIRED] Steps to reproduce
const admin = require('firebase-admin')
const adminApp = admin.initializeApp({
databaseURL: 'https://blah.firebaseio.com',
})
const noAuthApp = admin.initializeApp({
databaseURL: 'https://blah.firebaseio.com',
databaseAuthVariableOverride: null,
}, 'no-auth')
const userAuthApp = admin.initializeApp({
databaseURL: 'https://blah.firebaseio.com',
databaseAuthVariableOverride: {
uid: 'USER_ID_1',
},
}, 'user-auth')
const customClaimsAuthApp = admin.initializeApp({
databaseURL: 'https://blah.firebaseio.com',
databaseAuthVariableOverride: {
uid: 'USER_ABC',
token: {
customClaimAccess: 'USER_ID_2',
auth_time: (Date.now() / 1000)
},
},
}, 'custom-claims-auth')
const assert = require('assert').strict
async function test() {
await adminApp.database().ref().set({
USER_ID_1: {
data: {
blah: "hello1",
},
},
USER_ID_2: {
data: {
blah: "hello2",
},
},
USER_ID_3: {
publicAccess: true,
data: {
blah: "hello3",
},
}
})
await assert.rejects(noAuthApp.database().ref('USER_ID_1/data').once('value'), /permission_denied/i, 'Should deny access with noAuthApp as not accessing public data')
await assert.doesNotReject(userAuthApp.database().ref('USER_ID_1/data').once('value'), 'Should allow access with userAuthApp as accessing own data')
await assert.doesNotReject(customClaimsAuthApp.database().ref('USER_ID_2/data').once('value'), 'Should allow access with customClaimsAuthApp as accessing data specified in claims')
await assert.doesNotReject(noAuthApp.database().ref('USER_ID_3/data').once('value'), 'Should allow access with noAuthApp as accessing public data')
}
test().catch(error => {
console.error(error)
}).finally(() => Promise.all(admin.apps.map(a => a.delete())))
- Run emulator
firebase --project blah emulators:start --only database
(can't useemulators:exec
as need to keep it running to view the rule evaluations). - Run the above script against the emulator
FIREBASE_DATABASE_EMULATOR_HOST=localhost:9000 node script.js
. - View the evaluated DB rules:
http://localhost:9000/.inspect/coverage?ns=blah
[REQUIRED] Expected behavior
I'd expect the tests to pass and the auth_time
rule to be evaluated 3 times - returning true
once and false
twice.
[REQUIRED] Actual behavior
Rules are not always evaluated - seems to bail out...
To simplify the case when running:
await assert.rejects(noAuthApp.database().ref('USER_ID_1/data').once('value'), /permission_denied/i, 'Should deny access with noAuthApp as not accessing public data')
I'd expect this rule to be evaluated (as there's no auth.uid
):
(auth.token.auth_time >= ((now / 1000) - 60 * 60) && auth.token.customClaimAccess === $userId) || data.parent().child('publicAccess').val() === true
The whole rule seems not to be evaluated, despite the first rule being evaluated...!
Just to add to this, I think the missing evaluations might be to do with null
values because if you have a rule such as data.child('blah').val() < 100 || data.child('other').exists()
and blah
is unset (null
) the hover info will show .val()
returning null
each time but the result count of data.child('blah').val() < 100
won't match this count.
I'm also tackling with similar.
If you're still on this (2 years later!!!!):
- are you sure the calls lead to the emulators?
.firebaseio.com
looks suspicious - By using
demo-blah
(anything starting withdemo-
) one makes sure the calls will not aim at the real Firebase project in the cloud.
I've created a request for proper documentation on how to use databaseAuthVariableOverride
with firebase-admin
and emulation.
@akauppi I don't understand your points sorry. The .firebaseio.com
hostname is swapped out by the internals of the SDK when FIREBASE_DATABASE_EMULATOR_HOST
envar is specified. I imagine its something to do with null
checks in the emulator etc. etc. that doesn't quite match production.
@rhodgkins - have you tried running this with the latest version of firebase-admin and firebase-database-emulator?
@maneesht yep, if you run the reproduction steps above the same thing happens.
Ran firebase setup:emulators:database
to get the latest DB emulator (it downloaded v4.8.0
).
@rhodgkins thank you for your patience. We might have identified what the problem is, however we do not have the bandwidth to support this right now. I will keep this open until we can look further into this.