이번에는 Hapi Api 서버의 인증 기능을 구현해 보자. hapi-auth-cookie 모듈을 이용하여 세션 쿠키를 이용하여 인증을 구현할 것이다. 인증 정보를 세션에 저장하고 클라이언트와는 쿠키를 통해 인증상태를 통신하도록 구현한다.
세션 인증 활성화
Hapi에는 서버객체의 register() 함수를 통해 플러그인을 등록할 수 있다. Hapi에서의 플러그인은 익스프레스의 미들웨어와 비슷한 개념이다. 세션 인증 모듈 hapi-auth-cookie를 등록하기위해 server.register()
함수를 사용한다.
// Hapi 서버 객체를 이용해 인증 설정을 한다.
module.exports = function (server) {
// 인증 모듈을 등록한다.
server.register(require("hapi-auth-cookie"), function (err) {
if (err) {
throw err
}
// 인증 strategy 를 생성한다.
server.auth.strategy("mySessionStrategy", "cookie", {
password: "secret",
cookie: "sid-example",
redirectTo: false,
isSecure: false,
})
})
}
인증에는 scheme
와 strategy
라는 두 가지 개념이 등장한다. scheme은 hapi-auth-cookie 모듈을 통해 생성되는데 서버의 인증 방법을 설정하는 것이다. hapi에서는 scheme별로 추가적인 모듈을 제공한다.
이외에도 서드파티에서 제공하는 shceme 모듈이 있다.
strategy는 서버객체의 server.auth.strategy()
함수로 생성하는데 세부적인 인증 정책에 대한 정의라고 할 수 있다. 위 샘플코드의 경우 세션쿠키에 대해 비밀번호 설정, 쿠키명 설정, 리타이렉트 정책 설정, secure 설정을 정의하고 있다.
세션 플러그인을 설정한 Hapi 서버 객체는 request 객체를 통해 세션을 시작하고 종료할 수 있다.
- request.auth.session.set(): 세션 시작
- request.auth.session.clear(): 세션 종료
이 두 함수를 이용해 세션 시작/종료 함수를 구현한 것이 아래 코드다.
exports.startup = function (auth, data) {
// 사전 인증정보가 없을 경우 세션에 인증정보 저장
if (!auth.isAuthenticated) {
auth.session.set(data)
}
}
exports.teardown = function (auth) {
// 세션정보 삭제
auth.session.clear()
}
이상 Hapi 서버에 세션 쿠키 인증을 위한 환경설정을 마쳤다. 이제는 실제 인증을 위한 프로토콜일 login, logout을 구현해 보자.
라우팅 설정
프로토콜은 최대한 RESTful 하게 작성하기 위해 아래와 같이 구현한다.
- /auth (post): 로그인, 인증 파라메터는 payload로 받는다. 인증성공시 201, 실패시 403코드를 반환한다.
- /auth (delete): 로그아웃
- /auth (get): 테스트용 프로토콜. 세션 정보를 확인한다.
우선 위 프로토콜에 대한 라우팅 설정하는 부분이다. route() 함수 설정 객체의 속성중 config.auth 키를 통해 인증 정책을 설정한다. mode
는 'try', 'required' 두 가지 타입을 설정할 수 있다. 로그인의 경우 'try'를 설정하고 인증된 상태에서 접근할수 있는 프로토콜은 'required'를 설정한다. strategy
는 server.auth.strategy() 함수로 생성한 strategy를 문자열로 설정한다.
module.exports = function (server) {
// 로그인
server.route({
method: "POST",
path: "/auth",
handler: ctrl.login,
config: {
auth: {
mode: "try", // 인증 시도
strategy: "mySessionStrategy", // 사용할 strategy
},
},
})
// 로그아웃
server.route({
method: "DELETE",
path: "/auth",
handler: ctrl.logout,
config: {
auth: {
mode: "required", // 인증 필수
strategy: "mySessionStrategy",
},
},
})
// 세션 확인 (개발용)
server.route({
method: "GET",
path: "/auth",
handler: ctrl.find,
config: {
auth: {
mode: "required",
strategy: "mySessionStrategy",
},
},
})
}
컨트롤러 로직
로그인 시나리오는 이렇다. /auth (post) 프로토콜을 통해 인증 시도 → 인증 정보 검증 → 검증에 통과하면 세션에 유저정보 저장 → 클라이언트에 인증 성공 정보를 응답. 아래 코드와 주석을 같이 살펴보자.
'use strict';
// 위에서 구현한 세션 모듈. 이것을 통해서 세션 시작/종료 할 수 있다.
var session = require('../../components/session');
exports.login = function (req, reply) {
var user = {
username: 'Chris',
password: 'chrisPass'
};
// 인증 정보 검증
if (user.username !== req.payload.username || user.password !== req.payload.password) {
return reply(403);
}
// 세션 시작, 유저정보 저장
session.startup(req.auth, user);
// 인증 성공 정보를 응답
reply(user).code(201);
};</pre>
로그아웃은 세션을 삭제하고 응답한다. find는 세션 확인용이다.
<pre class="lang:js decode:true " title="routes/auth/auth.ctrl.js">exports.logout = function (req, reply) {
// 세선 종료
session.teardown(req.auth);
// 응답
reply();
};
// 세션 확인용
exports.find = function (req, reply) {
reply(req.auth);
};
이상으로 로그인/로그아웃 프로토콜을 구현했다. 실제 로그인, 로그아웃 프로토콜 결과를 첨부한다.
로그인:
세션확인:
로그아웃:
전체 코드: https://github.com/jeonghwan-kim/hapi_study/tree/08_auth구현