Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase Admin SDK: Set / Merge Custom User Claims

Does Firebase have any trick like { merge: true } to set extra/more custom claims without delete/override the old variables?

Step to reproduce:

admin.auth().setCustomUserClaims(uid, { a: 'value' }) // Run this first
admin.auth().setCustomUserClaims(uid, { b: 'value' }) // Then run this after

Result:

{ b: 'value'}

Expected result:

{ a: 'value', b: 'value' }

Or I did something wrong?

like image 993
l2aelba Avatar asked Dec 05 '22 09:12

l2aelba


2 Answers

The Firebase documentation for setCustomUserClaims states:

  • customUserClaims: Object
    The developer claims to set. If null is passed, existing custom claims are deleted. Passing a custom claims payload larger than 1000 bytes will throw an error. Custom claims are added to the user's ID token which is transmitted on every authenticated request. For profile non-access related user attributes, use database or other separate storage systems.

It isn't entirely clear from this description, but the statement, "If null is passed, existing custom claims are deleted," provides a hint that the custom claims are completely overwritten with each call to setCustomUserClaims.

Therefore, custom claims need to be set as follows:

claims = {
  a: 'value',
  b: 'value'
}

admin.auth().setCustomUserClaims(uid, claims) 

Workaround: addCustomUserClaims

A helper function could be created to merge in new claims.

async function addCustomUserClaims(uid, claims) {
  const user = await admin.auth().getUser(uid)
  let updated_claims = user.customClaims || {}

  for (let property in claims) {
    if (Object.prototype.hasOwnProperty.call(claims, property)) {
      updated_claims[property] = claims[property]
    }
  }
  await admin.auth().setCustomUserClaims(uid, updated_claims)
}
like image 161
Christopher Peisert Avatar answered Jan 01 '23 19:01

Christopher Peisert


Christopher Peisert's answer is correct, but it can be done much more cleanly as

admin.auth().getUser(uid).then(({customClaims: oldClaims}) =>
    admin.auth().setCustomUserClaims(uid, { ...oldClaims, b: 'value' }))

If you want to abstract this logic into a function, it can be done as

function addCustomUserClaims(uid, claims) {
    return admin.auth().getUser(uid).then(({customClaims}) =>
        admin.auth().setCustomUserClaims(uid, { ...customClaims, ...claims }))
}

or equivalently* as

const addCustomUserClaims = (uid, claims) => 
    admin.auth().getUser(uid).then(({customClaims}) =>
        admin.auth().setCustomUserClaims(uid, { ...customClaims, ...claims }))
like image 32
Avi Mehra Avatar answered Jan 01 '23 18:01

Avi Mehra