✨ Login & register
This commit is contained in:
@ -1,15 +1,23 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:dio_smart_retry/dio_smart_retry.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:native_dio_adapter/native_dio_adapter.dart';
|
||||
|
||||
const kUseLocalNetwork = true;
|
||||
|
||||
const kAtkStoreKey = 'nex_user_atk';
|
||||
const kRtkStoreKey = 'nex_user_rtk';
|
||||
|
||||
class SnNetworkProvider {
|
||||
late final Dio client;
|
||||
|
||||
late final FlutterSecureStorage _storage = FlutterSecureStorage();
|
||||
|
||||
SnNetworkProvider() {
|
||||
client = Dio();
|
||||
|
||||
@ -27,6 +35,56 @@ class SnNetworkProvider {
|
||||
],
|
||||
));
|
||||
|
||||
client.interceptors.add(
|
||||
InterceptorsWrapper(
|
||||
onRequest: (
|
||||
RequestOptions options,
|
||||
RequestInterceptorHandler handler,
|
||||
) async {
|
||||
try {
|
||||
var atk = await _storage.read(key: kAtkStoreKey);
|
||||
if (atk != null) {
|
||||
final atkParts = atk.split('.');
|
||||
if (atkParts.length != 3) {
|
||||
throw Exception('invalid format of access token');
|
||||
}
|
||||
|
||||
var rawPayload =
|
||||
atkParts[1].replaceAll('-', '+').replaceAll('_', '/');
|
||||
switch (rawPayload.length % 4) {
|
||||
case 0:
|
||||
break;
|
||||
case 2:
|
||||
rawPayload += '==';
|
||||
break;
|
||||
case 3:
|
||||
rawPayload += '=';
|
||||
break;
|
||||
default:
|
||||
throw Exception('illegal format of access token payload');
|
||||
}
|
||||
|
||||
final b64 = utf8.fuse(base64Url);
|
||||
final payload = b64.decode(rawPayload);
|
||||
final exp = jsonDecode(payload)['exp'];
|
||||
if (exp >= DateTime.now().millisecondsSinceEpoch) {
|
||||
log('Access token need refresh, doing it at ${DateTime.now()}');
|
||||
atk = await refreshToken();
|
||||
}
|
||||
|
||||
if (atk != null) {
|
||||
options.headers['Authorization'] = 'Bearer $atk';
|
||||
} else {
|
||||
log('Access token refresh failed...');
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
handler.next(options);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS || Platform.isMacOS)) {
|
||||
// Switch to native implementation if possible
|
||||
client.httpClientAdapter = NativeAdapter();
|
||||
@ -37,4 +95,34 @@ class SnNetworkProvider {
|
||||
if (ky.startsWith("http://")) return ky;
|
||||
return '${client.options.baseUrl}/cgi/uc/attachments/$ky';
|
||||
}
|
||||
|
||||
Future<void> setTokenPair(String atk, String rtk) async {
|
||||
await Future.wait([
|
||||
_storage.write(key: kAtkStoreKey, value: atk),
|
||||
_storage.write(key: kRtkStoreKey, value: rtk),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> clearTokenPair() async {
|
||||
await Future.wait([
|
||||
_storage.delete(key: kAtkStoreKey),
|
||||
_storage.delete(key: kRtkStoreKey),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<String?> refreshToken() async {
|
||||
final rtk = await _storage.read(key: kRtkStoreKey);
|
||||
if (rtk == null) return null;
|
||||
|
||||
final resp = await client.post('/cgi/id/auth/token', data: {
|
||||
'grant_type': 'refresh_token',
|
||||
'refresh_token': rtk,
|
||||
});
|
||||
|
||||
final atk = resp.data['access_token'];
|
||||
final nRtk = resp.data['refresh_token'];
|
||||
await setTokenPair(atk, nRtk);
|
||||
|
||||
return atk;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user