Skip to content

GraphQL authorization policy

The GraphQL authorization policy (graphql-authz) controls access to individual query and mutation fields of a GraphQL API. It checks the OAuth scopes and JWT claims of each request against rules that you configure per field.

A GraphQL API has a single endpoint, so the gateway can't attach a policy to individual operations at the route level. The graphql-authz policy inspects the request body instead, and applies your rules to each root field that the operation selects.

Before you begin

Attach an authentication policy, such as jwt-auth, before graphql-authz in the policy order. The authentication policy validates the token and makes its scopes and claims available to graphql-authz. For the list of available policies, see the Policy Hub.

How rules match fields

You configure rules in three places:

  • queries: rules for root fields of the Query type, by field name.
  • mutations: rules for root fields of the Mutation type, by field name.
  • global: one rule that applies to every query and mutation root field.

In queries and mutations, a rule named * matches every field of that type.

Every rule that matches a field applies, and all of them must grant access. A * rule or the global rule doesn't replace a field's own rule. If a field has its own rule and a * rule, the request must satisfy both.

If no rule matches a field, the policy doesn't govern that field. The request passes through for that field, and the rest of the API's policy chain decides whether to allow it.

Parameters

The policy takes the following parameters. You must set at least one of them.

Parameter Type Description
queries Array of rules Rules for root Query fields. When present, it must contain at least one rule.
mutations Array of rules Rules for root Mutation fields. When present, it must contain at least one rule.
global Object A scopes or claims requirement that applies to every query and mutation root field, in addition to any matching rule.

Each rule in queries or mutations takes the following fields. A rule must set at least one of scopes or claims, and the global rule takes the same fields except name.

Field Required Description
name Yes The field name to authorize, such as books or addBook, or * to match every field of the type.
scopes No The scopes that the token must carry. Use allOf to require every listed scope, anyOf to require at least one, or both.
claims No The claims that the token must carry. Use allOf, anyOf, or both. Each entry has a claim name and a list of accepted values.

If you set both allOf and anyOf, the request must satisfy both conditions. A claim matches when its value is one of the listed values.

How the policy evaluates a request

For each POST request, the policy runs these steps:

  1. Parse the GraphQL request body, and select the operation to run. If the document defines more than one operation, the request must set operationName.
  2. Collect the root fields that the operation selects, including fields inside fragments. The policy ignores __typename.
  3. Find every rule that matches each root field.
  4. If no rule matches any field, forward the request without further checks.
  5. If at least one field has a matching rule, require an authenticated request. If the request isn't authenticated, return 401.
  6. Check every matching rule for every governed field. If any rule fails, return 403.
  7. If every rule passes, forward the request unchanged.

A request can select more than one root field, for example query { books authors }. The request succeeds only if it satisfies the rules for every governed field.

Attach the policy

To attach the policy in the console, follow these steps:

  1. Open the GraphQL API, and then select Develop > Policies.
  2. Attach the jwt-auth policy, and configure your identity provider.
  3. Find graphql-authz in Available policies, and configure its rules.
  4. Select Attach policy, and then select Save.
  5. Redeploy the API. For more information, see Deploy a GraphQL API.

For more information about the Policies page, see Apply policies to a GraphQL API.

To attach the policy by using the Platform API, add it to the policies array of the API metadata. For more information, see Manage GraphQL APIs with the Platform API.

Examples

The examples show the graphql-authz parameters for a bookstore API with books and authors queries, and addBook and deleteBook mutations.

Example 1: Basic field access control

The following configuration requires books:read for the books query and books:write for the addBook mutation:

queries:
  - name: books
    scopes:
      anyOf:
        - "books:read"
mutations:
  - name: addBook
    scopes:
      anyOf:
        - "books:write"
Request Caller Result
query { books { id } } Has books:read Authorized
mutation { addBook(title: "Dune") { id } } Has books:read only 403, because addBook requires books:write
query { books { id } } Not authenticated 401, because a rule governs books
query { authors { id } } Any caller Not governed by this policy, because no rule matches authors

Example 2: Type-wide wildcard

The following configuration adds a * rule, so every query field also requires api:read:

queries:
  - name: books
    scopes:
      anyOf:
        - "books:read"
  - name: "*"
    scopes:
      anyOf:
        - "api:read"
Request Caller Result
query { authors { id } } Has api:read Authorized, because only the * rule matches authors
query { books { id } } Has api:read only 403, because the books rule also applies and requires books:read
query { books { id } } Has books:read and api:read Authorized

Example 3: Global baseline

The following configuration requires api:access for every query and mutation field, in addition to the field-level rules:

queries:
  - name: books
    scopes:
      anyOf:
        - "books:read"
mutations:
  - name: addBook
    scopes:
      anyOf:
        - "books:write"
global:
  scopes:
    anyOf:
      - "api:access"

Fields without their own rule, such as authors, now require api:access instead of passing through.

Request Caller Result
query { authors { id } } Has api:access Authorized, because only the global rule matches authors
query { books { id } } Has api:access only 403, because the books rule requires books:read
query { books { id } } Has books:read only 403, because the global rule requires api:access
query { books { id } } Has books:read and api:access Authorized

Example 4: Claim-based rules

The following configuration restricts deleteBook to callers whose role claim is admin:

mutations:
  - name: deleteBook
    claims:
      allOf:
        - claim: role
          values: ["admin"]
queries:
  - name: books
    scopes:
      anyOf:
        - "books:read"
  - name: authors
    scopes:
      anyOf:
        - "authors:read"
Request Caller Result
mutation { deleteBook(id: "1") } Claim role=admin Authorized
mutation { deleteBook(id: "1") } Claim role=user 403, because the claim value doesn't match
query { books { id } authors { id } } Has books:read only 403, because authors requires authors:read

Example 5: Claims only

Rules don't need scopes. The following configuration uses only claims:

queries:
  - name: books
    claims:
      anyOf:
        - claim: department
          values: ["sales", "marketing"]
mutations:
  - name: deleteBook
    claims:
      allOf:
        - claim: role
          values: ["admin"]
global:
  claims:
    allOf:
      - claim: tenant
        values: ["acme-corp"]
Request Caller Result
query { books { id } } Claims department=sales and tenant=acme-corp Authorized
query { books { id } } Claims department=engineering and tenant=acme-corp 403, because the department value isn't accepted
mutation { deleteBook(id: "1") } Claims role=admin and tenant=other-corp 403, because the global rule requires tenant=acme-corp
mutation { deleteBook(id: "1") } Claims role=admin and tenant=acme-corp Authorized

Limitations

  • Subscriptions: The policy doesn't govern subscription operations. The gateway doesn't route GraphQL subscriptions.
  • Fields without rules: The policy doesn't evaluate a field that no rule matches. To require a baseline scope or claim for every field, configure global.
  • Adding * or global later: These rules apply in addition to field-level rules. If you add one to an existing configuration, every field that it matches must also satisfy it.