Secure Your Schema and Endpoint

How to control access to your schemas, endpoints and data sources

As you build out your GraphQL APIs, you may need to add some form of authentication and access control mechanisms on your schemas, to prevent unauthorized use of these endpoints. Examples include:

  • Introspections and calls to some or all schema operations.
  • Variables used in operations.
  • Fields returned from operations.
  • Requests to some or all root operations and operation fields.

You can control who can issue queries and mutations using the following approaches:

Use an Admin Key

We recommend you use an admin key that carries the necessary privileges to allow edits to your schema. While other forms of authentication are also possible, endpoint creation should only be done by users with the highest privilege.

Using an admin key gives the caller full access to the schema and the queries/mutations in it, including diagnostics.

Issue the call by including the Authorization header in a request:

-H "Authorization: Apikey ...."

Use an App Key

To allow apps to execute queries and mutations on your endpoint(s), you can provide app developers with API keys to incorporate into their apps. The apps then call the GraphQL API using the app key.

The key gives the app full access to the schema and the queries/mutations in it (but no diagnostics).

Issue the call by including the Authorization header in a request:

-H "Authorization: Apikey ...."

See the blog: Spotify OAuth 2.0 Tokens in 3 Steps for information on how to bundle an access token request using StepZen's @sequence directive.

Use no Keys or JWTs

While apps can embed API keys, the API keys need to remain a secret, which means there needs to be a separate server that communicates with your GraphQL API.

Since that is not always desirable, we support the following forms of access:

Unauthenticated Access with no Conditions

While API keys (discussed above) work great, their usage assumes that you are running a trusted app where you can safely hide the API key. For browser apps however, you cannot simply hide your API key in browser code because anyone who opens the code will see it. This means you'll need an intermediary entity between the browser and your GraphQL endpoint, which isn't always desirable or practical.

You can control access to your GraphQL endpoint when you cannot depend on the API key, by making certain queries accessible without any authentication.

For example, if you have a productListing query that you want anyone to use, add the following to your config.yaml:

fieldbasedrules:
  - rootoperationtype: Query
    publicfields:
      - myPublicQuery1
      - myPublicQuery2
    privatefieldspredicate: false

In this example, myPublicQuery1 and myPublicQuery2 are the two queries to be made public.

Unauthenticated requests made to myPubllicQuery1 and myPublicQuery2 will succeed, whereas an unauthenticated request to myPublicQuery3 will fail.

Note: In the future, StepZen will support public mutations, but for now, only fields of type Query can be made public (hence rootoperationtype: Query). The yaml entry: privatefieldspreciate: false can be ignored for now.

Unauthenticated Access with Conditions

As you can see from the example in the previous section, it is all or nothing. However, you might want to give unauthenticated access based on some conditions.

For example, consider a query like itemsOnSale (month: Int!) which displays the items on sale for a specific month. To ensure that users do not get a sneak peek at items you might have on sale next month, you may only want to allow a value of 11 to be passed to the query for this month, and a value of 12 next month.

This can be achieved by setting fieldbasedrules (you will have to follow this example but use your GraphQL endpoint for the right query names, variable names, etc.):

- rootoperationtype: Query
  privatefieldspredicate: "itemsOnSale.month == 11"

This ensures that a query like the following will succeed:

  itemsOnSale (month: 11) {
    name
    ...
  }
}

But when month is set to any other value, the query will fail.

Authenticated Access Using a JWT

Follow the steps below to enable authenticated access using a JWT:

  1. Add the following at the top of your config.yaml:

    deployment:
      identity:
        keys:
          - algorithm: HS256
            key: my-little-secret

    Note: The string my-little-secret can be anything you choose. As long as you issue JWTs with the key that you recorded above, then the GraphQL using that JWT will be accepted as a valid call, when received by your endpoint.

  2. Add the following to the bottom of your config.yaml (where you had fieldbasedrules):

    - rootoperationtype: Query
      privatefieldspredicate: "?jwt"
  3. Try this out by navigating to JWT.io and entering my-little-secret in the VERIFY SIGNATURE on the right side (and keep the PAYLOAD: DATA section as blank. For example, {}).

  4. Copy the encoded version on the left which should look similar to the following:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.QtmAB4MXp1A9X0yJXTtag0bXsmj9A4p1l9MgHF    DCOnY
  5. Issue a query to your endpoint with -H Authorization: Bearer ey... to verify that it works successfully. You can further verify that the token works, by changing the value of the token by one character causing the call to fail.

Use JWT Claims to Further Restrict Access

The JWT in the previous section was only used to check if the caller was authenticated.

JWTs also support claims like "I am user X".

Follow the steps below to use JWT claims to further restrict access:

  1. Navigate to JWT.io and set PAYLOAD: DATA to be:

    {
      "user": "john.doe@example.com"
    }

    This generates a new JWT token like this:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiam9obi5kb2VAZXhhbXBsZS5jb20ifQ    .QCj6sKSgw08Ob2_-XOg6JMsGugWvRHxvZs7yQ_nStes
  2. Change your config.yaml to contain the following predicate:

    privatefieldoperations: '$jwt.user: String == "john.doe@example.com"'
  3. Try out the query to verify that it works successfully. Only the JWT token with that value of user in its PAYLOAD: DATA will be allowed. You can further verify that the claim works, by changing the value of user by one character causing the call to fail.

    Note: You can also fetch the name of the user from a variable and explicitly set access permission, as shown in the following example:

    query: customerOrder condition: $jwt.user == $variables.email access:

Overall rules

  1. An admin key or an app key gives you full access to the schema.
  2. A fieldbasedrules section in your config.yaml permits unauthenticated and JWT authenticated requests:

    • By setting publicfields, these queries can be accessed without authentication.
    • By setting privatefieldspredicate, you can control who can access any query that is not in publicfields. You have access to $now, $jwt (including claims in JWT), $variables, and $operationName to set access conditions.
    • If you are using JWT, then you need to tell StepZen how to verify the correctness of JWT. That is accomplished through the deployment specification at the beginning of your config.yaml (as described above).

This site uses cookies: By using this website, you consent to our use of cookies in accordance with our Website Terms of Use and Cookie Policy.