问题描述
背景
我正在为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
受用户限制,并且需要用户令牌来创建它,但这是在用户上下文之外但受信任的。
我能想到的选项
- 在第二种情况下,传递伪造的用户令牌以构建服务
- 在创建服务时不需要用户令牌,而是将其传递给每个方法(将问题推到其他位置)
- 将授权逻辑放入外部控制器(GQL解析器或Webhook处理程序)中,并使
roomService
完全具有特权,并假定应该进行这些更改。在这种情况下,控制器将不得不从数据库中获取数据以进行授权。 - 具有两层服务-一层完全具有特权,可以接收
userId
作为其方法的参数,另一层则是用户限制的,因为GQL层现在使用该服务。 Webhook直接使用特权层。 - 我确定还有其他人。
问题
- 这个问题是否已经在某个地方描述,命名和解决过?
- 如果是的话,那是什么?!
- 如果没有,以上选项听起来如何?
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)