Open Eomm opened 1 year ago
Good find, sounds like a bug in the way the type-level auth works - would you be up for contributing a PR?
No, sorry, I don't have enough time to work on it
Completely understand, no problem at all!
Hi @Eomm. From your example I think the issue is your Grid resolveType function. You're checking for obj.adminColumn
and obj.moderatorColumn
both of which do not exist, unless i'm missing something. Here is a working version based on the example from apollographql
Grid: {
resolveType(obj, context, info) {
if (obj.totalRevenue) return "AdminGrid";
if (obj.banHammer) return "ModeratorGrid";
return "UserGrid";
},
}
Another option is to use the user role from the context like so:
Grid: {
resolveType(obj, context, info) {
const role = context.auth.identity;
if (role === "admin") return "AdminGrid";
if (role === "moderator") return "ModeratorGrid";
return "UserGrid";
},
},
Here are some unit tests which i've successfully tested locally:
"use strict";
const { test } = require("tap");
const Fastify = require("fastify");
const mercurius = require("mercurius");
const mercuriusAuth = require("..");
const schema = `
directive @auth(role: String) on OBJECT
union Grid = AdminGrid | ModeratorGrid | UserGrid
type AdminGrid @auth(role: "admin") {
totalRevenue: Float
}
type ModeratorGrid @auth(role: "moderator") {
banHammer: Boolean
}
type UserGrid @auth(role: "user") {
basicColumn: String
}
type Query {
searchData: Grid
}
`;
const resolvers = {
Query: {
searchData: async function (root, args, context, info) {
return {
totalRevenue: 42,
banHammer: true,
basicColumn: "basic",
};
},
},
Grid: {
resolveType(obj, contextValue, info) {
const role = context.auth.identity;
if (role === "admin") return "AdminGrid";
if (role === "moderator") return "ModeratorGrid";
return "UserGrid";
},
},
};
test("A user with the `admin` role should only be able to retrieve the `totalRevenue` field", (t) => {
const app = Fastify();
t.teardown(app.close.bind(app));
app.register(mercurius, {
schema,
resolvers,
});
app.register(mercuriusAuth, {
authContext(context) {
return {
identity: context.reply.request.headers["x-user"],
};
},
async applyPolicy(policy, parent, args, context, info) {
const role = policy.arguments[0].value.value;
return context.auth.identity === role;
},
authDirective: "auth",
});
const request = (query) => {
return app.inject({
method: "POST",
headers: { "content-type": "application/json", "X-User": "admin" },
url: "/graphql",
body: JSON.stringify({ query }),
});
};
t.plan(3);
t.test("should be able to retrieve the `totalRevenue` field", async (t) => {
const query = `query {
searchData {
... on AdminGrid {
totalRevenue
}
}
}`;
const response = await request(query);
t.same(JSON.parse(response.body), {
data: {
searchData: {
totalRevenue: 42,
},
},
});
});
t.test("should not be able to retrieve the `banHammer` field", async (t) => {
const query = `query {
searchData {
... on ModeratorGrid {
banHammer
}
}
}`;
const response = await request(query);
t.same(JSON.parse(response.body), {
data: {
searchData: {},
},
});
});
t.test(
"should not be able to retrieve the `basicColumn` field",
async (t) => {
const query = `query {
searchData {
... on UserGrid {
basicColumn
}
}
}`;
const response = await request(query);
t.same(JSON.parse(response.body), {
data: {
searchData: {},
},
});
}
);
t.end();
});
Given this schema:
and this plugin setup:
The
applyPolicy
function is never executed.If I change the schema to:
The function is executed instead.
Here a complete code example + test (skipped) https://github.com/Eomm/fastify-discord-bot-demo/pull/30/commits/7ec5f23bdba2458f0f7027c95b32175c01d3a7fc