Flutter - 리프레시 토큰으로 갱신하기

#Flutter
• • •

개요

  • Dio를 사용하면 인터셉터를 쉽게 구현할 수 있음
  • 인터셉터에서 401 응답에 따라 리프레시 토큰을 사용하는 방식
  • 본 예제에서 리프레시 토큰은 클라이언트가 직접 다루지 않고 httpOnly 쿠키로 브라우저에 저장되었다고 가정

구현 예시

  • 클라이언트가 브라우저 환경에서 httpOnly 쿠키를 서버에 보낼 수 있도록 하려면 withCredentials: true 옵션 필요
!! 서버에서도 CORS 및 credentials 설정 필요
class AuthClient {
  final Dio _dio;
  final AuthService authService;

  AuthClient({
    required this.authService,
  }) : _dio = Dio(
	  BaseOptions(
	    baseUrl: 'your_api_url',
	    extra: {
	      if (kIsWeb) 'withCredentials': true,
	    },
	  ),
	);
  
  // ..
  
  Future<Result<dynamic, AuthError>> refreshToken() async {
    try {
      final response = await _dio.get('/auth/refresh');
      final newToken = response.data;
      authService.refresh(newToken);
      return Result.ok(newToken);
    } catch (e) {
      authService.signout();
      debugPrint(e.toString());
      return Result.error(AuthError.refreshError);
    }
  }
}
AuthService는 토큰을 저장하고 관련된 이벤트를 발행하는 서비스라고 가정
AuthInterceptor에서 onError 처리
class AuthInterceptor extends Interceptor {
  final TokenProvider tokenProvider;

  AuthInterceptor({
    required this.tokenProvider,
  });
	
  // ...

  @override
  void onError(
    DioException err,
    ErrorInterceptorHandler handler,
  ) async {
    final token = await tokenProvider.getToken();

    if (token != null && err.response?.statusCode == 401) {
      final result = await authClient.refreshToken();

      switch (result) {
        case Success<dynamic, AuthError>(:final data):
          final retryRequest = err.requestOptions
            ..headers['Authorization'] = 'Bearer $data';
          final response = await Dio().fetch(retryRequest);
          return handler.resolve(response);
        case Failure<dynamic, AuthError>(:final error):
          return handler.next(
            DioException(
              requestOptions: err.requestOptions,
              error: error,
            ),
          );
      }
    }

    return handler.next(err);
  }
}
저장된 토큰이 있는데 401 응답인 경우에 토큰 요청
인터셉터 주입 후 추가
class DioApiClient implements ApiClient {
  final Dio _dio;

  DioApiClient({
    required Interceptor authInterceptor,
  })  : _dio = Dio()
          ..interceptors.add(authInterceptor)
          ..interceptors.add(otherInterceptor);
	
  // ..
}

참고

  • ChatGPT 4
published 10 months ago · last updated 10 months ago