Ryan Loggerythm
Ryan Loggerythm

Reputation: 3314

Firebase Cloud Firestore rules / permissions issue

I'm trying to set permissions so a user with a given email address can only read a document with a matching docId.

This is the collection of documents:

/cats/[email protected]
/cats/[email protected]
/cats/[email protected]

The following works perfectly in the "Rules Playground", but it always ends up returning nothing in my Angular app.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    match /cats/{catDocId} {
      allow read: if request.auth.token.email.lower() == catDocId;
      allow write: if false;
    }

  }
}

Here is the app code:

myPage.component.ts:

import { AngularFireAuth } from '@angular/fire/auth';
import { AngularFirestore } from '@angular/fire/firestore';
import { Observable } from 'rxjs';

// ...

cats: Observable<any[]>;

// ...

ngOnInit(): void {
    this.afAuth.authState.subscribe(user => {
        this.cats = this.firestore.collection('cats').valueChanges();
    });
}

myPage.component.html:

<ul>
    <li class="text" *ngFor="let cat of cats | async">
        {{cat.name}}, {{cat.color}}, {{cat.favoriteFood}}
    </li>
</ul>

Any ideas where this is going wrong? Thanks.

Upvotes: 0

Views: 70

Answers (1)

Renaud Tarnec
Renaud Tarnec

Reputation: 83048

From your comment:

So it seems I'm trying to use the rules as a filter. Is there a simple way to grant read access to a single doc based on the user's token data, namely their email address?

You just need to use the User object in order to get the user's email. I'm not really versed in angularfire but the following code (from the angularfire doc on Authentication) shows how to get the displayName property. You should do the same with the email property. Then you should directly query the document, as explained here, since you have the entire DocumentReference.

import { Component } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import firebase from 'firebase/app';

@Component({
  selector: 'app-root',
  template: `
    <div *ngIf="auth.user | async as user; else showLogin">
      <h1>Hello {{ user.displayName }}!</h1>
      <button (click)="logout()">Logout</button>
    </div>
    <ng-template #showLogin>
      <p>Please login.</p>
      <button (click)="login()">Login with Google</button>
    </ng-template>
  `,
})
export class AppComponent {
  constructor(public auth: AngularFireAuth) {
  }
  login() {
    this.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider());
  }
  logout() {
    this.auth.signOut();
  }
}

Upvotes: 1

Related Questions