서론
지금까지 프로젝트를 하며 토큰 방식 인증에 대해 아무런 의문을 가지지 않고 사용해 왔다. 기본 원칙은 다음과 같다.
- AT TTL은 짧게, RT TTL은 길게
- AT에 유저의 권한을 포함한 기본 정보 (민감 정보는 제외하고..) 를 담아두기
- AT 만료 후 RT를 통한 AT 갱신 시, RTR 방식 적용
프로젝트를 장기적으로 진행하며 권한 변경 API 등을 설계하던 중, 이런 의문도 들기 시작했다.
이미 AT를 발급받은 유저의 권한을 변경하는 이벤트가 일어나면?
-> 이전 권한을 가진 AT는 서버에서 어떻게 대응해야 하는가?
토큰 방식 인증/인가는 stateless 하기 때문에 (정확하게는 AT에만 해당하는 말이지만), 서버는 AT만을 보고 권한 판단을 할 수 밖에 없다. 이렇게 중간에 권한 변경이 일어난다면, 인증 구조를 바꿔서 설계해볼 필요가 있었기에 몇가지 해결 전략에 대해 고민해 보았다.
해결 전략
1. AT TTL을 더 짧게 하자
사실 이 해결책은 기존 구조와는 차이가 없다. 단지 AT의 만료 시점을 훨씬 앞당겨서 권한 변경 이벤트가 최대한 신속히 유저에게 반영될 수 있도록 하는 것이다.
자세히 말하자면, AT TTL이 짧으니 AT 만료 주기가 기존보다 훨씬 빨라지고, 따라서 RT를 통해 새로운 AT를 발급받아, 권한 변경이 토큰에 반영되는 시점이 앞당겨지는 것이다.
- 장점: 구현이 쉽고, AT의 stateless 특성이 깨지지 않는다.
- 단점: 즉각적인 권한 변경 적용이 어렵다.
AT TTL은 적절한 시점을 잘 잡아야 하는데, 단점을 해결하고자 TTL을 과도하게 빠르게 하면 (ex. 10초) 사용자는 api 요청 시마다 AT,RT를 재발급받는 번거로운 상황에 놓일 것이다.
2. Token Versioning
유저 엔티티에 version 필드를 추가하여 AT 검사에 도입하는 방식이다. 다음과 같은 방식으로 동작한다.
- User 엔티티에 version 필드를 추가한다. (여기서는 version이 정수형이라고 가정한다.)
- 권한 변경 이벤트가 일어날 경우, 변경된 User에 대해서 version++ 처리를 해준다.
- AT는 권한과 더불어 발급 시점의 User version을 담고 있도록 한다.
- 이전 권한 정보를 담은 AT가 들어오면, 현재 version과 일치하지 않으므로 거부된다.
이때, 4번 과정에서 refresh를 하여 AT를 최신화 해줄 수 있다.
- 장점: 권한 변경이 즉시 반영된다
- 단점: AT의 완전한 stateless가 깨진다. AT의 version 일치 여부 확인을 위해 DB 접근이 항상 요구된다.
아무래도 매 요청마다 User의 version 확인을 하는 DB Transaction이 생기는 것은 속도도 느려지고, 부하를 유발할 것이다. 따라서 현재 AT 인증이 자주 일어나는 User에 대해서, version을 redis에 캐싱하는 방향으로 성능 향상을 노려볼 수 있다.
이 외에, 권한이 userId를 Blacklist로 캐싱해두는 방식 또한 Token Versioning과 같은 맥락을 공유한다고 볼 수 있다.
3. AT에 권한을 포함시키지 말자
기존에는 AT에 권한 정보도 같이 넣었기에 권한 최신화 문제가 발생했다. 그렇다면 권한 정보를 빼버리고, 매 요청마다 해당 userId에 대해서 권한 검증을 하는 것은 어떨까?
- 장점: 권한 변경이 즉시 반영된다 (DB에서 최신 정보를 확인하므로)
- 단점: AT의 완전한 stateless가 깨진다. 해당하는 User의 권한을 얻기 위해 DB 접근이 항상 요구된다.
방식은 차이가 있으나, 2번과 거의 비슷한 장단점을 공유한다. 따라서 이 또한 userId/role의 key/value 캐싱을 통해 성능 향상을 노려볼 수 있을 것이라 생각한다.
결론
우리 프로젝트에서는 권한 변경 이벤트가 거의 일어나지 않고, 일어나더라도 예측 가능한 범위에서 일어나기 때문에 기존처럼 AT를 유지하기로 했다.
서비스가 복잡해지는 실무 환경에서는 이러한 이슈에 대해 정말 고민을 많이 해야 할 것 같고, 따라서 상술한 전략 외에 더 나은 전략이 분명 있을 것이다. 보안과 성능 그 사이의 절충안을 마련하는 것은 서버 인프라 구축에서 뗄래야 떼어놓을 수 없는 포인트가 아닌가 싶다.