如何将 Firebase 数据库锁定给来自特定(电子邮件)域的任何用户?

Posted

技术标签:

【中文标题】如何将 Firebase 数据库锁定给来自特定(电子邮件)域的任何用户?【英文标题】:How do I lock down Firebase Database to any user from a specific (email) domain? 【发布时间】:2016-08-24 21:51:33 【问题描述】:

我有一个使用 Firebase 数据库的小型个人 Firebase 网络应用。我想将此应用程序保护(锁定)给来自单个特定域的任何用户。我想通过 Google 进行身份验证。我不清楚如何将规则配置为“只有来自单个特定域(例如 @foobar.com)的用户才能读取和写入此数据库”。

(我看到的部分问题:很难用足够的信息引导数据库以使这个用例工作。我需要在身份验证时知道用户的电子邮件,但 auth 对象不包含电子邮件。这似乎是一个鸡蛋问题,因为我需要编写引用数据库中数据的 Firebase 规则,但该数据还不存在,因为我的用户无法写入数据库。)

如果auth 有邮箱,那我就可以轻松编写规则了。

提前致谢!

【问题讨论】:

问题:当您说从单个域锁定时,这是否意味着锁定到单个提供商,例如 google,或者它的字面意思是只允许来自 myDomain 的用户访问您的 Firebase,即 test@ myDomain 通过密码验证是可以的,但是 thing@anotherDomain 通过 google 验证是不允许的。 我的意思是,“来自@foobar.com 的任何人,经 Google 验证/认证,都可以读取和写入数据库” 你真的需要规则吗?在某些时候,用户必须输入他们的电子邮件地址才能登录或首先创建他们的帐户,所以你不能在输入时解析它并拒绝任何没有@foobar.com作为域? 不幸的是,坚定的用户可以使用他们浏览器的 DevTools 插入他们想要的任何电子邮件。电子邮件地址不是来自客户端的可信数据。 问题是,我不信任网络浏览器,因为开发工具很容易让恶意用户伪造 oauth 提供者返回的数据。 【参考方案1】:

如果您使用的是新的 Firebase,现在可以这样做了,因为安全规则中提供了 email

在安全规则中,您可以访问电子邮件地址以及它是否经过验证,这使得一些很好的用例成为可能。例如,使用这些规则,只有经过身份验证和验证的 gmail 用户可以编写他们的个人资料:


  "rules": 
    ".read": "auth != null",
    "gmailUsers": 
      "$uid": 
        ".write": "auth.token.email_verified == true && 
                   auth.token.email.matches(/.*@gmail.com$/)"
      
    
  

您可以在项目的Firebase Database console 中输入这些规则。

【讨论】:

firebase.google.com/docs/reference/security/database/#authtoken 包含auth.token 中可用的文档。 看到这样的例子会很酷,它从数据库的根中提取@gmail.com,例如my_database/acceptable_domains/,例如,他们只想将他们的应用程序打开到少数人的电子邮件提供商。例如授权的学校和企业。 是否有不需要对每个域进行硬编码的替代方法 - 例如“gmailUsers”? 您目前无法根据用户的域限制哪些用户可以使用 Firebase 身份验证登录。您可以使用我上面给出的安全规则,根据他们的域限制他们可以访问的数据。 firestore 有什么类似的吗?【参考方案2】:

这里的代码在我的数据库中运行良好,我设置了只有我公司的电子邮件才能读取和写入我的 firebase 数据库数据的规则。


  "rules": 
    ".read": "auth.token.email.matches(/.*@yourcompany.com$/)",


        ".write": "auth.token.email.matches(/.*@yourcompany.com$/)"
      
    

【讨论】:

【参考方案3】:

对我有用的代码。

