Setting Up Dgraph With Auth0 To Secure Your GraphQL Endpoint | by Flo Ragossnig | Apr, 2022

Part 3: The bridal

This is the third article of this series which explains how to set up Dgraph with Auth0 for our demo project. If you haven’t set up Dgraph nor Auth0, you might want to read Part 1 and/or Part 2 of this series. If however, you are only interested in the proper securing of your GraphQL endpoint, you can safely skip this part.

During the last two parts of this series we have set up our cloud API (Dgraph) and our authentication service (Auth0) for our Quote demo application. Although we have an auth service, our GraphQL endpoint is still not protected since Dgraph does not know anything about Auth0 yet. Establishing a connection between these services is the goal of this article.

So far, we have created a Dgraph Account and set up the schema for our Quote application, which looks like this

// Quote
type Quote {
id: ID!
text: String!
author: Author!
}
// Author
type Author {
id: ID!
name: String!
}

We have also created an Auth0 account and set up a

  • Quote application
  • user admin@quoteapp.com

Since Dgraph needs to know that we want to secure our GraphQL endpoint via Auth0, we need to assign our application key (the key which is being used to sign all JWT tokens which we will get from Auth0) to our schema.

Construct the verification key from the Quote app’s singing certificate

Let’s log-in to our Auth0 dashboard and manoeuvre to Applications > Quote App and scroll all the way down to Advanced Settings. Open the dropdown, select the Certificates tab, click on Download Certificate and select PEM.

Download the Signing Certificate as PEM file.

You should have file with *.pem in your downloads folder now. The Privacy Enhanced Mail file includes the Public Key which we will need to extract in the next step. I’m working with MacOS (for Linux systems, the following is identical) — for Windows there might be different commands.

Open your console and navigate to the downloads folder which includes you PEM file. Enter

openssl x509 -pubkey -noout -in <your-file-name>.pem | awk '{printf "%s\n", $0}'

and copy the resulting string up to the n% at the end.

Notice the n ‘s in the string! They are important because Dgraph requests a single string in its JSON data bag.

Get the Public Key from the PEM file via the console.

Apply the Auth0 Public Key to the Dgraph Authorization Header

We are almost done!

In the next step we have to set up our Dgraph Authorisation Header which will be attached to every request we send to out GraphQL endpoint. The header must consist of the following

  • Verification Key → this is the Public Key we have just created
  • Header → this can be anything you want. For our application let’s set it to Quote-Auth
  • Namespace → For all Dgraph specific requests this can be anything so let’s set it to https://quote-app-claims (just make sure you have a valid URL here!)
  • Algo → the algorithm which the key has been signed with. Since we are using a public key, this is set to RS256.
  • Audience → this is actually an array of audiences. For now it’s our Quote App Client ID, later on, when we will have Machine to Machine callswe will add an additional audience here.

So how do we get this stuff into Dgraph now? Well, Dgraph makes it really simple for us. Dgraph accepts a simple string of the form

# Dgraph.Authorization {"VerificationKey":"<AUTH0-PUBLIC-KEY>","Header":"Quote-Auth","Namespace":"https://quote-app-claims","Algo":"RS256","Audience":["<AUTH0-QUOTE-APP-CLIENT-ID>"]}

We simply need to add this string to the bottom of our schema. Therefore, log into your Dgraph dashboard click on Schema and copy & paste the string.

Don’t forget to replace <AUTH0-PUBLIC-KEY> and <AUTH0-QUOTE-APP-CLIENT-ID> with your key/ID.

Add the authorisation string to the bottom of the Dgraph schema.

The Quote app Client ID can be found in your Auth0 dashboard under Applications > Quote App

You need the Quote App Client ID as your audience field in your Dgraph authorisation string.

Deploy the schema and we are done!

Testing the endpoint protection

You might ask yourself what kind of protection do we have now? Well, at the moment nothing… All our nodes are publicly queryable and no real protection is in place. In order to see the effect of our implementation, we have to query against our endpoint from an external source.

I personally use the Altair GraphQL Client for that reason (it’s free too 🙌) but you could also use Postman or whatever tool you like to make simulate GraphQL requests to your endpoint.

First, let’s query what is already stored in our database. If you were following Part 1 of this series, you should already have a quote and an author stored in your database. We first have to enter our GraphQL endpoint (available on the Overview tab of you Dgraph dashboard) and run the introspection. Altair will now fetch your schema from Dgraph with all the information it needs to send request to your endpoint. Then we can enter the quote query in the query field

query QueryQuotes {
queryQuote {
id
text
author {
name
}
}
}
Altair is a very easy to use tool for testing your GraphQL endpoint. Enter the URL to your endpoint and hit the reload button to run a schema introspection.

In the result window you should see the first quote we have added, including the author. Let’s add a second quote right from our GraphQL Client:

Without requirements or design, programming is the art of adding bugs to an empty text file. — Louis Srygley

The mutation then is:

