【问题标题】:Firebase insufficient permissions for .doc().get()Firebase 对 .doc().get() 的权限不足
【发布时间】:2021-08-23 17:35:26
【问题描述】:

我正在尝试对我的应用中的用户进行身份验证。我有一个登录屏幕,它需要一个电子邮件和密码,然后运行这个:

login2(email: string, password: string): Observable<any> {
    const signInObs: Observable<UserCredential> = from(this.fAuth.signInWithEmailAndPassword(email, password));
    
    return signInObs.pipe(take(1), catchError(this.handleError), mergeMap((result: UserCredential) => {
        console.log(`done this ONE... ${result.user.uid}`);
        
        return this.firestore.collection('users').doc(result.user.uid).get().pipe(catchError(this.handleError), mergeMap((doc: DocumentSnapshot<any>) => {
            console.log(`done this two... ${doc.data().name}`);
            
            return result.user.getIdTokenResult(true).then((token: IdTokenResult) => {
                // authenticate the user in the code
                console.log(`done this three.. ${token.claims.admin}`);
                                    
                this.handleAuthentication(
                    result.user.email, 
                    result.user.uid,
                    doc.data().name,
                    token.claims.admin,
                    token.token 
                );
            }).catch(error => {
                throw new Error(error);
            });
        })); 
    }));
}

但是旧的ERROR FirebaseError: Missing or insufficient permissions. 错误发生在this.firebase.collection('users').doc(result.user.uid).get() 部分。没有那个部分,这一切都很好 - 即它登录,给我一个令牌等等。一切正常,只是它不允许我访问该用户记录...

我的数据库中的规则是:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    
    // match logged in user doc in users collection
    match /users/{userId} {
      allow create: if request.auth.uid != null;
      allow read: if request.auth != null && request.auth.uid == userId;
    }
    
    // match docs in the users collection
    match /users/{userId} {
        allow read, write: if request.auth.uid != null;
    }
  }
}

在提到可能是 FireAuth 和 Firestore 模块无法正确通信的回复时,app.module 的外观是这样的。这些都是 app.module 中对 angular fire 模块的引用。

import { environment } from 'src/environments/environment';
import { AngularFireModule } from '@angular/fire';
import { AngularFireFunctions } from '@angular/fire/functions';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { AngularFirestoreModule } from '@angular/fire/firestore';

@NgModule({
  declarations: [
    // just components
  ],
  imports: [
    // other imports 
    AngularFireModule.initializeApp(environment.firebaseConfig),
    AngularFirestoreModule, 
    AngularFireAuthModule
  ],
  providers: [
        {provide: HTTP_INTERCEPTORS, useClass: AuthInterceptorService, multi: true}, 
        AuthenticationService,
        AngularFireFunctions
    ],
  bootstrap: [AppComponent]
})
export class AppModule { }

以及Authentication服务中的构造函数:

constructor(private fAuth: AngularFireAuth, 
            private firestore: AngularFirestore,
            private router: Router) {
            }

我已经尝试通过模拟器运行 id,它似乎应该可以工作。我已经从登录中记录了用户 ID,它与它应该寻找的文档名称相同。

这一切似乎都应该起作用,但事实并非如此。我已经阅读了一堆教程,但没有一个做任何巨大的不同 - 教程之间的差异是不一致的。

我认识到代码在编写方式上并不完美,但我想先让它发挥作用。

我在数据库中的数据结构是这样的:

文档的ID是相关人员的用户ID,我仔细检查了它们都是正确的。

如果我允许通用读写,请仔细检查它,否则一切正常......只有当这些规则出现时它们才不起作用。

【问题讨论】:

  • 请添加初始化fAuthfirebase 的方式。在这种情况下,firebase 似乎也不是 firebase SDK 而是 Firestore 实例 - 您应该将其重命名为 firestoredb 等其他名称。按照惯例,您应该考虑变量保留 "firebase" 的名称 - 像 undefined 一样对待它,不要将其重新分配给其他任何东西。
  • 就在构造函数中:constructor(private fAuth: AngularFireAuth, private firebase: AngularFirestore, private router: Router) {}... db 是我的应用程序中的服务名称,但可能在其中私下它可能改名就好,谢谢。
  • 虽然这只是告诉我它们是如何传递到您的组件中的,但它并没有涵盖您在何处以及如何初始化它们。我正在寻找您在哪里使用initializeApp() 等等。
  • 哦,这在我的应用模块文件中:imports: [ AngularFireModule.initializeApp(environment.firebaseConfig) ]。配置参数取自项目,但现在要仔细检查它们......(编辑:一切看起来都很好,firebaseConfig 位于正确的位置,并且与 firebase 控制台上的细节相同)。

