API后端代码结构-如何分离代码的授权部分和特权部分

问题描述

背景

我正在为Webapp编写一个Web API和webhooks(对于非webhooks来说是GraphQL,但这并不重要,因为这只是外层),其中大部分请求都经过了身份验证,并且授权逻辑略为琐碎。

我正在使用Firestore,该数据库为我提供了类似于“持久层”以及“控制器”(GQL查询/突变或无服务器功能webhooks)的API,以及一个提供服务的“服务层”逻辑和对持久层的调用。这些服务也互相调用,从不读取Firestore中的其他集合。

该应用是团队聊天室服务,因此下面的代码中包含“聊天室”,“成员资格”和“用户”等术语。

对于身份验证,我收到了经过验证的JWT。我没有在这些JWT中使用自定义声明-实际上只是它们中的用户ID。

当前状态

我的服务是这样构造的:

function createRoomService(
  userToken: {uid: string,email_verified: boolean},deps: {
    firestore: FirestoreInstance
    services: {
      user: UserService
    }
  }): RoomService {
  

  return {
    getRoom(id: string) {
      const room = deps.firestore.collection('rooms').get(id)

      if (!room.allowedUsers.includes(userToken.uid)) {
        return null
      }

      return room
    },changeUserStatus(roomId: string,status: string) {
      const room = await this.getRoom(roomId)
      if (!room) {
        throw new Error("No such room or not your room");
      }

      // yes Could do these in a batch and I really do
      await deps.firestore.collection('rooms').doc(id).update({
        users: {[userToken.uid]: {status}}
      })
      await deps.services.user.updateLastSeen();

      return true // succceeded
    }
  }
}

GQL解析器如下所示:


getRoom(parent,args,context) {
  return context.services.roomService.getRoom(args.id)
}

在请求开始时,已经将userToken添加到构造的服务中。

问题

这很好,您会注意到roomService对操作进行自己的授权,并且由于服务在其自己的方法之间进行调用,因此在某种意义上“共享”了部分授权。 / p>

现在,我有一个Webhook呼叫,该呼叫来自受信任的来源,应该会更新用户的状态:

app.post("/subscription-lapsed",(req,res) => {
  verifyRequestIsTrusted(req.headers.appToken)

  const userId = req.body.userId
  const roomId = req.body.subscriptionId

  // I can't do this,because roomService is user-scoped,whereas this is not a user request
  await roomService.changeUserStatus(roomId,"deactivated")
})

我的原始roomService用户限制,并且需要用户令牌来创建它,但这是在用户上下文之外但受信任的。

我能想到的选项

  1. 在第二种情况下,传递伪造的用户令牌以构建服务
  2. 在创建服务时不需要用户令牌,而是将其传递给每个方法(将问题推到其他位置)
  3. 将授权逻辑放入外部控制器(GQL解析器或Webhook处理程序)中,并使roomService完全具有特权,并假定应该进行这些更改。在这种情况下,控制器将不得不从数据库获取数据以进行授权。
  4. 具有两层服务-一层完全具有特权,可以接收userId作为其方法的参数,另一层则是用户限制的,因为GQL层现在使用该服务。 Webhook直接使用特权层。
  5. 我确定还有其他人。

问题

  1. 这个问题是否已经在某个地方描述,命名和解决过?
  2. 如果是的话,那是什么?!
  3. 如果没有,以上选项听起来如何?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)