DEV Community

loading...

GraphQL HTTP-only JWT Authentication with Next.js

cvr profile image Cristian Velasquez Ramos Originally published at cvr.im ・2 min read

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 😄

Discussion

pic
Editor guide
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?