标签: typescript firebase google-cloud-firestore firebase-security angularfire


【解决方案1】:

您必须指定要匹配的文档,在这种情况下,它是所有文档(递归)。这是规则:

  rules_version = '2';
  service cloud.firestore {
  match /databases/{database}/documents {
    
    // match logged in user doc in users collection
    match /users/{userId}/{documents=**} {
      allow create: if request.auth.uid != null;
      allow read: if request.auth.uid == userId;
    }
    
    // match docs in the users collection
    match /users/{userId}/{documents=**} {
        allow read, write: if request.auth.uid != null;
    }
  }
}

评论后编辑:根据https://firebase.google.com/docs/firestore/security/rules-structure#version-2“在第 2 版的安全规则中,递归通配符匹配零个或多个路径项。match/cities/{city}/{ document=**} 匹配任何子集合中的文档以及城市集合中的文档。”。我猜上面的代码解决了子集合问题。如果您仍然遇到问题,请分享您的数据结构,以便人们可以更好地提供帮助。

第二次编辑:我猜这个问题是因为 request.auth 为空。如果有空指针,Firebase 会抛出错误。所以在 request.auth.uid 之前,您必须检查 request.auth 是否为空。像这样:

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

    // match logged in user doc in users collection
    match /users/{userId}/{documents=**} {
      allow create: if request.auth != null && request.auth.uid != null;
      allow read: if request.auth != null && request.auth.uid == userId;
    }
    
    // match docs in the users collection
    match /users/{userId}/{documents=**} {
        allow read, write: if request.auth != null && request.auth.uid != null;
    }
  }
}

第三次编辑:我昨天遇到了同样的问题(规则很好,当所有规则都设置为 true 时它可以工作)但奇怪的是它只发生在移动设备上。我在 GitHub 上搜索后发现了这个问题:https://github.com/angular/angularfire/issues/2838。该问题是由 Firebase 版本引起的。我的版本是8.6.2,所以我降级到8.6.1,问题就解决了。您可以通过以下方式降级:

npm install firebase@8.6.1

我认为这对 Firebase 来说是一个非常严重的问题,它阻碍了人们使用 Firebase 后端开发和维护项目。我希望这也能解决您的问题。

【讨论】:

  • 我担心同样的问题 - 虽然文档的子文件夹是一个集合,所以 document=** 可能无法解决数据库中的任何问题。
  • 嗨,谢谢,我在主帖中添加了数据结构。您是否知道我可以检查发送到 Firebase 的有效负载?也许这将有助于诊断问题...
  • 嘿,我继续感谢帮助,但问题相同。登录工作完美,除非我尝试从数据库中读取,所以 .doc() 仍然是导致问题的原因。同样的错误消息:/我也认为如果有用户登录,request.auth 不应该为空......
  • 登录肯定是异步的 - 你知道在你的 .get() 之前实际上已经完成了身份验证吗?这是使用 auth 侦听器的有用之处。仅请求身份验证 [this.fAuth.signInWithEmailAndPassword(email, password)] 是不够的;实际授权对此是 ANSYNCHRONOUS 。 firebase.google.com/docs/reference/js/firebase.auth.Auth
  • 我试过了,它没有用,现在我再试一次,它可以。甚至可以确定我改回了较新的版本,它又坏了,然后又回到 8.6.1,它又可以工作了。太感谢了,赏金!!!将通过修复更新主要帖子。
【解决方案2】:

让我们尝试一种较少角度的方法,因为您有一个奇怪的Observable 对象链,但每个操作只执行一次。因此,我们不会将每个步骤转换为 Observable 并将它们链接起来,而是使用并返回原生的 Promise 链,您可以稍后将其转换为 Observable

