Auth di GraphQL
Authentication (siapa kamu?) biasanya di transport layer (HTTP header). Authorization (boleh akses apa?) di resolver atau directive level.
Authentication via Context
// Server setup
context: async ({ req }) => {
const token = req.headers.authorization?.replace("Bearer ", "");
let user = null;
if (token) {
try {
user = await verifyToken(token);
} catch {
// Token invalid — user tetap null
}
}
return { user, db: prisma };
}
Authorization di Resolver
const resolvers = {
Query: {
me: (_, __, { user }) => {
if (!user) throw new GraphQLError("Not authenticated", {
extensions: { code: "UNAUTHENTICATED" },
});
return user;
},
adminDashboard: (_, __, { user }) => {
if (!user) throw new GraphQLError("Not authenticated");
if (user.role !== "ADMIN") throw new GraphQLError("Not authorized", {
extensions: { code: "FORBIDDEN" },
});
return getDashboardData();
},
},
};
Auth Directive
# Schema
directive @auth(requires: Role = VIEWER) on FIELD_DEFINITION
type Query {
publicPosts: [Post!]!
me: User! @auth
adminStats: Stats! @auth(requires: ADMIN)
}
// Directive implementation
class AuthDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field) {
const requiredRole = this.args.requires;
const { resolve } = field;
field.resolve = async function (...args) {
const context = args[2];
if (!context.user) throw new GraphQLError("Not authenticated");
if (requiredRole && context.user.role !== requiredRole) {
throw new GraphQLError("Not authorized");
}
return resolve.apply(this, args);
};
}
}
Field-level Authorization
// User bisa lihat profile sendiri, tapi email hanya visible untuk admin
User: {
email: (parent, _, { user }) => {
if (user.id === parent.id || user.role === "ADMIN") {
return parent.email;
}
return null; // Hide email
},
}