title: Firestore rules do not permit claims for signInWithCustomToken user using custom JWT
body: <p>I'm having a structured set of Firestore rules that everything works fine except for custom user claims inside a custom JWT.</p>
  
  <p>I'm using Laravel framework and <a href="https://github.com/kreait/firebase-php" rel="nofollow noreferrer">kreait/firebase-php</a> with <a href="https://firebase-php.readthedocs.io/en/latest/authentication.html#create-custom-tokens" rel="nofollow noreferrer"><code>createCustomToken</code></a> like this:</p>
  
  <pre class="lang-php prettyprint-override"><code>'firebase_custom_token' =&gt; (string)$firebase-&gt;getAuth()-&gt;createCustomToken(
    "mrp-admin-user:{$user-&gt;id}", [
      'isPanel' =&gt; true,
      'userRoles' =&gt; $user-&gt;roleIds // returns an array like ['admin', 'operator']
    ]
  )
  </code></pre>
  
  <p>Then at the client I'm successfully signing-in Firebase like this:</p>
  
  <pre><code>firebase.auth().signInWithCustomToken(firebase_custom_token);
  </code></pre>
  
  <p>I have a Firestore rules function that checks if the user is a panel user by checking the <code>isPanel == true</code> token claim:</p>
  
  <pre><code>function isPanelUser() {
    return request.auth != null &amp;&amp; 
      'isPanel' in request.auth.token &amp;&amp; 
      request.auth.token.isPanel == true
  }
  </code></pre>
  
  <p>And everything that uses that function fails with <strong>Missing or insufficient permissions</strong> error:</p>
  
  <pre><code>match /users/{userId}/{document=**} {
    allow read, write: if isSignedInAs(userId) || isPanelUser();
  }
  
  match /system/services/{serviceId}/{document=**} {
    allow read;
    allow write: if isPanelAdmin();
  }
  </code></pre>
  
  <h3>Firestore rules:</h3>
  
  <pre><code>rules_version = '2';
  
  service cloud.firestore {
    match /databases/{database}/documents {
      match /system/config {
        allow read;
        allow write: if isPanelAdmin();
      }
  
      match /services/{serviceId}/{document=**} {
        allow read;
        allow write: if isPanelAdmin();
      }
  
      match /orders/{orderId}/{document=**} {
        allow read, write: if  isPanelUser() || isSignedInAs(resource.data.uid);
      }
  
      match /users/{userId}/{document=**} {
        allow read, write: if isSignedInAs(userId) || isPanelUser();
      }
  
      match /drivers/{userId}/{document=**} {
        allow read, write: if isSignedInAs(userId) || isPanelUser();
      }
  
      function isSignedIn() {
        return request.auth != null
      }
  
      function isSignedInAs(uid) {
        return request.auth != null &amp;&amp; request.auth.uid == uid
      }
  
      function isPanelUser() {
        return request.auth != null &amp;&amp; 
          'isPanel' in request.auth.token &amp;&amp; 
          request.auth.token.isPanel == true
      }
  
      function isPanelUserOfRole(role) {
        return isPanelUser() == true &amp;&amp; 
          'userRoles' in request.auth.token &amp;&amp; 
          role in request.auth.token.userRoles
      }
  
      function isPanelSuperAdmin() {
        return isPanelUser() == true &amp;&amp; 
          'userRoles' in request.auth.token &amp;&amp; 
          'super-admin' in request.auth.token.userRoles
      }
  
      function isPanelAdmin() {
        return isPanelUser() == true &amp;&amp; 
          'userRoles' in request.auth.token &amp;&amp; (
            'admin' in request.auth.token.userRoles || 
            'super-admin' in request.auth.token.userRoles
          )
      }
  
      function isPanelOperator() {
        return isPanelUser() == true &amp;&amp; 
          'userRoles' in request.auth.token &amp;&amp; (
            'operator' in request.auth.token.userRoles ||
            'admin' in request.auth.token.userRoles || 
            'super-admin' in request.auth.token.userRoles
          )
      }
    }
  }
  </code></pre>
  
  <p><strong>EDIT</strong></p>
  
  <p>Here is the javascript code that writes to the <code>system/config</code> document:</p>
  
  <p><strong>Notice</strong>: If I remove the written check from the <code>system/config</code> rule then it works and writes to the document.</p>
  
  <pre><code>import firebase from "firebase/app";
  import 'firebase/firestore';
  
  ...
  
  const systemConfigRef = firebase.firestore().collection('system').doc('config');
  
  systemConfigRef.set({
    status: 'down'
  })
  .then(docRef =&gt; {
    console.log('System config written');
  })
  .catch(error =&gt; {
    console.log('System config error', error); // getting permissions error
  });
  </code></pre>