GraphQL offers a wide array of advantages, such as reduced over-fetching of data and understandable description of the data in your API and makes it easier to evolve APIs over time. However, it also brings along its own set of challenges, particularly in security.
One of the security concerns with GraphQL is introspection, which allows clients to query the schema to discover available types, queries, and mutations. While this feature can be incredibly useful during development and debugging, it can also pose a risk if not properly secured, as it can potentially expose sensitive information about the API’s structure and endpoint to malicious actors.
Another significant security consideration is granular authorization, which revolves around controlling access to specific fields and/or operations within a GraphQL schema. This level of fine-tuned control is a powerful feature but can be hard to implement properly.
In this blog post, I will walk you through how Azure API Management can effectively solve these issues by utilizing JWT validation, enforcing authorization rules based on the token’s claims, and deactivating introspection through the GraphQL validation policy.
For this example, I will be using community-built Rick and Morty GraphQL API, which allows clients to query data related to the TV show’s characters, locations, and episodes.
To create this GraphQL API within the Azure API Management service:
For this API I want to have the following security controls enabled:
I will reference these security controls by a number in parenthesis as we add more configurations to the API.
Let’s start with adding JWT token validation:
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized" output-token-variable-name="jwt-token">
<issuer-signing-keys>
<key>{{jwt-signing-key}}</key>
</issuer-signing-keys>
</validate-jwt>
This policy validates the JWT present in the “Authorization” header of incoming requests. It checks the signature of the JWT using the provided signing key (jwt-signing-key), which we specified in the first step. If the JWT is invalid or missing, it returns a 401 Unauthorized response with the error message “Unauthorized”. The validated JWT is stored in the `jwt-token` variable for later use. (1)
Next, we will add GraphQL validation policy to disable introspection and enforce authorization for the selected field.
Add the following policy right after the JWT validation policy :
<validate-graphql-request error-variable-name="validation-error" max-size="102400" max-depth="4">
<authorize>
<rule path="/__*" action="reject" />
<rule path="/Character/created" action="@(((Jwt)context.Variables["jwt-token"]).Claims["role"].Contains("admin") ? "allow" : "reject")" />
</authorize>
</validate-graphql-request>
This policy is specific to GraphQL requests, and it performs a variety of query validations:
Resulting policy should look the following:
After we added policies to our API, we can test that the security rules work correctly:
We did not pass a valid JWT and got a 401 Unauthorized response back as expected.
To test the JWT validation and authorization I will create two tokens using jwt.io with roles ‘user’ and ‘admin’ with a signing key used in the previous steps.
Note: for the test purposes you also need to add an `exp` field with a future date in a Unix Timestamp format
To test the API with JWT, navigate to the Test tab and add `Authorization` header with a generated token.
Request without restricted fields and using JWT containing `user` claim.
Request with a `created` field and using JWT containing `user` claim.
Request with a `created` field and using JWT containing `admin` claim.
By following these simple steps and incorporating a few lines of policies, the security of GraphQL APIs can be significantly enhanced when using Azure API Management.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.