A few months ago when I had first started learning about GraphQL, I had written a previous tutorial for using it with Couchbase and Node.js. The tutorial focused on the basics which included creating GraphQL objects and querying those objects from the NoSQL database, Couchbase. Fast forward a bit and I wrote a tutorial that offered an alternative way to use GraphQL with Node.js, even though the database layer wasn’t the emphasis.

When using or creating an API, there are often scenarios where you don’t want all data to be accessible by everyone. In these scenarios you’d want some kind of regulation through authorization and API tokens. Like with a RESTful API, this can easily be accomplished through JSON web tokens (JWT).

We’re going to see how to use JWT in a GraphQL application to protect certain pieces of data rather than all or none.

Going forward, it is probably a good idea to have at least a little bit of an understanding of how JSON web tokens (JWT) work. If you’d like to check out a quick getting started guide, check out my tutorial titled, JWT Authentication in a Node.js Powered API. The focus of that tutorial is not GraphQL, but it still works as a good starting point.

The goal for this tutorial is to create an API that uses GraphQL. We’ll be able to create accounts, obtain tokens, and use those tokens to access protected parts of our API. Instructions for installing and configuring Couchbase will not be a part of this tutorial, but nothing special needs to be done for compatibility.

Creating a New Node.js Application with the Project Dependencies

Before we jump into the heavier and more complex parts of our project, let’s create a fresh project with all the dependencies and some boilerplate code. Assuming that you’ve already got Node.js installed and configured, execute the following from your CLI:

The above commands will create a new package.json file and install each of our necessary pacakges. These packages include a GraphQL library as well as a GraphQL extension for Express Framework. We’re also including a way to hash our password data, create JWT, and store everything in Couchbase. Could I have done this with a single line? Yes, but I thought it would be easier to read if I broke it up.

With the project created, go ahead and create an app.js file that includes the following code:

We’re going to call the above code our boilerplate code. Essentially we’re just doing some setup and nothing truly relevant to our end goal. We’re importing our downloaded packages, establishing a connection to Couchbase, configuring Express Framework, defining three endpoints, and serving on port 3000. Make sure to use your own Couchbase information rather than the information I used. Also, for better security, change the jwt-secret so your tokens are hashed in a less predictable fashion than mine.

Designing Simple Account API endpoints for Login and Registration

You’ll notice from our boilerplate code that we have an endpoint for both registration and sign-in. There are many ways to accomplish what we’re trying to do, probably some better than my solution. However, I found what we’re about to see logical and easy to implement without getting too far away from our goals.

Starting with the /register endpoint, we have the following:

When a user sends a POST request with username and password in the body, we are first creating a new UUID to be used as our Couchbase document key, then we are hashing the password using Bcrypt, like previously demonstrated in a tutorial I wrote.

Once a hash is created, we store the profile data in Couchbase and return it. The whole goal behind the /register endpoint is to give us some data to authenticate with. To authenticate, we use the /login endpoint as seen below:

In the above code we are creating a N1QL query to find a document that contains a username that matches the username that was passed with the request. We’re using a parameterized query to prevent any kind of SQL injection attack.

If a document was found, we then compare the stored hashed password with the password that was passed in with the request. If it matches, we can sign the user id using our secret, giving us a JWT to be used in future requests.

A few things to note with our two endpoints:

  1. We’re not doing any data validation. This is an example and we’re trying to keep things simple.
  2. We’re not setting an expiration on our JWT. Typically you’d want it to expire within an hour, but this is just a simple example.

As of now we’ve done nothing with GraphQL. We’ve just laid down the foundation to creating users and getting JSON web tokens. Even though we’re creating JWT, we are not validating that they are accurate as of now.

Validating JSON Web Tokens (JWT) with an Express Framework Function

When it comes to working with protected data, having a JWT is not enough. We want to make sure the token is in fact valid by checking the signature. We also need a solution for passing JWT around.

Express Framework does have middleware for working with JSON web tokens, but I found the documentation to be lacking. Instead, it seemed easier to just create my own method for validation. Take the following:

So what are we doing in the above code?

Since we’re using app.use, on every request this function will be called. When this function is called, we look at the current request headers and look for an authorization header. If an authorization header exists, we make sure it is a bearer token and use the actual token value in our verification. The actual token is our JWT. If the token is valid, we can add the decoded value to the request which then becomes accessible in the next stage of our request. If no token is present, no worries because nothing will happen. Our logic only checks our tokens if they exist because not all data points will be protected.

Are there better ways to obtain our JWT token in a request and validate it? Probably, but the above code worked when I tested it out and it wasn’t too complicated.

Developing a partially protected API with GraphQL and JavaScript

Now that we have the JWT logic in place, we can focus on the development of our API. Technically the /login and /register endpoints are part of our API, but the focus is GraphQL.

Before we worry about the queries, let’s focus on our GraphQL objects:

Remember, if you saw my previous Couchbase with GraphQL tutorial, the objects above probably look a little different. This is because I modeled them after my alternative GraphQL tutorial. Essentially, we have two objects where one is for account data and the other is for course data. Maybe not the best example, but we’ll make it work.

The course data will reference an account for the course author. We’ll be creating a resolver method for author information, but not yet. Let’s first create a query for account data separately:

It only makes sense that when we have a query like account, we want to get data for a particular account and that should probably be our own account. If we’re querying our own account, we should probably have a valid JWT.

Notice how we’re using context.decodeToken in the above code. The context allows us to get data from the request and in our case the decoded token data might exist in the request. If it doesn’t exist, we should probably throw an error because a valid token is a requirement for this query.

If we have a valid token, we can create a N1QL query and use the user id to query for our account and return it.

Not bad right?

Let’s expand on our queries. Within the fields object, we’re going to add another query, but this time we’ll be querying for course data and course data won’t necessarily be protected.

In the above code we are doing a simple N1QL query for all courses stored in the database. The catch here is that for author data, we’re only obtaining the key, not the fully loaded account data which is protected.

We’re actually going to do some data manipulations in the GraphQL object for CourseType instead:

Notice that we now have a resolve function on the author property. In this function we check for a valid token and if we don’t find one, we return an error. This check and error only happens if the author data is requested. If the query does not request author data, we can get the other information without a token since it is not protected.

There is another catch here. Not only are we checking to make sure a valid JWT is present, but we also want the JWT to match the data returned in the N1QL query. This means if we have several authors, only the authors we have permission for will succeed. Again, not the best example, but it proves our point.

To clean up any loose ends, we need to update our /graphql endpoint:

All we’ve done is add our schema which includes our two possible queries.

Conclusion

While our example was simple, it allowed us to demonstrated protected queries and pieces of data with GraphQL, Couchbase, and JSON web tokens (JWT). Some core things to remember:

  1. Request data can be accessed in the GraphQL context variable.
  2. JSON web tokens and accounts should probably be created through separate endpoints rather than with GraphQL mutations.
  3. You can restrict queries as well as data properties with JWT. It is not an all or nothing series of events.

If you want to learn more about GraphQL, I suggest you check out my previous tutorial on the subject. To learn more about Couchbase with Node.js, check out the Couchbase Developer Portal.

Posted by Nic Raboy, Developer Advocate, Couchbase

Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in Java, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Apache Cordova. Nic writes about his development experiences related to making web and mobile development easier to understand.

Leave a reply