이슈
위 포스트에서 OAuth2.0
방식의 소셜 로그인을 구현함으로써 AccessToken
과 RefreshToken
을 이용한 인증 방식을 사용하였다고 밝혔다. 이 때 RefreshToken
은 AccessToken
이 탈취당했을 상황을 고려하여 토큰에 만료 시간을 부여함에 따라 토큰을 갱신해줄 때 사용되는 토큰이다. 토큰이 갱신되는 과정을 나열하면 다음과 같다.
인증 토큰 갱신 과정
- Client에서 Server로
AccessToken
을 담아 API 요청 - 이 때
AccessToken
이 만료됨에 따라 Server에서 인증 오류 반환 - Client에서 인증 오류를 확인하고
RefreshToken
을 담아 토큰 갱신 API 요청 RefreshToken
만료에 따른 분기RefreshToken
이 만료되지 않았을 경우 Server에서 새로운AccessToken
발급RefreshToken
도 만료되었을 경우 Server에서 다시 인증 오류를 반환하고 Client에서 이를 확인 후 로그인 재요청 화면 표시
어러한 과정을 flutter
에서 어떻게 구현할 수 있을까? 매 API
통신 코드마다 해당 과정을 수행하는 코드를 덧붙일 순 없을 것이다. 이를 더욱 쉽게 핸들링하는 방법은 무엇일까? 더 나아가 매 요청마다 항상 AccessToken
을 자동으로 담아줄 순 없을까?
해결
핵심
Dio의 interceptor을 이용하여 매 통신마다 수행할 핸들러를 구현한다.
라이브러리
코드
1 |
|
고찰
Dio interceptor
함수의 인자로 BuildContext
를 따로 받는 이유는 Dialog
를 사용해 로그인 만료를 알리거나 Navigator
를 통해 로그인 재요청 페이지로 이동하려면 현재 사용자가 머물고 있는 화면의 context
가 필요하기 때문이다. 또한 요청하는 API
의 도메인이 항상 같다면 baseUrl
속성을 본 dio
객체에 부여할 수도 있다.
위 코드에서 최종적으로 반환하는 dio
객체가 하는 일은 다음과 같다.
- 매 요청마다 헤더에
AccessToken
추가 - 인증 오류(401) 발생 시
RefreshToken
을 담아 토큰 갱신 API 요청 - 토큰 갱신 성공 시 새로운
AccessToken
으로 교체하여 기존 API 재요청 RefreshToken
만료로 인해 토큰 갱신 실패 시 로그인 재요청
따라서 위 코드를 따로 선언해두고 앞으로 모든 요청에 대해 본 dio
객체를 사용하면 된다.
예를 들어 다음과 같다.
1 |
|
위와 같이 일반적인 API
통신에서 앞서 구현한 auth dio를 사용함으로써 매 요청마다 토큰
을 담거나 갱신하는 것에 대해 따로 신경 쓸 필요가 없어진다.
Flutter Secure Storage
위 코드에서 잠깐 등장한 flutter secure storage
는 기기의 보안 저장소를 이용할 수 있게 하는 라이브러리이다. 단순한 상태
나 설정값
들을 저장할 때는 SharedPreferences
를 이용하지만 인증 토큰
과 같은 민감한 정보들을 기기에 저장할 때에는 이와 같은 보안 저장소를 이용한다. 이러한 토큰을 기기에 따로 저장하는 가장 큰 이유는 바로 자동 로그인 때문이다. 자동 로그인을 사용하지 않으면 사용자는 앱을 실행하는 매 순간마다 번거롭게 로그인 버튼을 눌러 로그인을 진행해야 한다. 하지만 이와 같이 기기의 안전한 곳에 토큰
을 저장해두면 앱을 최초로 로드하는 부분에서 이를 불러옴으로써 로그인 과정을 생략할 수 있다. 앞서 포스팅한 Splash 화면을 통한 초기 로딩 구현하기 와 연계되는 부분이다. 그러나 이렇게 자동 로그인을 구현하였다면 위 코드와 같이 로그인이 만료되었을 시 기기에 저장된 토큰 정보도 삭제해줘야 하는 것에 유의하자.