GraphQL server를 구축할 때 query
와 mutation
JWT를 이용한 인증에 대해 이야기 해 볼 예정입니다.😄
query
와 mutation
를 이용한 API에 대해 Bearer 인증 방식을 적용 해 볼 예정입니다.
"Authorization": "Bearer <토큰>"
Bearer 인증 방식을 동해 HTTP 헤더로 인증 토큰을 넘기면 passport에서 설정해준 전략을 통해 request 객체 내 user
객체의 존재 여부를 판별합니다.
이후 user
객체가 올바르지 않다면 에러를 던져 실패로 처리하고 성공한다면 문제 없이 해당 API로 요청이 정상적으로 잘 가도록 구현 할 예정입니다.
import { PrismaClient } from '@prisma/client';
import { Strategy as JwtStrategy, ExtractJwt } from 'passport-jwt';
import passport from 'passport';
const prisma = new PrismaClient();
const jwtOptions = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.JWT_SECRET_KEY,
};
const verifyJwt = async (payload, done) => {
try {
const isExist = await prisma.user.findOne({
where: {
id: payload.id,
},
});
const user = {
id: payload.id,
nickname: payload.nickname,
avatar: payload.avatar,
};
if (isExist) {
return done(null, user);
} else {
return done(null, false);
}
} catch (err) {
return done(err, false);
}
};
export const authenticateJwt = (req, res, next): void => {
passport.authenticate('jwt', { session: false }, (error, user) => {
if (user) {
req.user = user;
}
next();
})(req, res, next);
};
passport.use(new JwtStrategy(jwtOptions, verifyJwt));
JWT
내 payload의 user id를 참조해 prisma client를 통해 유저를 찾아 유저가 존재 할 경우 req 객체 내 해당 user의 객체를 생성합니다.
import ERROR_MSG from '@utils/errorMessage';
export default (req): void => {
if (!req.user) {
throw Error(ERROR_MSG.unauthorized);
}
return;
};
passport 설정을 통해 JWT을 판별해 user가 존재하지 않을 경우 error을 반환하는 미들웨어를 추가합니다.
import { PrismaClient } from '@prisma/client';
import './env';
import { GraphQLServer, PubSub } from 'graphql-yoga';
import { authenticateJwt } from './middlewares/passport';
import isAuthenticated from './middlewares/isAuthenticated';
import schema from './schema';
import logger from 'morgan';
const PORT = process.env.PORT || 4000;
const prisma = new PrismaClient();
const pubsub = new PubSub();
const server = new GraphQLServer({
schema,
context: ({ request }) => ({ request, isAuthenticated, pubsub }),
});
server.express.use(logger('dev'));
server.express.use(authenticateJwt); // server가 요청을 수신할 때마다 JWT 인증 방식을 적용합니다.
server.start(
{
port: PORT,
cors: { origin: true },
endpoint: '/graphql',
subscriptions: {
path: '/graphql',
},
},
() => {
console.log(`Server running on http://localhost:${PORT}`);
},
);
아래와 같이 GraphQL 서버에 context 옵션에 request와 인증과 관련된 middleware를 넘겨줍니다.
const server = new GraphQLServer({
schema,
context: ({ request }) => ({ request, isAuthenticated, pubsub }),
});
아래는 모든 유저의 정보를 조회하는 예시 resolver입니다.
import { PrismaClient} from '@prisma/client';
const prisma = new PrismaClient();
export default {
Query: {
allUsers: (_, __, { request, isAuthenticated }) => {
isAuthenticated(request);
return prisma.user.findMany({
include: {
messages: true,
},
});
},
},
};
3번째 인자는 context로 request, isAuthenticated와 같이 server.js에서 context를 통해 넘겨 준 함수들을 포함하고 있습니다.
request와 인증과 관련된 미들웨어를 사용함으로 써 해당 resolver에 인증 방식을 적용 할 수 있습니다.
isAuthenticated(request);
다른 query와 mutation을 이용한 API 요청에 대해서도 간단하게 적용 할 수 있겠죠?😄
GraphQL
의 경우 endpoint가 하나라 기존의 REST 방식
과 달라 어떻게 각각의 API에서 인증을 처리할 지 처음에는 혼란스러웠습니다.🤔
하지만 공부를 하면서 찾아보니 GraphQLServer의 context를 통해 request
객체와 필요한 함수들을 넘겨줘서 이를 이용 해 해당 API의 인증 처리를 간단하게 적용 할 수 있는 것을 확인하였고 해당 내용을 적용해 본 후 정리한 내용을 적어 보았습니다.
혹시 이해가 안 가시거나 잘못된 내용이 있다면 이야기해주시면 감사하겠습니다.😊