export class AuthenticationService 

    user: Observable<firebase.User>;

    constructor(public afAuth: AngularFireAuth) 
        this.user = afAuth.authState;
    

    login()
        var provider = new firebase.auth.GoogleAuthProvider();
        provider.setCustomParameters('hd': '<your domain>');
        this.afAuth.auth.signInWithPopup(provider)
        .then(response => 
            let token = response.credential.accessToken;
            //Your code. Token is now available.
        )
    

【讨论】:

【参考方案4】:

警告:不要相信这个答案。只是在这里讨论。

tldr:如果不运行您自己的服务器,我认为这是不可能的。

这是我迄今为止的尝试:


  "rules": 
    ".read": "auth.provider === 'google' && root.child('users').child(auth.uid).child('email').val().endsWith('@foobar.com')",
    ".write": "auth.provider === 'google' && root.child('users').child(auth.uid).child('email').val().endsWith('@foobar.com')",
    "users": 
      "$user_id": 
        ".write": "auth.provider === 'google' && $user_id === auth.uid && newData.child('email').val().endsWith('@foobar.com')"
      
    
  

我相信上面所说的“只允许人们创建一个新用户,如果他们已经过 Google 的身份验证,正在尝试为他们自己写入数据库节点 ($user_id === auth.uid) 并且他们的电子邮件以 foobar.com 结尾”。

但是,有人指出了一个问题:任何网络客户端都可以在将消息发送到 Firebase 之前轻松更改其电子邮件(使用开发控制台)。因此,当存储到 Firebase 中时,我们不能信任用户条目的数据。

我认为我们唯一可以真正信任的是规则中的auth 对象。 auth 对象由 Firebase 的后端填充。而且,不幸的是,auth 对象不包含电子邮件地址。

为了记录,我以这种方式插入我的用户:

function authDataCallback(authData) 
  if (authData) 
    console.log("User " + authData.uid + " is logged in with " + authData.provider + " and has displayName " + authData.google.displayName);
    // save the user's profile into the database so we can list users,
    // use them in Security and Firebase Rules, and show profiles
    ref.child("users").child(authData.uid).set(
      provider: authData.provider,
      name: getName(authData),
      email: authData.google.email
    );

正如您可能想象的那样,坚定的用户可以在这里覆盖email 的值(例如,通过使用 DevTools)。

【讨论】:

正如您所发现的:.write 规则可以控制谁可以写信到某个位置,.validate 规则可以验证他们写的是有效的电子邮件地址。但是无法在安全规则中验证它实际上是该用户的电子邮件地址。为此,您需要运行一些受信任的代码(通常在服务器上),将这个经过验证的电子邮件地址写入数据库中的(否则为只读)位置。【参考方案5】:

这应该适用于任何寻找 Cloud Firestore 选项的人,灵感来自 Frank van Puffelen 的回答。

rules_version = '2';
service cloud.firestore 
  match /databases/database/documents 
    match /document=** 
    // Allows all users to access data if they're signed into the app with an email of the domain "company.com"
      allow read, write: if request.auth.uid != null && request.auth.token.email.matches(".*@company.com$");
    
  

【讨论】:

【参考方案6】:

对于那些真的不想让未经验证的帐户登录的人。也许很脏,但非常有效。

这是我的解决方法(Angular 应用):

this.userService.login(this.email.value, this.password.value).then(data => 
  if (data.user.emailVerified === true) 
    //user is allowed
   else 
    //user not allowed, log them out immediatly
    this.userService.logout();
  
).catch(error => console.log(error));

【讨论】:

以上是关于如何将 Firebase 数据库锁定给来自特定(电子邮件)域的任何用户?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Firebase/Google Analytics 分析用户行为并记录来自华为手机的特定事件?

如何将 S3 存储桶锁定到特定用户和 IAM 角色

如何将子firebase数据库中的特定名称获取到recyclerview?

Firebase 与 Android Java 中的 Google 云消息传递

Flutter:如何显示来自 Firebase 的文本?

如何将来自不同组件的 firebase auth 和 Cloud Firestore 用作单个 Firebase 应用程序