A Match Made In Heaven: Authentication and Authorization with Dgraph and Auth0

When creating a serverless app that integrates both authentication and authorization, using both Dgraph and Auth0 might be the holy grail. Auth0 allows you to incorporate an authentication service with Dgraph’s authorization layer quickly and easily. Let’s dive into how to integrate both of these services.

Note: This post is a continuation of No Tresspassing: Introduction to GraphQL Authorization. If you are new to Dgraph authorization, the previous article will help you get started.

Setting Up Auth0 Account

The first step is setting up an Auth0 account. Once you log in, we’ll need to create an application. Create a single page application, as seen below.

Create Application

Grabbing the private key

Go into the Settings page of the application we created, and then scroll down until you see the ‘Show Advanced Settings` link. Click on it and navigate to the Certificates tab. Download the private key in the PEM format.

Download Private Key

Generating a public key

Now that we’ve downloaded our private key, let’s generate a public key. Use the following command:

openssl x509 -pubkey -noout -in FILE_NAME.pem

This command will output a public key to the console. We need to replace all the newlines in our public key with \n. The end result is something like the key below:

-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6UwwVB5VNqA9t6ehAEg4\nlYK7Pw3PnRmDqkmzQHczOmQhd7hszmNsfgazPAl2VD3KrIjigWj9kD2iWOHkoB7d\nlg22dRU693hKHAMMUEOkHiBQZ+M3T75NQsK5teW9NHcrC7aB1PUQoSEX/TqYGCJE\npDXEfvTfDrxFkl0vQxc6ITNzt1xxTKHihohOQGPx+aVTRDUehi4XYsAxMOdta3rj\nEVjtS04dPLJ4mXibIIt5qcdX7Ent7Do4q66ZTBNtK0b2RIleI86FdcGmdJ3Mryne\nPZDcvsUSkMEYrNA5F9bi2/HfdLpVML8PjWtLN4JjSa6afzTOEzQtCA47MgC7K7Rp\n3wIDAQAB\n-----END PUBLIC KEY-----

Injecting Claims Into JWT

Now we’ll need to inject the username into our JWT. Let’s create an Auth0 rule, as seen below:

function (user, context, callback) {
  const namespace = "https://dgraph.io/jwt/claims";
  context.idToken[namespace] =
    {
      'USER': user.email,
    };
  
  return callback(null, user, context);
}

What we’re doing is adding some claims into a namespace on any Id token issued by Auth0. The USER field will contain the user’s email.

Updating Schema

Time to add everything to our schema.

# Dgraph.Authorization {
    "VerificationKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6UwwVB5VNqA9t6ehAEg4\nlYK7Pw3PnRmDqkmzQHczOmQhd7hszmNsfgazPAl2VD3KrIjigWj9kD2iWOHkoB7d\nlg22dRU693hKHAMMUEOkHiBQZ+M3T75NQsK5teW9NHcrC7aB1PUQoSEX/TqYGCJE\npDXEfvTfDrxFkl0vQxc6ITNzt1xxTKHihohOQGPx+aVTRDUehi4XYsAxMOdta3rj\nEVjtS04dPLJ4mXibIIt5qcdX7Ent7Do4q66ZTBNtK0b2RIleI86FdcGmdJ3Mryne\nPZDcvsUSkMEYrNA5F9bi2/HfdLpVML8PjWtLN4JjSa6afzTOEzQtCA47MgC7K7Rp\n3wIDAQAB\n-----END PUBLIC KEY-----",
    "Header": "Authorization",
    "Namespace": "https://dgraph.io/jwt/claims",
    "Algo": "RS256",
    "Audience":["twbsDkG7kWPm70fBZ3ytXC02jLnAzYFS"]
}

VerificationKey is our public key. Header is the header that we will pass our JWT in. Namespace is the namespace we are using in our Auth0 rule. Algo is the algorithm our key is using, RS256 with Auth0 keys. Finally, Audience a list of audiences, and your audience is your Auth0 app’s clientId.

Finalized Schema

type Todo @auth(
  query: { rule: """
    query($USER: String!) {
      queryTodo {
        user(filter: { username: { eq: $USER } }) {
          __typename
        }
      }
    }"""}), {
  id: ID!
  value: String! @search(by: [fulltext])
  completed: Boolean! @search
  user: User!
}

type User {
  username: String! @id @search(by: [hash])
  name: String @search(by: [exact])
  todos: [Todo] @hasInverse(field: user)
}
# Dgraph.Authorization {
    "VerificationKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6UwwVB5VNqA9t6ehAEg4\nlYK7Pw3PnRmDqkmzQHczOmQhd7hszmNsfgazPAl2VD3KrIjigWj9kD2iWOHkoB7d\nlg22dRU693hKHAMMUEOkHiBQZ+M3T75NQsK5teW9NHcrC7aB1PUQoSEX/TqYGCJE\npDXEfvTfDrxFkl0vQxc6ITNzt1xxTKHihohOQGPx+aVTRDUehi4XYsAxMOdta3rj\nEVjtS04dPLJ4mXibIIt5qcdX7Ent7Do4q66ZTBNtK0b2RIleI86FdcGmdJ3Mryne\nPZDcvsUSkMEYrNA5F9bi2/HfdLpVML8PjWtLN4JjSa6afzTOEzQtCA47MgC7K7Rp\n3wIDAQAB\n-----END PUBLIC KEY-----",
    "Header": "Authorization",
    "Namespace": "https://dgraph.io/jwt/claims",
    "Algo": "RS256",
    "Audience":["twbsDkG7kWPm70fBZ3ytXC02jLnAzYFS"]
}

Grabbing a Token

Finally, we’ve updated our schema, but how do we get a token to access our data?

We won’t cover this part in-depth, but here’s a high-level overview. You’ll need a frontend that logs a user in via Auth0, using the code below:

import { useAuth0 } from "@auth0/auth0-react"
...
const { loginWithRedirect, ... } = useAuth0();
loginWithRedirect();

Then, we’ll get the access token using the code below:

const { getIdTokenClaims, ... } = useAuth0();
const token = (await getIdTokenClaims()).__raw;

Then you can include this token as the header you defined in your schema above. It’s that simple!

For a more thorough example, take a look at this repository. It has full Auth0 integration.

Further Reading

For more information about integrating with Auth0, take a look at this tutorial.