此外,收集用户的令牌和用户数据的快照可以同时完成,因为它们不相互依赖。您也无需强制刷新用户的 ID 令牌,因为它已作为上一个登录步骤的一部分进行了刷新。

注意:不鼓励使用firebase 作为AngularFirestore 实例的变量名。 firebase 应被视为为 Firebase 客户端 SDK 本身保留,以防止混淆。因此,在此代码示例中,我将其重命名为 firestore,尽管您也可以使用 db 或其他名称。

login2(email: string, password: string): Promise<void> {
    return this.fAuth.signInWithEmailAndPassword(email, password)
        .then((result) => {
            const userDocRef = this.firestore
                .collection('users')
                .doc(result.user.uid);

            return Promise.all([
                Promise.resolve(result.user),    // User object
                result.user.getIdTokenResult(),  // User's decoded ID token
                userDocRef.ref.get()             // User data in Firestore (from the wrapped Firebase SDK DocumentReference)
            ]);
        })
        .then(([user, tokenData, userDataSnapshot]) => {
            this.handleAuthentication( // <-- if this is a Promise, return it
                user.email, 
                user.uid,
                userDataSnapshot.get('name'), // <- more efficient than snap.data().name
                tokenData.claims.admin,
                tokenData.token
            );
        });
}

要转换为Observable,您可以使用:

login2(email: string, password: string): Observable<any> {
    const signInPromise = /* ... code from above ... */

    return from(signInPromise);
}

【讨论】:

  • 谢谢,但同样的问题 - 我得到了身份验证问题 ERROR Error: Uncaught (in promise): FirebaseError: [code=permission-denied]: Missing or insufficient permissions. FirebaseError: Missing or insufficient permissions. 。我确实喜欢您执行登录的方式,但是更简洁,并且感谢命名约定提示,我现在已经在代码中更改了它。我的身份验证规则仍然是match /users/{userId}} { allow create: if request.auth.uid != null; allow read: if request.auth.uid == userId; }
  • 啊,这对我手动输入不好,我现在看到 AngularFire overloaded 原生 Firebase SDK 的 get() 带有 Observable-returning 方法。我们可以绕过它,使用userDocRef.ref.get() 拉出原生的Promise-returning 方法。
  • @Bogomip 如果此代码仍然无法正常工作,则问题可能是由于 AngularFirestoreAngularFireAuth 被初始化为两个独立的 Firestore 应用程序造成的。这意味着当您登录fAuth 使用的应用程序实例时,firestore 使用的应用程序实例尚未经过身份验证。您需要在问题中说明您是如何设置和初始化这些的。
  • @Bogomip 可以将所有相关文件(删除所有不相关的文件,但它仍然运行,就像MWE)收集到 ZIP 文件(package.json、AppModule、Component/Service with登录,等等)发送到my email,所以我可以看看。理想情况下,我应该能够运行npm install,然后立即开始运行它。
  • 您是否尝试过将 Firebase 降级到 8.6.1,正如我在回答中所说的那样?我相信这可能是问题所在。
【解决方案3】:

登录肯定是异步的 - 你知道在你的 .get() 之前实际上已经完成了身份验证吗?这是使用 auth 侦听器的有用之处。仅请求身份验证 [this.fAuth.signInWithEmailAndPassword(email, password)] 是不够的;实际授权对此是 ANSYNCHRONOUS 。 https://firebase.google.com/docs/reference/js/firebase.auth.Auth

我使用一系列异步调用和侦听器来登录/注销 - 我正在将其中的 一些 构建到一个库中,但是所需的侦听器和状态更改超出了图书馆。

【讨论】:

  • 嗨 LeadDreamer,我非常有信心,因为如果我对所有人开放身份验证,它就可以正常工作 - 只有当我在数据库上有规则时它才会起作用。我已经更新了主帖中的代码,所以你可以看到最新的版本...
  • 再次嗨,实际上现在看到如果我将所有规则都设置为 if request.auth.uid != null 它仍然不起作用,所以也许这是一个异步问题......我试着把它全部分开看看是否效果更好。
猜你喜欢
  • 2019-11-15
  • 1970-01-01
  • 2022-01-15
  • 2019-10-24
  • 2020-10-14
  • 1970-01-01
  • 1970-01-01
  • 2022-01-25
  • 2021-10-31
相关资源
最近更新 更多