4-3. JWT 처리 미들웨어 만들기
쿠키가 한번 설정이 되고 나면, 요청이 들어올때마다 쿠키에 access_token
이 함께 전달됩니다. 이번 섹션에서는 해당 쿠키가 존재한다면, 토큰을 해석해서 요청의 user 값으로 설정하는 미들웨어를 만들어보도록 하겠습니다.
먼저, JWT 디코딩 하는 방법을 알아봅시다.
JWT 디코딩은 다음과 같이 합니다:
jwt.verify(token, jwtSecret, (error, decoded) => {
if(error) {
console.error(error);
return;
}
console.log(decoded);
});
이제 이 함수를 사용하여 프로미스 기반으로 토큰을 해석하는 함수를 새로 생성해보겠습니다. 다음 함수를 src/lib/token.js 파일에 만드세요.
src/lib/token.js
(...)
function decodeToken(token) {
return new Promise(
(resolve, reject) => {
jwt.verify(token, jwtSecret, (error, decoded) => {
if(error) reject(error);
resolve(decoded);
});
}
);
}
디코딩 하는 함수도 만들었으니, 미들웨어를 만들어보도록 하겠습니다. 주석과 함께 코드를 읽어보세요.
src/lib/token.js
exports.jwtMiddleware = async (ctx, next) => {
const token = ctx.cookies.get('access_token'); // ctx 에서 access_token 을 읽어옵니다
if(!token) return next(); // 토큰이 없으면 바로 다음 작업을 진행합니다.
try {
const decoded = await decodeToken(token); // 토큰을 디코딩 합니다
// 토큰 만료일이 하루밖에 안남으면 토큰을 재발급합니다
if(Date.now() / 1000 - decoded.iat > 60 * 60 * 24) {
// 하루가 지나면 갱신해준다.
const { _id, profile } = decoded;
const freshToken = await generateToken({ _id, profile }, 'account');
ctx.cookies.set('access_token', freshToken, {
maxAge: 1000 * 60 * 60 * 24 * 7, // 7days
httpOnly: true
});
}
// ctx.request.user 에 디코딩된 값을 넣어줍니다
ctx.request.user = decoded;
} catch (e) {
// token validate 실패
ctx.request.user = null;
}
return next();
};
이 미들웨어를 적용하고 나면, 라우트 핸들러에서 ctx.request.user 를 조회하면 유저정보가 반환됩니다.
이제, 이 미들웨어를 적용해줍시다. 방금 만든 미들웨어를 불러온다음에, api 라우트가 적용되기 전에 미들웨어를 적용해줍니다.
src/index.js
const { jwtMiddleware } = require('lib/token');
(...)
app.use(bodyParser()); // 바디파서 적용, 라우터 적용코드보다 상단에 있어야합니다.
app.use(jwtMiddleware);
router.use('/api', api.routes()); // api 라우트를 /api 경로 하위 라우트로 설정
app.use(router.routes()).use(router.allowedMethods());
app.listen(port, () => {
console.log('heurm server is listening to port ' + port);
});
check API 만들기
이제, 만약에 쿠키에 access_token 이 있다면, 현재 로그인된 유저의 정보를 알려주는 API 를 만들어보겠습니다.
src/api/auth/auth.controller.js
- check
exports.check = (ctx) => {
const { user } = ctx.request;
if(!user) {
ctx.status = 403; // Forbidden
return;
}
ctx.body = user.profile;
};
이렇게, ctx.request.user 에 접근하면 토큰에 설정했던 객체값을 얻을 수 있습니다. 이제 라우트를 설정을 하겠습니다.
src/api/auth/index.js
const Router = require('koa-router');
const auth = new Router();
const authCtrl = require('./auth.controller');
auth.post('/register/local', authCtrl.localRegister);
auth.post('/login/local', authCtrl.localLogin);
auth.get('/exists/:key(email|username)/:value', authCtrl.exists);
auth.post('/logout', authCtrl.logout);
auth.get('/check', authCtrl.check);
module.exports = auth;
여기까지 구현을 하고 나서, 로그인 API 를 통해 토큰을 쿠키에 설정받은 다음에, http://localhost:4000/api/auth/check
에 GET 요청을 하면 해당 토큰이 가지고 있는 회원정보를 반환 하게 됩니다.
GET http://localhost:4000/api/auth/check
{
"thumbnail": "/static/images/default_thumbnail.png",
"username": "velopert"
}
이제 회원인증을 위한 API 준비를 어느정도 마쳤습니다. 다음 5장 부터 프론트엔드 작업을 시작해보도록 하겠습니다.