自定义令牌的查询权限

时间:2019-02-11 14:08:27

标签: firebase firebase-authentication google-cloud-firestore firebase-security-rules

我有一个自定义令牌,其中包含有效负载 appid 来识别第三方应用。

我想验证对我的数据的读/写,以便:

  1. 用户登录(有一个有效的 uid /令牌)
  2. appid 已在/apps集合中注册
  3. uid appid 是记录中的字段,并且与凭据匹配
  4. (与此问题无关,但有待完善)该记录与文档的架构匹配。

当前最佳解决方案和尚待解决的问题

我最终偶然发现的答案是相当好,但可能会得到改善。

我要做的第一件事是正确识别自定义令牌中提供的有效负载-因为我正在使用云服务函数来生成有效负载(graphcool),所以我的有效负载是default: { appid }而不是仅appid。从那里开始,对权限进行一些重新措词就足以使用我想象中的当前规则成功验证:

service cloud.firestore {
  match /databases/{database}/documents {

    match /sampleData/{type}/{appName}/{record} {
      allow read: if isSignedIn() && isValidApp(database) && ownsExisting() && appIdInExisting()
      allow write: if isSignedIn() && isValidApp(database) && ownsPayload() && appIdInPayload()
    }

    // functions
    function isSignedIn () {
        return request.auth != null
    }

    function isValidApp (database) {
        return exists(/databases/$(database)/documents/apps/$(request.auth.token.appid))
    }

    function ownsExisting () {
        return resource.data.uid == request.auth.uid
    }

    function ownsPayload () {
        return request.resource.data.uid == request.auth.uid
    }

    function appIdInExisting () {
        return resource.data.app_id == request.auth.token.appid
    }

    function appIdInPayload () {
        return request.resource.data.app_id == request.auth.token.appid
    }
  }
}

虽然有人可以做得更好。

  1. 有什么方法可以验证 appid 而不使用现有请求(也不需要编写 if-else if 链)-就像使用数组一样也许直接在规则中?

  2. 我如何确保有效载荷中指定的 appid 与我向客户端第三方应用程序发布服务凭证时所设想的相符?

编辑:原始问题

我认为自己至少可以得到前两个:

service cloud.firestore {
  match /databases/{database}/documents {

    match /sampleData {
      allow read: if isSignedIn() && isValidApp() && ownsExisting() && appIdInExisting()
      allow write: if isSignedIn() && isValidApp() && ownsPayload() && appIdInPayload()
    }

    // functions
    function isSignedIn () {
        return request.auth != null
    }

    function isValidApp () {
        return get(path('apps')).data.child(request.auth.token.appid).exists()
    }

    function ownsExisting () {
        return resource.data.uid == request.auth.uid
    }

    function ownsPayload () {
        return request.resource.data.uid == request.auth.uid
    }

    function appIdInExisting () {
        return resource.data.app_id == request.auth.token.appid
    }

    function appIdInPayload () {
        return request.resource.data.app_id == request.auth.token.appid
    }
  }
}

/apps有1个名为“ sample-app-id”的文档,其中“ sample-app-id”的 id name 字段...在我的令牌中使用它不起作用:FirebaseError: Missing or insufficient permissions

我正在通过服务器上的此函数生成令牌:

var FirebaseAdmin = require('firebase-admin')
var serviceAccount = require('./firebase-service-credentials.json')
var claims = require('./custom-token-claims') // {appid: 'sample-app-id'}

let credential = FirebaseAdmin.credential.cert(serviceAccount)
FirebaseAdmin.initializeApp({ credential })

const generateTokenWithPayload = async id => {
  try {
    const token = await FirebaseAdmin.auth().createCustomToken(id, claims)

    return { data: { token } }
  } catch (err) {
    return { error }
  }
}

module.exports = async event =>
  await generateTokenWithPayload(event.data.userIdentifier)

并且在发布前我要登录-我可以验证这一部分是否正常运行,因为我在firebase控制台的 Authentication-> Users 标签中看到了新的非匿名用户:

— Feb 11, 2019 Feb 11, 2019 smaple-user-id

本质上是客户端代码:

await firebase
      .auth()
      .signInWithCustomToken(token)
      .catch(console.error)
const db = await firebase.firestore()
db.collection(path + this.state.appName).add(payload) 

我正在将具有架构{app_id, app_name, date, metric, uid}的记录发布到 sampleData / metrics / sample-app-name / {auto-generated}

注释:

  • 要注册的应用程序数量很小-如果可能,从财务角度来看,将其设置为权限文件中的静态数组可能是有意义的,而不是< em> get 请求。

  • 大的进步-我刚刚注意到request.auth.token.appid应该是request.auth.token.default.appid,因为我使用的是export default而不是modules.export

1 个答案:

答案 0 :(得分:0)

这个答案很好,但是可能会得到改善。

我要做的第一件事是正确识别自定义令牌中提供的有效负载-因为我正在使用云服务函数来生成有效负载(graphcool),所以我的有效负载是default: { appid }而不是仅appid。从那里开始,对权限进行一些重新措词就足以使用我想象中的当前规则成功验证:

service cloud.firestore {
  match /databases/{database}/documents {

    match /sampleData/{type}/{appName}/{record} {
      allow read: if isSignedIn() && isValidApp(database) && ownsExisting() && appIdInExisting()
      allow write: if isSignedIn() && isValidApp(database) && ownsPayload() && appIdInPayload()
    }

    // functions
    function isSignedIn () {
        return request.auth != null
    }

    function isValidApp (database) {
        return exists(/databases/$(database)/documents/apps/$(request.auth.token.appid))
    }

    function ownsExisting () {
        return resource.data.uid == request.auth.uid
    }

    function ownsPayload () {
        return request.resource.data.uid == request.auth.uid
    }

    function appIdInExisting () {
        return resource.data.app_id == request.auth.token.appid
    }

    function appIdInPayload () {
        return request.resource.data.app_id == request.auth.token.appid
    }
  }
}

虽然有人可以做得更好。

  1. 有什么方法可以验证 appid 而不使用现有请求(也不需要编写 if-else if 链)-就像使用数组一样也许直接在规则中?

  2. 我如何确保有效载荷中指定的 appid 与我向客户端第三方应用程序发布服务凭证时所设想的相符?