Putting it All Together - Dgraph Authentication, Authorization, and Granular Access Control (PART 4)

Update: On April 16th 2021, Slash GraphQL was officially renamed Dgraph Cloud. All other information below still applies.

The last in a 4-part series by community author Anthony Master.

This is the last of a four part series:

  1. Building an Access Control Based Schema
  2. Authorizing Users with JWTs and Rules
  3. Authenticating Against a Dgraph Database and Generating JWTs
  4. Bringing Authentication into the GraphQL Endpoint as a Custom Mutation

Continuing in our series as the fourth and final part we will discuss how to bring authentication into our Dgraph GraphQL endpoint with a custom mutation. So far we should have the following:

  1. A schema set up with @auth directives.
  2. A user to authenticate with.
  3. A url link to your functioning authentication script.

PART 4 - Bringing Authentication into the GraphQL Endpoint as a Custom Mutation

What we need to add to get the final product:

  • A remote type in the schema to receive the response from the authentication script.
  • A custom mutation to point to the authentication script. Note: This could be done with a custom Query or Mutation, but I like to think of the authentication process of validating a user and creating a JWT as a mutation, because it is creating something that is not there, the JWT.

So back in your schema, let’s begin this with creating the type “AuthResponse” that will transport the responses from our script back to the user. Remembering back to our script we wrote in Part 3, we are returned an object with four properties: token, username, expires, and possibly admin. We can take these properties and create the type. By appending the @remote directive onto the type, Dgraph will not generate any queries or mutations for this type. This is what we are wanting since the data from this type is not actually being stored in the Dgraph database.

type AuthResponse @remote {
  token: String
  username: String
  expires: DateTime
  admin: String
}

This type is all good and dandy, but right now it does nothing. Literally nothing at all besides take up space in our schema. To use this type we will need to define our Mutation. Now this is a little tricky because normally Dgraph handles all the work (yes literally, all the work!) in building Queries and Mutations.

Normally by now anyone who has experience with GraphQL and may be new to Dgraph would be used to spending days/weeks/months writing resolvers that point to Queries and Mutations. This will bring back some of those memories, but this time we will do it with only 9 lines of code. We are going to write a Mutation in the schema that will auto generate a resolver based on the parameters that we give it in the schema.

Now to someone who may be newer to GraphQL, let me direct your attention to the fact that we are going to create a Mutation type in our Schema. This single Mutation type will hold all of the custom Mutations that we write. There can only be a single Mutation type and a single Query type.

We will create the Authenticate Mutation which requires a username and a pass, and then use the @custom directive to tell Dgraph how to resolve this Mutation. We will provide the url to our function we created before, pass the method as POST, pass the JSON body with the username and pass.

type Mutation {
  Authenticate(username: String!, pass: String!): AuthResponse @custom(http: {
    url: "https://yourdomain.com/functions/authenticate" # wherever you deployed your function to.
    method: "POST"
    body: "{ username: $username, pass: $pass }"
    secretHeaders: ["Content-type"]
  })
}

If you noticed in that Mutation @custom directive, we also passed a secretHeaders property. This is a lesser known property and late documented feature. But you can pass a header defined in the schema to this http request. We are passing the following “Content-type” header.

# Dgraph.Secret Content-type "application/json"

Save this schema and that concludes our lesson. Well I will give you the example of how to use it. To authenticate a user and generate a JWT, all you have to do now, is to use the following mutation:

mutation Login($username: String!, $pass: String!) {
  Authenticate(username: $username, pass: $pass){
    token
    username
    expires
    admin
  }
}

I hope that this series helps you understand how to Authenticate User, generate JWTs, and build a schema that can support group ACL.

Huge thanks from Dgraph to Anthony for coming forward to write some great blogs. If you are using Slash GraphQL or Dgraph and would like to write a community post, reach out to Michael, Zhenni or Apoorv in our discuss community