Authentication & Authorization

◕ GraphQLLesson 12Advanced

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.