DEV Community

Jiayi
Jiayi

Posted on

JWT Authentication with Apollo

One of the features for this app is to have an authentication system without using OAuth. This implementation would be slightly new to me as I've never used GraphQL API.

The authentication flow is the same as how one would use in a REST API.

  1. Server (backend) will create the routes or mutations as they call it in GraphQL to perform authentication logic.
  2. The client will call the mutations, passing the necessary parameters such as username and password to the server.
  3. The server would handle the logic via jsonwebtoken to sign or verify the user and of course encrypt the password to store in the database.
  4. After a successful signature, server will return a valid token and client will store it.

I have a few routes that I want to protect and they must be logged in as the app's user to be able to make those requests. This is how I made it work.

// pages/api/graphql/index.js
import { ApolloServer } from 'apollo-server-micro';
import { makeExecutableSchema } from 'graphql-tools';
import resolvers from './resolvers';
import typeDefs from './TypeDef';
import jwt from 'jsonwebtoken';

const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
});

let db;

const apolloServer = new ApolloServer({
  schema,
  context: async ({ req, res }) => {
    // AUTHORIZATION
    let loggedUser;

    const token = req.headers.cookie ? req.headers.cookie.split('token=')[1] : '';

    if (token) {
      const user = jwt.verify(token, process.env.JWT_SECRET_KEY);

      if (!user) throw new AuthenticationError('You must be logged in');

      loggedUser = user;
    }

    // DATABASE
    if (!db) {
      // connect to db
    }

    return { db, loggedUser };
  },
});

export const config = {
  api: {
    bodyParser: false,
  },
};

export default apolloServer.createHandler({ path: '/api/graphql' });

Enter fullscreen mode Exit fullscreen mode

In the apollo server entry point, I'm making use of the context object to achieve this. There I'm verifying the token via jsonwebtoken. If there is a user signed with that token, I'm setting the loggedUser as the user that's verified and returning it in the context object. Now I have access to loggedUser in the resolvers as well.

// pages/api/graphql/resolvers.js

const resolvers = {
Query: {
    posts: async (_parent, _args, { db, loggedUser }, _info) => {
      if (!loggedUser) throw new AuthenticationError('you must be logged in');

      return await db
        .collection('posts')
        .find()
        .toArray();
    },
  }
}
Enter fullscreen mode Exit fullscreen mode

I want to protect this posts query from not-logged-users so I'm simply putting a check before returning desired response. The client can now handle it however they want with that error :)

So far, this is working rather nicely πŸ˜‚. I'll continue posting on the development process in the next posts. Happy hacking!

Top comments (0)