토큰을 해석하는 과정은 아래와 같다.

yarn add passport passport-jwt

passport 는 인증에 관련한 모든 일들을 한다. jwt 토큰이나 쿠키에서 정보를 가져와서 사용자 정보에 serialize(저장)한다. 토큰에서 정보를 가져와서 express 의 request 에 붙여준다. 달리 말하면 토큰을 가져와서 해독한 후에 사용자 객체를 request 에 추가해주는 것이다. 이 과정들을 자동으로 해준다.

src/passport.js

import dotenv from "dotenv";
import path from "path";
import { Strategy, ExtractJwt } from "passport-jwt";
import { prisma } from "../generated/prisma-client";

import passport from "passport";
import JwtStrategy from "passport-jwt";

const jwtOptions = {
  jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
  secretOrKey: process.env.JWT_SECRET
};

const verifyUser = async (payload, done) => {
  try {
    const user = await prisma.user({ id: payload.id });
    if (user !== null) {
      return done(null, user);
    } else {
      return done(null, false);
    }
  } catch (error) {
    return done(error, false);
  }
};

passport.use(new Strategy(jwtOptions, verifyUser));

사용자 정보가 암호화되어 토큰에 담긴다. JWT 는 토큰을 입력받아서 정보를 해석하고 해석된 정보를 콜백 함수로 전달해준다. 토큰에 id가 있고 사용자가 없다면, 새로 생성한다. 원하는 strategy 를 사용하고 인자 값으로 원하는 함수를 입력한다. strategy 가 모든 작업을 한 후에 결과물을 payload 에 전달해준다. 이제 토큰 생성 함수를 만들어야 한다.

yarn add jsonwebtoken

src/utils.js

...
...
export const generateToken = id => jwt.sign({ id }, process.env.JWT_SECRET);

src/server.js

...
...
server.express.use(passport.authenticate("jwt"));
...
...

경로를 미들웨어로 보호하고 싶을 때 server.express.use 로 미들웨어를 사용한다.

위의 코드는 모든 경로를 passport.authenticate("jwt") 로 보호한다.

src/passport.js

export const authenticateJwt = (req, res, next) =>
  passport.authenticate("jwt", { sessions: false }, (error, user) => {
    if (user) {
      req.user = user;
    }
    next();
  })(req, res, next);

passport.initialize();

passport.js 에 위 함수를 추가한다. 이 함수는 미들웨어 함수이기 때문에 req, res, next 를 인자로 받는다. passport 가 함수에 사용자 정보를 전달해 준다. 그 과정을 보자. verifyUser 함수로 부터 사용자 정보가 전달되고 그 사용자 정보로 작업을 할 수 있다. 사용자 정보를 받아온 후에, 사용자가 존재한다면 그 사용자 정보를 req 객체에 붙여준다. express 에서는 미들웨어를 지나서 라우트가 실행된다. 토큰을 받아서, 해석하고, 사용자를 찾고, 사용자가 존재한다면 req 객체에 사용자를 추가하고 나면 graphql 함수를 실행하는 것이다. 만약 로그인이 되어 있다면 모든 graphql 요청에 사용자 정보가 추가되어서 요청된다.

문제가 하나 있다. resolver 들이 위의 사실을 모른다. 그래서 resolver 들에게 전달해 줘야한다. 여기서 context 를 사용한다. context 는 resolver 사이에서 정보를 공유할 때 사용한다. prisma 를 resolver 에서 import 하지 않고 server.js 에서 import 하면 된다.

src/server.js

const server = new GraphQLServer({ schema });    // 수정 전
server.express.use(passport.authenticate("jwt"));

const server = new GraphQLServer({                // 수정 후
  schema,
  context: ({ request }) => ({ request })
});
server.express.use(authenticateJwt);

이제 requestSecret 의 세 번째 인자 값으로 { request } 을 넣어주면 된다.

playground 아래쪽 HTTP HEADERS 항목에서 Authorization 헤더를 추가하자.

{"Authorization": "Bearer TOOOOOOOOOOOKEEEEEEEEEEEN"}

+ 따끈한 최근 게시물