Authentication & Authorization
GraphQL has no built-in auth mechanism. Authentication (who you are) and authorization (what you can do) are implemented via the context object and resolver-level guard checks. Schema-level directives offer a declarative alternative.
1 Context-Based Auth Guards
JavaScript — Auth in Resolvers
const { GraphQLError } = require("graphql");
// Reusable auth helpers
function requireAuth(context) {
if (!context.currentUser) {
throw new GraphQLError("Authentication required", {
extensions: { code: "UNAUTHENTICATED" }
});
}
return context.currentUser;
}
function requireRole(context, ...roles) {
const user = requireAuth(context);
if (!roles.includes(user.role)) {
throw new GraphQLError("Insufficient permissions", {
extensions: { code: "FORBIDDEN", requiredRoles: roles }
});
}
return user;
}
// Usage in resolvers
const resolvers = {
Query: {
me: (_, __, ctx) => requireAuth(ctx),
users: (_, __, ctx) => { requireRole(ctx, "ADMIN"); return User.find(); },
},
Mutation: {
deleteUser: async (_, { id }, ctx) => {
requireRole(ctx, "ADMIN");
await User.findByIdAndDelete(id);
return true;
},
updatePost: async (_, { id, input }, ctx) => {
const user = requireAuth(ctx);
const post = await Post.findById(id);
if (!post) throw new GraphQLError("Post not found", { extensions: { code: "NOT_FOUND" } });
if (post.authorId.toString() !== user.id && user.role !== "ADMIN") {
throw new GraphQLError("You can only edit your own posts", { extensions: { code: "FORBIDDEN" } });
}
return Post.findByIdAndUpdate(id, input, { new: true });
}
}
};
2 Schema-Level Auth Directive
GraphQL SDL — @auth Directive
directive @auth(requires: Role = USER) on FIELD_DEFINITION
enum Role { USER EDITOR ADMIN }
type Query {
me: User! @auth
users: [User] @auth(requires: ADMIN)
publicPosts: [Post] # No auth required
}
type Mutation {
createPost: Post! @auth
deleteUser: Boolean @auth(requires: ADMIN)
}
3 Code Challenge
Challenge: Implement a
@auth directive using mapSchema from @graphql-tools/schema that wraps resolvers automatically, checking context.currentUser before executing the field resolver.