I am unable to get Firebase Storage work with custom rules and using custom claims.
In my Python Admin panel, I do the following to create the user and assign a clai
If I'm not wrong you are using this wrong. Should be:
service firebase.storage {
match /b/{bucket}/o {
match /{environment}/{client_id}/{allPaths=**} {
allow read: if request.auth.uid == client_id
}
}
}
The token returns others objects, like:
So for you be able to compare the user Id you must use request.auth.uid
. This way will compare the cliente client id. If you want to take a look at the docs, is everything abour the request.auth
.
Case you want your own custom token, like: request.auth.token.client_id
, you need do that with this code in Python:
uid = 'some-uid'
additional_claims = {
'client_id': your_custom_client_id
}
custom_token = auth.create_custom_token(uid, additional_claims)
Then you can use in your storage rules:
service firebase.storage {
match /b/{bucket}/o {
match /{environment}/{client_id}/{allPaths=**} {
allow read: if request.auth.token.client_id == client_id
}
}
}
See the docs
For completeness, one need to create a token before setting the token for the user.
Thus full code:
uid = 'some-uid'
additional_claims = {
'client_id': your_custom_client_id
}
# First time, only once needed
custom_token = auth.create_custom_token(uid, additional_claims)
# Then
auth.set_custom_user_claims(uid, additional_claims )
Custom claims are the only way to do this right now. Rules should look like this:
service firebase.storage {
match /b/{bucket}/o {
function isAuth() {
return request.auth != null && request.auth.uid != null
}
function isAdmin() {
return isAuth() &&
request.auth.token.admin == true;
}
function clientMatch(clientId) { // expects user's "client" field to be ID of client
return isAuth() &&
clientId == request.auth.token.clientId;
}
match /storage/path/{clientId}/{allPaths=**} {
allow read, write: if isAdmin() || clientMatch(clientId)
}
where we use two custom fields on the auth token: admin
and clientId
.
The cloud function to sync with the db can look something like this:
exports.updateUser = functions.firestore
.document('users/{userId}')
.onWrite( async (change, context) => {
// change.before and change.after are DocumentSnapshot objects
const userid=context.params.userId // (from {userId} above)
const isDeleted = !change.after.exists
const isNew = !change.before.exists
let customClaims = {}
if (!isDeleted) {
let newData = change.after.data()
let oldData = change.before.data()
// do we need to update anything?
if (isNew ||
newData.admin !== oldData.admin ||
newData.client !== oldData.client) {
customClaims.admin = Boolean(newData.admin)
customClaims.clientId = newData.client
}
}
else {
let oldData = change.before.data()
customClaims.admin = false
customClaims.clientId = null
}
// now do the update if needed
if (customClaims !== {}) {
// See https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth
await admin.auth().setCustomUserClaims(userid, customClaims)
console.log(`Updating client for ${isNew?"new":"existing"} user ${userid}: ` +
`${JSON.stringify(customClaims)}`)
}
})
That runs on any change to the user document, and syncs it to the auth's custom claims.