Back to Blog
Lesson 43 of the GraphQL: Your First GraphQL Schema & Server course
August 30, 20264 min read

Middleware and Authentication: Securing Your GraphQL API

Learn how to secure your GraphQL API by extracting headers from your server context and validating user authentication tokens within your resolvers.

Close-up of a computer screen displaying an authentication failed message.

Previously in this course, we explored organizing schema files to maintain a clean codebase as our API grows. In this lesson, we move from structural organization to security: we will learn how to extract authentication headers from the request and validate user identity within our GraphQL context.

Authentication at the Gateway

In a traditional REST architecture, you might use CORS configuration or dedicated middleware to handle security. In GraphQL, because we use a single endpoint, we handle authentication primarily by inspecting the incoming HTTP request before it reaches our business logic.

The goal is to pass the user's identity—usually verified via a JSON Web Token (JWT)—into the context object. This makes the user's "authenticated state" available to every resolver that needs it, without the resolvers needing to know the details of how the token was parsed.

Extracting Headers from Context

When you set up your Apollo Server, the context function receives an object containing the req (request) object. We can use this to grab the Authorization header.

Here is how you implement the extraction logic in your server setup:

JAVASCRIPT
const { ApolloServer } = require(CE9178">'apollo-server');

const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: ({ req }) => {
    // 1. Extract the Authorization header
    const authHeader = req.headers.authorization || CE9178">'';
    
    // 2. Simple token parsing logic
    const token = authHeader.replace(CE9178">'Bearer ', CE9178">'');
    
    // 3. Validate and attach user to context
    const user = getUserFromToken(token);
    
    return { user };
  },
});

By placing this logic in the context function, every single resolver in your API now has access to context.user. If user is null or undefined, you know the request is unauthenticated.

Validating User Authentication

Once the user object is in the context, you need to enforce security inside your resolvers. A common mistake is to try and handle this globally; however, GraphQL resolvers are the best place for granular control.

Here is a concrete example of a protected resolver:

JAVASCRIPT
const resolvers = {
  Query: {
    myProfile: (parent, args, context) => {
      // Check if user exists in context
      if (!context.user) {
        throw new Error(CE9178">'You must be logged in to view this profile');
      }
      
      return db.users.findById(context.user.id);
    },
  },
};

This pattern ensures that while the server processes the request, it stops execution immediately if the identity verification fails, preventing unauthorized access to your data layer. This is a fundamental concept in Authentication and Authorization: Secure System Identity Patterns applied specifically to the GraphQL execution lifecycle.

Hands-on Exercise

  1. Update your context: Modify your server's context function to look for a header named x-api-key.
  2. Implement validation: If the key is missing or invalid, do not pass a user object to the context.
  3. Protect a query: Write a resolver for a new query called adminStats that checks context.user and throws a "Forbidden" error if the user is not found or not an admin.

Common Pitfalls

  • Trusting the Client: Never trust the user's claims in the header without verifying the signature of the token. Always use a library like jsonwebtoken to verify that the token was actually issued by your server.
  • Leaking Context: Avoid putting sensitive internal objects (like raw database connection pools) into the context. Only expose what the resolvers strictly need.
  • Ignoring Errors: If authentication fails, throw a standard GraphQL error so the client can handle the failure gracefully instead of receiving a generic HTTP 500 error.

FAQ

Q: Should I use middleware or context for authentication? A: Use context. Middleware is great for HTTP-level tasks, but the GraphQL context is the idiomatic way to pass dependencies and user state into your resolvers.

Q: Can I access the request object inside a mutation? A: Yes, the request object is available via the context argument in all resolvers, including mutations.

Recap

We have successfully bridged the gap between raw HTTP headers and our GraphQL schema. By extracting tokens in the context function and validating that identity in our resolvers, we have added a critical layer of security to our API. This pattern allows us to scale our protection logic as we add more complex queries and mutations to our project.

Up next: We will look at Advanced Error Handling, where we learn how to create custom error classes to give our clients more meaningful feedback when authentication or other logic fails.

Similar Posts