mutation AddQuoteWithAuthor {
addQuote(
input: {
text: "Without requirements or design, programming is the art of adding bugs to an empty text file."
author: {
name: "Louis Srygley"
}
}
) {
numUids
}
}

So add this mutation to you query field in your GraphQL Client and run the mutation. If everything went well, you should see a numUids: 2 in the data section of the response object.

Running the mutation should result with numUids: 2 in the response object.

If you re-run the quote query we should now see two quotes:

We should now have two quotes in our database.

So far so good. So at the moment everybody can query quotes but also ADD quotes to our app! This is not really acceptable — we could end up with quotes we do not really wanna see in our application. We would like our app users to query quotes but only admins should have the right to actually add quotes.

Luckily Dgraph makes this really easy for us by using Dgraph’s built in @auth directive. Therefore, we head over to our Dgraph dashboard in the Schema tab and add the following lines to our Quote type

type Quote @auth(
add: { rule: "{$isAdmin: { eq: "true" } }"}
update: { rule: "{$isAdmin: { eq: "true" } }"}
delete: { rule: "{$isAdmin: { eq: "true" } }"}
)
{
id: ID!
text: String!
author: Author!
}

This now means that only users with a field isAdmin: “true” in their token claim will be allowed to add, update or delete quotes in our Quote App. After deploying the schema try to re-run the AddQuoteWithAuthor mutation — you should see addQuote: null in the data body of your result object.

That’s great! However, users could still clutter your database by adding authors. Since we don’t really want to add authors without a quote, we can simply deactivate all public access and remove the queries.

Again, head over to your Dgraph dashboard and add this to your Author type in the GraphQL Schema tab and add the @generate directive:

type Author 
@generate(
query: { query: false, get: false, aggregate: false }
mutation: { add: false, update: false, delete: true }
)
@auth (
delete: { rule: "{$isAdmin: { eq: "true" } }"}
)
{
id: ID!
name: String!
}

Since we left delete: true (we still want to be able to delete authors if eg. all quotes of an author are deleted), we add another @auth rule to the Author type too.

Remove unnecessary queries and mutations and add an @auth rule to protect the node.

move to Access > Edit Permissions and remove the read and write permissions from the Author type.

Gerat! If you were following Part 2 of this series, you have already created an admin user in your Auth0 dashboard. We now need to create the user claim which needs to include the field isAdmin: “true" for users which log into our app with via Auth0.

Therefore, head over to your Auth0 dashboard and got to User Management > Users and select your admin user you want to use with the Quote App (for me it’s admin@quoteapp.com). Scroll down to the Metadata section and enter

{
"isAdmin": "true"
}
Add the field which should appear in your claim to your user’s meta data.

Note that isAdmin is labeled as string. This is because Dgraph can only validate strings from fields in the token claim.

Next, go to Actions > Flows and choose a Login Flow. Click on the ➕ icon next to Add Action and select Build Custom.

Build a post login flow to add a custom claim to your JWT.

Give your action a catchy name — I’ve used Create User Claim and click on Create.

Choose a name for your Login Flow action.

We now need to tell Auth0 to add our user’s meta data as a custom claim to our JWT once the user has successfully logged in. There is a lot of things you can do but for now adding these lines will get the job done

// refactoring
const appMeta = event.user.app_metadata;
// set custom claim
api.idToken.setCustomClaim("https://quote-app-claims", {
"isAdmin": appMeta.isAdmin
});
Add the isAdmin field to the custom claim.

Deploy your action, click on Back to flow in the top left corner, find your custom action under the Custom tab and simply drag & drop the action in between Start and Complete. Click on Apply and you’re done! 👌

Hook your custom action into the login flow.

Let’s test out what we have created here. In Part 2 of this series I have created a CodeSandbox example to test out our Auth0 setup. We now want to extend this example by extracting our raw JWT token after we have logged in and check if our custom claim is properly set.

Here’s a link to my CodeSandox example — feel free to use this as a starter!

The Auth0 SDK provides a neat little feature which lets you extract your raw JWT — the getIdTokenClaims method. Once we have successfully logged in to our application, we can copy our JWT and check what’s inside at eg. jwt.io.

Copy the JWT from the console.
Paste the JWT into the “Encoded” field. You should immediately see our custom claim with our isAdmin field.

To see if our new token actually works, we now want to use it to make requests against our Dgraph GraphQL endpoint.

Thus, copy the JWT, open your GraphQL Client (for me it’s Altair), click on the header icon and add the Quote-Auth header with the JWT as value.

Set the “Quote-Auth” header in your GraphQL Client.

With this set, you can re-run the mutation (let’s add a 3rd quote) and see if it all works.

mutation AddQuoteWithAuthor {
addQuote(
input: {
text: "There are two ways to write error-free programs; only the third one works."
author: {
name: "Alan J. Perlis"
}
}
) {
numUids
}
}
Your endpoint is now protected against mutation from the public 👌

Happy days! Your endpoint is now protected against mutations from public users. Unauthorized users still have the right to query your quotes including the corresponding authors but won’t be able to add, update or delete quotes or authors.

Thanks for reading.

Leave a Comment