Authorization and Security Best Practices
# CHAPTER 16
Authorization and Security Best Practices
1. Introduction
In the last chapter, we handled Authentication (proving *who* the user is). In this chapter, we focus on Authorization (proving *what* the user is allowed to do) and general server security. Because GraphQL is incredibly flexible and allows clients to request exactly what they want, it also allows malicious actors to request massive amounts of data in an attempt to crash your server. We will learn how to implement Role-Based Access Control (RBAC), limit query depth, and protect against malicious queries.2. Learning Objectives
By the end of this chapter, you will be able to:- Differentiate between Authentication and Authorization.
- Implement Role-Based Access Control inside your GraphQL resolvers.
- Understand the threat of recursive queries and how to stop them using Depth Limiting.
- Explain the concept of Query Complexity and Rate Limiting in a GraphQL context.
3. Beginner-Friendly Explanation
Imagine a hospital. Authentication is checking your ID badge at the front door. You are an Employee. Authorization is checking what rooms your badge opens. Even though you are an employee, you might be a Janitor, not a Surgeon. You shouldn't be able to open the Operating Room.Furthermore, if a patient comes in and asks the receptionist to read them the medical records of *every single person in the city*, the receptionist will refuse, because that request is too complex and would take all day. That is Query Complexity Limiting.
4. Real-World Examples
-
Field-Level Authorization: In a company directory, anyone can query a user's
nameandjobTitle. But only the HR Manager role can query thesalaryfield. GraphQL allows you to secure individual fields, not just the whole object.
-
Preventing DoS Attacks: A hacker writes a query:
author { posts { author { posts { author { posts } } } } }. Without depth limiting, this will crash the server by infinitely looping the database.
5. Implementing Authorization
Authorization should happen *inside* the resolver logic (or delegated to business logic classes).PHP Code Example:
6. Field-Level Security
You can apply the exact same logic from Section 5 to a single scalar field!7. Query Depth Limiting
To prevent the hacker scenario described in Section 4, we must limit how deep a query can nest. Most GraphQL server libraries have this built-in.In webonyx/graphql-php:
8. Query Complexity Limiting
Sometimes a query isn't deep, but it's very "wide" (asking for 10,000 items). Complexity rules allow you to assign a "point value" to fields. A string is 1 point. A database relationship is 10 points. If a query exceeds a total of 100 points, the server rejects it before even executing it.9. Best Practices
-
Delegate Authorization: Don't write 50 lines of permission checks inside the resolver. Pass the request to a dedicated Policy class (e.g.,
$postPolicy->canDelete($user, $post)).
- Disable Introspection in Production: Introspection is the feature that allows GraphQL Playground to auto-generate documentation by asking the server for its schema. Turn this off in production to prevent attackers from mapping your entire database structure.
- Use Rate Limiting: Implement standard API rate limiting (e.g., 100 requests per minute per IP) at the web-server level (Nginx/Apache) or application middleware.
10. Common Mistakes
- Putting Auth Logic in the Schema: You cannot handle complex business authorization purely in the Schema SDL. It must be written in the backend resolver logic.
-
Trusting Client Input: Never trust an argument like
mutation { updateRole(role: "ADMIN") }. The backend must strictly verify if the current user is allowed to execute that mutation.
11. Mini Exercises
-
1.
If you restrict access to the
emailfield inside aUsertype, is that Authentication or Authorization?
-
2.
What technique prevents a query like
album { tracks { album { tracks { album } } } }?
12. Coding Challenges
Challenge 1: Write the conceptual PHP resolver logic for a mutation calledpublishArticle. It should check $context['user']['role']. If the role is "WRITER", it should return true. If it is anything else, it should throw a "Forbidden" exception.
13. MCQs with Answers
What is the difference between Authentication and Authorization?
What is Query Depth Limiting designed to prevent?
14. Interview Questions
- Q: Describe how you would secure a specific field inside an object so that only the owner of the object can view it.
- Q: Explain Query Complexity Limiting. How does it differ from Query Depth Limiting?
- Q: Why is it recommended to turn off Schema Introspection in a production environment?
15. FAQs
Q: Can I use Directives for Authorization? A: Yes! Advanced architectures often use custom server-side directives like@auth(requires: ADMIN) applied directly in the Schema SDL, which automatically wraps the resolver in permission-checking logic.