요약
express cookie를 사용해서 authentication 기능을 구현한다.
goal(목표)
- express cookie를 사용해서 authentication 토큰으로 jwt를 발급해서 auth_token={value}형태로 프런트에 정보를 내려준다.
- 프런트에서는 받은 쿠키를 사용해 서버에 접근한다.
Non-goal
로그인 방법에 대한 정책
jwt 에 대한 개념
plan (정책)
- domain을 설정하는 시점: 런타임 시점
도메인을 확인하고 설정하는 시점은 크게 두 가지이다.
(1) 빌드 시점에서 도메인 값을 설정하는 방법이다. env파일에 DOMAIN 값을 설정해서 넣어줄 수 있다.
(2) 런타임 시점에서 도메인 값을 설정하는 방법으로, req.hostname을 확인해서 넣어주는 방법이 있다.
(1)은 도메인이 고정되어 있는 경우에 사용하는 것이 좋은데, domain이 얼마든 지 변화할 수 있는 상황이기 때문에 (2)를 선택해서 구현하려고 한다.
- 쿠키의 domain을 root domain으로 설정한다.
쿠키는 domain의 cname까지 영향을 받아, root domain이 같다고 하더라도 cname이 다른 경우 다른 저장소로 인식해 쿠키를 공유하지 않는다. 예를 들어 abc.com이라는 root domain이 있을 때 first.abc.com과 second.abc.com은 쿠키를 공유하지 않는다. 이에 대해 쿠키를 만들 때 설정에 domain을 root domain으로 지정해주면 쿠키가 abc.com기준으로 공유되기 때문에 first.abc.com과 second.abc.com이 쿠키를 공유할 수 있게 된다.
- jwt를 생성한다
정보를 암호화하는 방법으로 jwt를 선택한다. jsonwebtoken 패키지를 이용한다. auth_token={jwt value} 형식으로 주고 받는다.
토큰 expire는 임의로 7일을 설정한다.
TechSpec
서버 내부 구현
간단하게 MVC 환경을 만들기 위해 nestJS로 express 환경을 구성했다.
http://localhost:3000/ 에 auth_token:{jwt value}를 쿠키에 설정해 내려주도록 했다.
import { Controller, Get, Res } from '@nestjs/common';
import { AppService } from './app.service';
import jwt from 'jsonwebtoken';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
createToken(data: object) {
return jwt.sign(
{
...data,
exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 60,
},
'_secret',
);
}
@Get()
getHello(@Res() res) {
const data = { name: 'joie' };
const token = this.createToken(data);
res.cookie('auth_token', token, { httpOnly: true, maxAge: null });
res.send(this.appService.getHello());
}
}
이 코드를 실행하면 다음과 같이 쿠키가 설정되어 내려오는 것을 확인할 수 있다.
이 쿠키의 Domain을 보면 localhost로 설정되어있다. 이는 auth_token 쿠키 저장소가 localhost == root domain일 때 유지된다는 의미이다. 이 값을 원하는 대로 설정해보려고 한다.
cookie의 Domain이 root domain을 보는 것을 조금 더 명확하게 보기 위해 localhost를 특정 도메인에 연결하기 위해 hosts파일을 수정한다. 아래는 localhost를 joie.example.com에 매핑한 결과이다.
$ vi /etc/hosts <----- 파일 수정으로 이동
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting. Do not change this entry.
##
127.0.0.1 localhost
255.255.255.255 broadcasthost
::1 localhost
#example
127.0.0.1 joie.example.com <------------------------- 이 부분을 수정한다.
# Added by Docker Desktop
# To allow the same kube context to work on the host and the container:
127.0.0.1 kubernetes.docker.internal
# End of section
쿠키의 Domain이 joie.example.com으로 바뀌어 있는 것을 확인할 수 있다. 이제 이 부분을 root domain (example.com)을 보도록 하려고 한다. 그래야 joie.example.com이든 woody.example.com이든 쿠키 저장소를 공유할 수 있기 때문이다. 이를 처리하는 방법이 크게 두 가지가 있다.
첫번째 방법은 .env 로 바라볼 domain name을 고정값으로 넣는 방법이다. 이렇게 할 경우 서버는 빌드 시점에서 domain을 알게 된다. 하지만 이럴 경우 설정할 서버 도메인이 바뀌면 환경 변수를 맞춰 변경해야하는 불편함이 있다.
두번째 방법은 런타임 시점에서 서버가 host의 도메인을 알 수 있게 하는 방법이다. express req의 API 중 하나인 hostname을 사용하면 express request가 보내는 host의 도메인을 알 수 있다. 이 host 정보는 trust proxy를 통과한 requst에 대해 Host HTTP header에서 확인할 수 있다.
우리는 이 중에서 (2)를 선택해 root domain만 쿠키 도메인으로 셋팅하려고 한다. 따라서 아래와 같이 설정한다. 그리고서 쿠키를 찍어보면 다음과 같이 domain 설정이 root domain으로 바뀌어 있는 것을 확인할 수 있다!
@Get()
getHello(@Req() req, @Res() res) {
const data = { name: 'joie' };
const token = this.createToken(data);
res.cookie('auth_token', token, {
httpOnly: true,
maxAge: null,
domain: req.hostname.split('.').slice(-2).join('.'), // <-----root domain만 선택
});
res.send(this.appService.getHello());
}
TBD
jwt
Reference
위의 내용은 github 코드로 정리되어 있습니다: https://github.com/erie0210/express-cookie-nestjs-react
req의 api들 https://expressjs.com/ko/api.html#req.hostname
'백엔드 개발 > 백엔드 일기' 카테고리의 다른 글
#022. github의 graphql explorer로 private repository 접근하기 (0) | 2022.11.09 |
---|---|
#021. nginx의 reverse proxy로 cors 에러 해결하기 (0) | 2022.09.18 |
#019. API 수정 (0) | 2022.06.23 |
#017. 백엔드 성장일기 : 디자인 패턴 한 문장 정리 (0) | 2022.06.01 |
#015. 백엔드 성장일기: 데이터 독 ( Data Dog )으로 서버 모니터링 하기 (0) | 2022.05.30 |