DEV Community

Cristian Velasquez Ramos
Cristian Velasquez Ramos

Posted on β€’ Originally published at cvr.im

GraphQL HTTP-only JWT Authentication with Next.js

Lately, I've been working on building one of the app challenges on devchallenges.io.

I decided to use Next.js with GraphQL as my stack. I was a little worried about how I would implement secure HTTP-only authentication but it turned out to be super simple! Let me show you how.

Starting off we'll use a basic graphql API route adapted from the next.js example

import { ApolloServer, gql } from 'apollo-server-micro'

const typeDefs = gql`
  type Query {
    me: User
  }

  type Mutation {
    signup(username: String!, password: String!): User
  }

  type User {
    username: String!
  }
`

const resolvers = {
  Query: {
    me(_parent, _args, context) {
      // what do we do here?
    },
  },

  Mutation: {
    signup(_parent, {username, password}, context) {
      // ??
    },
  }

}

const apolloServer = new ApolloServer({ typeDefs, resolvers })

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

export default apolloServer.createHandler({ path: '/api/graphql' })
Enter fullscreen mode Exit fullscreen mode

Here's where the fun begins.

We'll import jsonwebtoken and cookies (make sure you add them to your dependencies!):

import jwt from "jsonwebtoken";
import Cookies from "cookies";
Enter fullscreen mode Exit fullscreen mode

Then we'll add a context within the apollo server where we'll create a cookie jar to set and get cookies within our resolves and parse our JWT token (if we have it).

const verifyToken = (token) => {
  if (!token) return null;
  try {
    return jwt.verify(token, process.env.SECRET!);
  } catch {
    return null;
  }
};


const apolloServer = new ApolloServer({
  typeDefs, 
  resolvers,
  context: ({ req, res }) => {
    const cookies = new Cookies(req, res);
    const token = cookies.get("auth-token");
    const user = verifyToken(token);
    return {
      cookies,
      user,
    };
  },
});
Enter fullscreen mode Exit fullscreen mode

Now in our resolvers, we can set the cookie when a user signs up (and signs in, but I'll let you figure that out):


const resolvers = {
  // ...
  Mutation: {
    async signup(_parent, {username, password}, context) {
        let hash = await bcrypt.hash(password, 10);
        // bring your own db logic
        let user = await db.createUser({username, password: hash})


        let token = jwt.sign({ id: user.id }, process.env.SECRET!);
        context.cookies.set("auth-token", token, {
          httpOnly: true,
          sameSite: "lax",
          // here we put 6 hours, but you can put whatever you want (the shorter the safer, but also more annoying)
          maxAge: 6 * 60 * 60,
          secure: process.env.NODE_ENV === "production",
        });
        return user;
    },
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, whenever a request is made to check our auth status, it's easy!

const resolvers = {
  Query: {
    me(_parent, _args, context) {
      // bring your own db logic
      context.user?.id ? db.findUser(context.user.id) : null
    },
  },
}
Enter fullscreen mode Exit fullscreen mode

That should be enough to get you started πŸ˜„

Top comments (1)

Collapse
Β 
mucorolle profile image
Muco Rolle Tresor β€’

Thanks for the article I'm looking to start working on the dev challenges projects can please show us on how you handled this on the client with apollo client 3?