전역 구조 리팩터링 및 테스트 확장
This commit is contained in:
19
lib/core/services/token_storage.dart
Normal file
19
lib/core/services/token_storage.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import 'token_storage_stub.dart'
|
||||
if (dart.library.html) 'token_storage_web.dart'
|
||||
if (dart.library.io) 'token_storage_native.dart';
|
||||
|
||||
/// 액세스/리프레시 토큰을 안전하게 보관하는 스토리지 인터페이스.
|
||||
abstract class TokenStorage {
|
||||
Future<void> writeAccessToken(String? token);
|
||||
|
||||
Future<String?> readAccessToken();
|
||||
|
||||
Future<void> writeRefreshToken(String? token);
|
||||
|
||||
Future<String?> readRefreshToken();
|
||||
|
||||
Future<void> clear();
|
||||
}
|
||||
|
||||
/// 플랫폼에 맞는 스토리지 구현체를 생성한다.
|
||||
TokenStorage createTokenStorage() => buildTokenStorage();
|
||||
53
lib/core/services/token_storage_native.dart
Normal file
53
lib/core/services/token_storage_native.dart
Normal file
@@ -0,0 +1,53 @@
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
|
||||
import 'token_storage.dart';
|
||||
|
||||
const _kAccessTokenKey = 'access_token';
|
||||
const _kRefreshTokenKey = 'refresh_token';
|
||||
|
||||
TokenStorage buildTokenStorage() {
|
||||
const storage = FlutterSecureStorage(
|
||||
aOptions: AndroidOptions(encryptedSharedPreferences: true),
|
||||
iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock),
|
||||
mOptions: MacOsOptions(),
|
||||
wOptions: WindowsOptions(),
|
||||
lOptions: LinuxOptions(),
|
||||
);
|
||||
return _SecureTokenStorage(storage);
|
||||
}
|
||||
|
||||
class _SecureTokenStorage implements TokenStorage {
|
||||
const _SecureTokenStorage(this._storage);
|
||||
|
||||
final FlutterSecureStorage _storage;
|
||||
|
||||
@override
|
||||
Future<void> clear() async {
|
||||
await _storage.delete(key: _kAccessTokenKey);
|
||||
await _storage.delete(key: _kRefreshTokenKey);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> readAccessToken() => _storage.read(key: _kAccessTokenKey);
|
||||
|
||||
@override
|
||||
Future<String?> readRefreshToken() => _storage.read(key: _kRefreshTokenKey);
|
||||
|
||||
@override
|
||||
Future<void> writeAccessToken(String? token) async {
|
||||
if (token == null || token.isEmpty) {
|
||||
await _storage.delete(key: _kAccessTokenKey);
|
||||
return;
|
||||
}
|
||||
await _storage.write(key: _kAccessTokenKey, value: token);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> writeRefreshToken(String? token) async {
|
||||
if (token == null || token.isEmpty) {
|
||||
await _storage.delete(key: _kRefreshTokenKey);
|
||||
return;
|
||||
}
|
||||
await _storage.write(key: _kRefreshTokenKey, value: token);
|
||||
}
|
||||
}
|
||||
24
lib/core/services/token_storage_stub.dart
Normal file
24
lib/core/services/token_storage_stub.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
import 'token_storage.dart';
|
||||
|
||||
TokenStorage buildTokenStorage() => _UnsupportedTokenStorage();
|
||||
|
||||
class _UnsupportedTokenStorage implements TokenStorage {
|
||||
Never _unsupported() {
|
||||
throw UnsupportedError('TokenStorage is not supported on this platform.');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> clear() async => _unsupported();
|
||||
|
||||
@override
|
||||
Future<String?> readAccessToken() async => _unsupported();
|
||||
|
||||
@override
|
||||
Future<String?> readRefreshToken() async => _unsupported();
|
||||
|
||||
@override
|
||||
Future<void> writeAccessToken(String? token) async => _unsupported();
|
||||
|
||||
@override
|
||||
Future<void> writeRefreshToken(String? token) async => _unsupported();
|
||||
}
|
||||
45
lib/core/services/token_storage_web.dart
Normal file
45
lib/core/services/token_storage_web.dart
Normal file
@@ -0,0 +1,45 @@
|
||||
// ignore: deprecated_member_use, avoid_web_libraries_in_flutter
|
||||
import 'dart:html' as html;
|
||||
|
||||
import 'token_storage.dart';
|
||||
|
||||
const _kAccessTokenKey = 'access_token';
|
||||
const _kRefreshTokenKey = 'refresh_token';
|
||||
|
||||
TokenStorage buildTokenStorage() => _WebTokenStorage(html.window.localStorage);
|
||||
|
||||
class _WebTokenStorage implements TokenStorage {
|
||||
const _WebTokenStorage(this._storage);
|
||||
|
||||
final Map<String, String> _storage;
|
||||
|
||||
@override
|
||||
Future<void> clear() async {
|
||||
_storage.remove(_kAccessTokenKey);
|
||||
_storage.remove(_kRefreshTokenKey);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> readAccessToken() async => _storage[_kAccessTokenKey];
|
||||
|
||||
@override
|
||||
Future<String?> readRefreshToken() async => _storage[_kRefreshTokenKey];
|
||||
|
||||
@override
|
||||
Future<void> writeAccessToken(String? token) async {
|
||||
if (token == null || token.isEmpty) {
|
||||
_storage.remove(_kAccessTokenKey);
|
||||
} else {
|
||||
_storage[_kAccessTokenKey] = token;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> writeRefreshToken(String? token) async {
|
||||
if (token == null || token.isEmpty) {
|
||||
_storage.remove(_kRefreshTokenKey);
|
||||
} else {
|
||||
_storage[_kRefreshTokenKey] = token;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user