Next.js를 사용한 GraphQL HTTP 전용 JWT 인증

최근에 devchallenges.io 에서 앱 챌린지 중 하나를 구축하는 작업을 하고 있습니다.

내 스택으로 GraphQL과 함께 Next.js를 사용하기로 결정했습니다. 안전한 HTTP 전용 인증을 구현하는 방법에 대해 조금 걱정했지만 매우 간단하다는 것이 밝혀졌습니다! 방법을 보여드리겠습니다.

먼저 next.jsexample에서 적용된 기본 graphql API 경로를 사용합니다.

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' })


여기에서 재미가 시작됩니다.
jsonwebtokencookies를 가져올 것입니다(이를 종속성에 추가해야 합니다!):

import jwt from "jsonwebtoken";
import Cookies from "cookies";


그런 다음 apollo 서버 내에 컨텍스트를 추가하여 해결 내에서 쿠키를 설정 및 가져오고 JWT 토큰(있는 경우)을 구문 분석할 쿠키 항아리를 생성합니다.

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,
    };
  },
});


이제 리졸버에서 사용자가 로그인할 때 쿠키를 설정할 수 있습니다.


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;
    },
  }
}


이제 인증 상태 확인 요청이 있을 때마다 간단합니다!

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


시작하기에 충분합니다 😄

좋은 웹페이지 즐겨찾기