Open altschuler opened 7 years ago
@altschuler I think it should be the responsibility of lower middlewares as pointed out by this article of @helfer. Btw, if you're using Hapi, hapi-nes provides subscription filters
that you may be interested in.
@altschuler , @helfer : Would you please tell me what's the difference between withFilter
and resolve functions. Are they the same?
@anhldbk The problem is not where to do authorization, but rather that if a user subscribes to something that user is not authorized to, that subscription should not be allowed, and somehow cancelled or denied. As it is, the subscription will be made, but will simply never trigger if the user does not have permission to view the results. It's a solution that works, but it feels somewhat like a hack.
As for your question, resolve
enables you to manipulate what the subscription publishes, whereas subscribe
in combination with withFilter
enables you to filter out results that you don't want to publish under specific circumstances (eg user/state). Kind of like the difference between map and filter :)
@altschuler Thanks for sharing with me.
@altschuler What you said about authorization is related to the protocol behind, right? The specification has nothing about Authorization.
@altschuler Is this what you want?
const subscriptionServer = SubscriptionServer.create({
schema: executableSchema,
execute,
subscribe,
onConnect(connectionParams, webSocket) {
const userPromise = new Promise((res, rej) => {
if (connectionParams.jwt) {
jsonwebtoken.verify(connectionParams.jwt, JWT_SECRET,
(err, decoded) => {
if (err) {
rej('Invalid Token');
}
res(User.findOne({ where: { id: decoded.id, version: decoded.version } }));
});
} else {
rej('No Token');
}
});
return userPromise.then((user) => {
if (user) {
return { user: Promise.resolve(user) };
}
return Promise.reject('No User');
});
},
// to be continued
(from https://github.com/srtucker22/chatty/blob/master/server/index.js#L61)
@anhldbk That only solve where all subscriptions required authentication
but how about just some of them are required..
then based on the code, there would be more lines to check whether or not the subscription path (e.g. foobarUpdated
need to be authenticated)
In Query
and Mutation
, authentication could be performed in directive or resolver.
It would be nice if Subscription
also has a way to do authentication in directive or via a property in resolver object (i.e. the same level as subscribe
and resolve
)
https://github.com/apollographql/graphql-subscriptions/blob/master/.designs/authorization.md does this help at all? It's a very old doc, unfortunately I don't really know much about this topic.
Not sure, if anyone is still interested, but I found a little hackish, but working solution, for refusing connections in resolvers: first you make authorization in onConnect as pointed by @anhldbk (but don't reject the socket yet, just return the status)
onConnect(connectionParams) {
// ... do authorization
return { authorized: false }; // or true
}
next off, when declaring your topic in subscribtion you can access the .authorized field:
@Subscription({
topics: ({ args, context, payload }) => {
if (!context.authorized )
{
// this gives user error response and cancels subsribtion
throw new AuthenticationError(`Unauthorized user cannot receive info from this socket`);
}
return SOME_TOPIC;
}
})
accountBalanceChangeTopic(
//...
}
throwing error inside topics ( or filters) results in socket connection being closed.
Any new info on securing specific subscriptions? In my case I want to only allow the user to subscribe to their own data (based on an owner field).
i have taken the following approach:
{
onMessage: {
subscribe: async (_, { channel }) => {
// do you auth logic here
if (channel !== 'happy') {
unauthorized() // throw an ApolloError
}
return pubsub.asyncIterator(MESSAGE_TOPIC)
}
}
}
Is this valid?
Not sure if they're best practices, and it's not documented, but see the following two things:
Empirically Apollo Server supports what's needed, it's just not documented well.
I've got a
foobarUpdated
subscription, but I need to authorize the users who subscribe to it as well as validate the variable (if the given id is not a valid foobar id, subscription doesn't make sense).Is there a way to deny/cancel a subscription? I'm using
withFilter
but that only works for filtering after the subscription has been initiated.