49 Resolver Authentication Based on Context
title: 49 Resolver Authentication Based on Context: An Effective Approach to Authorization in GraphQL date: 2024-06-15 author: Senior Engineer - Medium Publication tags:
- GraphQL
- Authorization
- Security
- Backend
- NodeJS
When we talk about authentication in a GraphQL API, there are so many methods we can use. However, junior developers often get trapped in a single paradigm: handling all authorization at the middleware level. Yet, in modern system development, especially in microservices and at large scale, performing context-based resolver authentication is one of the most powerful, maintainable, and scalable techniques available.
As GraphQL grows ever more popular among backend engineers, the need to build authorization mechanisms that are both granular and universal becomes increasingly crucial. In this article, I’ll discuss the approach I call “49 Resolver Authentication Based on Context” — the number 49 here is simply a marker for the technique that made it easy to recognize back at my former company, and now I’m sharing the logic with everyone.
We’ll break down the concept, code examples, flow diagrams, and best practices through a real-world example that’s simple yet practical. Let’s dive right in!
What Is Resolver Context in GraphQL?
Before we get into the code, let’s first understand the concept of context in GraphQL. In ecosystems like Apollo Server, the context is an object provided on every request and passed along to each resolver. This object typically holds the authenticated user’s data, database connections, and anything else that needs to be global for the duration of a single request cycle.
1// server.js
2const { ApolloServer } = require('apollo-server');
3
4const server = new ApolloServer({
5 typeDefs,
6 resolvers,
7 context: ({ req }) => {
8 // Simulate authentication via bearer token
9 const token = req.headers.authorization || "";
10 const user = authenticateToken(token); // your custom logic
11 return { user };
12 }
13});That way, every resolver can access this context without having to invoke the authentication logic over and over again.
Resolvers, Context, and Authorization
A resolver in GraphQL is responsible for “answering” a client query by running a specific function. For example, say we have a simple resolver:
1const resolvers = {
2 Query: {
3 profile: (_, __, context) => {
4 // context.user will always be available if the token is valid
5 return context.user;
6 }
7 }
8};This is where the “49 Resolver Authentication Based on Context” technique ensures that every resolver respects the user’s context and only runs its logic if the user actually has the right access permissions.
Case Study: Role-based Resolver Guard
Let’s talk about a real scenario: you have three roles — ADMIN, USER, and GUEST. However, only ADMIN is allowed to run a certain query.
User Schema
1type User {
2 id: ID!
3 name: String!
4 role: String!
5}
6
7type Query {
8 getAllUsers: [User!]!
9 profile: User!
10}Resolver Middleware Implementation
One popular pattern in the ecosystem is the resolver decorator using higher-order functions. This keeps the guard reusable and clean.
1function withRole(requiredRole, resolverFn) {
2 return (parent, args, context, info) => {
3 if (!context.user || context.user.role !== requiredRole) {
4 throw new Error('Unauthorized! Forbidden access.');
5 }
6 return resolverFn(parent, args, context, info);
7 };
8}
9
10// Example resolver
11const resolvers = {
12 Query: {
13 getAllUsers: withRole('ADMIN', (_, __, context) => {
14 return fetchAllUsersFromDB();
15 }),
16 profile: (_, __, context) => {
17 if (!context.user)
18 throw new Error("Not authenticated!");
19 return context.user;
20 }
21 }
22};With this pattern, we can easily insert authentication and authorization logic per resolver based on context. Each withRole can be composed with different scenarios.
Simulation: Query Code and Response
Let’s simulate two requests to the GraphQL endpoint:
As ADMIN:
1query { 2 getAllUsers { id name role } 3}With an ADMIN token, we successfully get the user list.
As USER:
1query { 2 getAllUsers { id name role } 3}Response result:
1{ 2 "errors": [ 3 { 4 "message": "Unauthorized! Forbidden access." 5 } 6 ], 7 "data": { 8 "getAllUsers": null 9 } 10}
Simple, readable, and very easy to maintain.
Table: Mapping Roles to Resolver Access
Let’s build a table of role access for each resolver—
| Resolver | ADMIN | USER | GUEST |
|---|---|---|---|
| getAllUsers | ✅ | ❌ | ❌ |
| profile | ✅ | ✅ | ❌ |
Explanation: Authentication Flow Architecture
To make it easier to understand, here is the flow diagram in Mermaid format.
flowchart TD
A[Client Request]
B{Apakah Token Valid?}
C{Role Sesuai?}
D[Return Data]
E[Return Error]
A --> B
B -- Tidak --> E
B -- Ya --> C
C -- Tidak --> E
C -- Ya --> D
From the flow above, you can see the context validation process running from start to finish, ensuring the integrity of authentication across every resolver layer.
Tips & Best Practices
Use Higher-Order Resolver Functions
Patterns likewithRoleorwithAuthmake logic reusable. Avoid writing authentication logic inline within a resolver.Don’t Put Logic in Middleware Alone
Middleware is indeed powerful, but granular authorization should still live in the resolver to preserve maintainability.Simulate a Variety of Contexts
Build a test suite for users with different roles; make sure all the logic runs according to your business rules.Debug and Log the Context
Log the context object during development for observability throughout the debugging stage.
Conclusion
Building a context-based resolver authentication system is an essential technique for backend engineers who want to keep their systems secure, scalable, and readable. The “49 Resolver Authentication Based on Context” method brings best-in-class practices that are easy to adopt for GraphQL APIs.
By leveraging the context in every resolver along with the higher-order function approach for authorization guards, your codebase stays clean without losing the power to expand its access control logic in the future.
If you found this article helpful, don’t forget to share it and leave your feedback in the comments. See you in the next engineering article!
References: