Skip to main content
GraphQL Basics
CHAPTER 16 Beginner

Authorization and Security Best Practices

Updated: May 13, 2026
25 min read

# 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 name and jobTitle. But only the HR Manager role can query the salary field. 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:

php
123456789101112131415161718192021
'deletePost' => [
    'type' => Type::boolean(),
    'args' => ['id' => Type::nonNull(Type::id())],
    'resolve' => function ($root, $args, $context) {
        $user = $context['user'];
        
        // 1. Are they logged in?
        if (!$user) throw new Exception("Not authenticated");
        
        $post = fetchPostFromDB($args['id']);
        
        // 2. Are they AUTHORIZED to delete this specific post?
        // (Must be an Admin, OR the author of the post)
        if ($user['role'] !== 'ADMIN' && $user['id'] !== $post['author_id']) {
            throw new Exception("You do not have permission to delete this post.");
        }
        
        // 3. Execute logic
        return deletePostFromDB($args['id']);
    }
]

6. Field-Level Security

You can apply the exact same logic from Section 5 to a single scalar field!
php
1234567891011
// Inside the User Object definition
'socialSecurityNumber' => [
    'type' => Type::string(),
    'resolve' => function ($userRoot, $args, $context) {
        // Only return the SSN if the logged in user is looking at their OWN profile
        if ($context['user']['id'] === $userRoot['id']) {
            return $userRoot['ssn'];
        }
        return null; // Or throw an error
    }
]

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:

php
123456
use GraphQL\Validator\Rules\QueryDepth;
use GraphQL\Validator\DocumentValidator;

// Limit maximum nesting to 5 levels deep
$rule = new QueryDepth(5);
DocumentValidator::addRule($rule);

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. 1. If you restrict access to the email field inside a User type, is that Authentication or Authorization?
  1. 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 called publishArticle. 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

Question 1

What is the difference between Authentication and Authorization?

Question 2

What is Query Depth Limiting designed to prevent?

Q3. True or False: You can apply authorization checks to individual fields (like a salary), not just root queries. A) True B) False *Answer: A*

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.

16. Summary

In this chapter, we learned that because GraphQL is so flexible, it requires robust security. We learned how to implement Authorization checks inside our resolvers to ensure users can only see and modify data they own. We also explored critical GraphQL-specific security measures, namely Query Depth Limiting and Query Complexity Limiting, to protect our database from malicious or overly expensive queries.

17. Next Chapter Recommendation

Even with perfect security, things will eventually go wrong. Databases go down, validation fails, and users type bad inputs. Proceed to Chapter 17: Error Handling in GraphQL to learn how to properly format and return error messages to the frontend.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·