diff --git a/website/pages/docs/_meta.ts b/website/pages/docs/_meta.ts index 39ac3a1486..f734d731f6 100644 --- a/website/pages/docs/_meta.ts +++ b/website/pages/docs/_meta.ts @@ -12,6 +12,7 @@ const meta = { 'object-types': '', 'mutations-and-input-types': '', 'authentication-and-express-middleware': '', + 'authorization-strategies': '', '-- 2': { type: 'separator', title: 'Advanced Guides', diff --git a/website/pages/docs/authentication-and-express-middleware.mdx b/website/pages/docs/authentication-and-express-middleware.mdx index c03f444496..f1e5929a41 100644 --- a/website/pages/docs/authentication-and-express-middleware.mdx +++ b/website/pages/docs/authentication-and-express-middleware.mdx @@ -1,6 +1,6 @@ --- -title: Authentication and Express Middleware -sidebarTitle: Authentication & Middleware +title: Using Express Middleware with GraphQL.js +sidebarTitle: Using Express Middleware --- import { Tabs } from 'nextra/components'; @@ -100,3 +100,5 @@ In a REST API, authentication is often handled with a header, that contains an a If you aren't familiar with any of these authentication mechanisms, we recommend using `express-jwt` because it's simple without sacrificing any future flexibility. If you've read through the docs linearly to get to this point, congratulations! You now know everything you need to build a practical GraphQL API server. + +Want to control access to specific operations or fields? See [Authorization Strategies](\pages\docs\authorization-strategies.mdx). \ No newline at end of file diff --git a/website/pages/docs/authorization-strategies.mdx b/website/pages/docs/authorization-strategies.mdx new file mode 100644 index 0000000000..c467ce6640 --- /dev/null +++ b/website/pages/docs/authorization-strategies.mdx @@ -0,0 +1,178 @@ +--- +title: Authorization Strategies +--- + +GraphQL gives you complete control over how to define and enforce access control. +That flexibility means it's up to you to decide where authorization rules live and +how they're enforced. + +This guide covers common strategies for implementing authorization in GraphQL +servers using GraphQL.js. It assumes you're authenticating requests and passing a user or +session object into the `context`. + +## What is authorization? + +Authorization determines what a user is allowed to do. It's different from +authentication, which verifies who a user is. + +In GraphQL, authorization typically involves restricting: + +- Access to certain queries or mutations +- Visibility of specific fields +- Ability to perform mutations based on roles or ownership + +## Resolver-based authorization + +> **Note:** +> All examples assume you're using Node.js 20 or later with [ES module (ESM) support](https://nodejs.org/api/esm.html) enabled. + +The simplest approach is to enforce access rules directly inside resolvers +using the `context.user` value: + +```js +export const resolvers = { + Query: { + secretData: (parent, args, context) => { + if (!context.user || context.user.role !== 'admin') { + throw new Error('Not authorized'); + } + return getSecretData(); + }, + }, +}; +``` + +This works well for smaller schemas or one-off checks. + +## Centralizing access control logic + +As your schema grows, repeating logic like `context.user.role !=='admin'` +becomes error-prone. Instead, extract shared logic into utility functions: + +```js +export function requireUser(user) { + if (!user) { + throw new Error('Not authenticated'); + } +} + +export function requireRole(user, role) { + requireUser(user); + if (user.role !== role) { + throw new Error(`Must be a ${role}`); + } +} +``` + +You can use these helpers in resolvers: + +```js +import { requireRole } from './auth.js'; + +export const resolvers = { + Mutation: { + deleteUser: (parent, args, context) => { + requireRole(context.user, 'admin'); + return deleteUser(args.id); + }, + }, +}; +``` + +This pattern makes your access rules easier to read, test, and update. + +## Field-level access control + +You can also conditionally return or hide data at the field level. This +is useful when, for example, users should only see their own private data: + +```js +export const resolvers = { + User: { + email: (parent, args, context) => { + if (context.user.id !== parent.id && context.user.role !== 'admin') { + return null; + } + return parent.email; + }, + }, +}; +``` + +Returning `null` is a common pattern when fields should be hidden from +unauthorized users without triggering an error. + +## Declarative authorization with directives + +If you prefer a schema-first or declarative style, you can define custom +schema directives like `@auth(role: "admin")` directly in your SDL: + +```graphql +type Query { + users: [User] @auth(role: "admin") +} +``` + +To enforce this directive during execution, you need to inspect it in your resolvers +using `getDirectiveValues`: + +```js +import { getDirectiveValues } from 'graphql'; + +function withAuthCheck(resolverFn, schema, fieldNode, variableValues, context) { + const directive = getDirectiveValues( + schema.getDirective('auth'), + fieldNode, + variableValues + ); + + if (directive?.role && context.user?.role !== directive.role) { + throw new Error('Unauthorized'); + } + + return resolverFn(); +} +``` + +You can wrap individual resolvers with this logic, or apply it more broadly using a +schema visitor or transformation. + +GraphQL.js doesn't interpret directives by default, they're just annotations. +You must implement their behavior manually, usually by: + +- Wrapping resolvers in custom logic +- Using a schema transformation library to inject authorization checks + +Directive-based authorization can add complexity, so many teams start with +resolver-based checks and adopt directives later if needed. + +## Best practices + +- Keep authorization logic close to business logic. Resolvers are often the +right place to keep authorization logic. +- Use shared helper functions to reduce duplication and improve clarity. +- Avoid tightly coupling authorization logic to your schema. Make it +reusable where possible. +- Consider using `null` to hide fields from unauthorized users, rather than +throwing errors. +- Be mindful of tools like introspection or GraphQL Playground that can +expose your schema. Use caution when deploying introspection in production +environments. + +## Additional resources + +- [Anatomy of a Resolver](./resolver-anatomy): Shows how resolvers work and how the `context` +object is passed in. Helpful if you're new to writing custom resolvers or +want to understand where authorization logic fits. +- [GraphQL Specification, Execution section](https://spec.graphql.org/October2021/#sec-Execution): Defines how fields are +resolved, including field-level error propagation and execution order. Useful +background when building advanced authorization patterns that rely on the +structure of GraphQL execution. +- [`graphql-shield`](https://github.com/dimatill/graphql-shield): A community library for adding rule-based +authorization as middleware to resolvers. +- [`graphql-auth-directives`](https://github.com/the-guild-org/graphql-auth-directives): Adds support for custom directives like +`@auth(role: "admin")`, letting you declare access control rules in SDL. +Helpful if you're building a schema-first API and prefer declarative access +control. + + diff --git a/website/pages/docs/index.mdx b/website/pages/docs/index.mdx index 7f1e61f04d..3b45c15e4b 100644 --- a/website/pages/docs/index.mdx +++ b/website/pages/docs/index.mdx @@ -3,16 +3,17 @@ title: Overview sidebarTitle: Overview --- -GraphQL.JS is the reference implementation to the [GraphQL Specification](https://spec.graphql.org/draft/), it's designed to be simple to use and easy to understand -while closely following the Specification. +GraphQL.js is the official JavaScript implementation of the +[GraphQL Specification](https://spec.graphql.org/draft/). It provides the core building blocks +for constructing GraphQL servers, clients, tools, and utilities in JavaScript and TypeScript. -You can build GraphQL servers, clients, and tools with this library, it's designed so you can choose which parts you use, for example, you can build your own parser -and use the execution/validation from the library. There also a lot of useful utilities for schema-diffing, working with arguments and many more. +This documentation site is for developers who want to: -In the following chapters you'll find out more about the three critical pieces of this library +- Understand how GraphQL works +- Build a GraphQL API using GraphQL.js +- Extend, customize, or introspect GraphQL systems +- Learn best practices for using GraphQL.js in production -- The GraphQL language -- Document validation -- GraphQL Execution - -You can also code along on [a tutorial](/docs/getting-started). +Whether you're writing your own server, building a GraphQL clients, or creating tools +that work with GraphQL, this site guides you through core concepts, APIs, and +advanced use cases of GraphQL.js.