🐛 Fix refresh token
This commit is contained in:
parent
4d12d243b3
commit
a629f5e12c
@ -35,5 +35,13 @@
|
||||
"loginEnterPassword": "Enter the code",
|
||||
"loginSuccess": "Logged in as {}",
|
||||
"authFactorPassword": "Password",
|
||||
"authFactorEmail": "Email verification code"
|
||||
"authFactorEmail": "Email verification code",
|
||||
"accountIntroTitle": "Hello there!",
|
||||
"accountIntroSubtitle": "Pick an option below to get started.",
|
||||
"accountLogout": "Logout",
|
||||
"accountLogoutSubtitle": "Log out of the current account.",
|
||||
"accountLogoutConfirmTitle": "Are you sure you want to logout?",
|
||||
"accountLogoutConfirm": "You will need to re-enter your account password, even if you have already done so. This is required to login again.",
|
||||
"accountPublishers": "Your publishers",
|
||||
"accountPublishersSubtitle": "Manage your publish identities."
|
||||
}
|
||||
|
@ -35,5 +35,13 @@
|
||||
"loginEnterPassword": "验证代码",
|
||||
"loginSuccess": "登录为 {}",
|
||||
"authFactorPassword": "密码",
|
||||
"authFactorEmail": "电邮一次性验证码"
|
||||
"authFactorEmail": "电邮一次性验证码",
|
||||
"accountIntroTitle": "喜欢您来!",
|
||||
"accountIntroSubtitle": "登陆以探索更广大的世界。",
|
||||
"accountLogout": "退出登录",
|
||||
"accountLogoutSubtitle": "注销当前账户的登陆状态。",
|
||||
"accountLogoutConfirmTitle": "您确定要退出登录吗?",
|
||||
"accountLogoutConfirm": "您需要重新输入账号密码,甚至可能需要多步验证来再次登陆。",
|
||||
"accountPublishers": "你的发布者",
|
||||
"accountPublishersSubtitle": "管理你的公共形象。"
|
||||
}
|
||||
|
@ -4,12 +4,11 @@ import 'package:surface/providers/sn_network.dart';
|
||||
import 'package:surface/types/attachment.dart';
|
||||
|
||||
class SnAttachmentProvider {
|
||||
late final SnNetworkProvider sn;
|
||||
|
||||
late final SnNetworkProvider _sn;
|
||||
final Map<String, SnAttachment> _cache = {};
|
||||
|
||||
SnAttachmentProvider(BuildContext context) {
|
||||
sn = context.read<SnNetworkProvider>();
|
||||
_sn = context.read<SnNetworkProvider>();
|
||||
}
|
||||
|
||||
Future<SnAttachment> getOne(String rid, {noCache = false}) async {
|
||||
@ -17,7 +16,7 @@ class SnAttachmentProvider {
|
||||
return _cache[rid]!;
|
||||
}
|
||||
|
||||
final resp = await sn.client.get('/cgi/uc/attachments/$rid/meta');
|
||||
final resp = await _sn.client.get('/cgi/uc/attachments/$rid/meta');
|
||||
final out = SnAttachment.fromJson(resp.data);
|
||||
_cache[rid] = out;
|
||||
|
||||
@ -33,7 +32,7 @@ class SnAttachmentProvider {
|
||||
return rids.map((rid) => _cache[rid]!).toList();
|
||||
}
|
||||
|
||||
final resp = await sn.client.get('/cgi/uc/attachments', queryParameters: {
|
||||
final resp = await _sn.client.get('/cgi/uc/attachments', queryParameters: {
|
||||
'take': pendingFetch.length,
|
||||
'id': pendingFetch.join(','),
|
||||
});
|
||||
|
@ -67,7 +67,7 @@ class SnNetworkProvider {
|
||||
final b64 = utf8.fuse(base64Url);
|
||||
final payload = b64.decode(rawPayload);
|
||||
final exp = jsonDecode(payload)['exp'];
|
||||
if (exp >= DateTime.now().millisecondsSinceEpoch) {
|
||||
if (exp <= DateTime.now().millisecondsSinceEpoch ~/ 1000) {
|
||||
log('Access token need refresh, doing it at ${DateTime.now()}');
|
||||
atk = await refreshToken();
|
||||
}
|
||||
@ -78,6 +78,8 @@ class SnNetworkProvider {
|
||||
log('Access token refresh failed...');
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
log('Failed to authenticate user: $err');
|
||||
} finally {
|
||||
handler.next(options);
|
||||
}
|
||||
@ -114,7 +116,12 @@ class SnNetworkProvider {
|
||||
final rtk = await _storage.read(key: kRtkStoreKey);
|
||||
if (rtk == null) return null;
|
||||
|
||||
final resp = await client.post('/cgi/id/auth/token', data: {
|
||||
final dio = Dio();
|
||||
dio.options.baseUrl = kUseLocalNetwork
|
||||
? 'http://localhost:8001'
|
||||
: 'https://api.sn.solsynth.dev';
|
||||
|
||||
final resp = await dio.post('/cgi/id/auth/token', data: {
|
||||
'grant_type': 'refresh_token',
|
||||
'refresh_token': rtk,
|
||||
});
|
||||
|
@ -10,12 +10,11 @@ class UserProvider extends ChangeNotifier {
|
||||
bool isAuthorized = false;
|
||||
SnAccount? user;
|
||||
|
||||
late final SnNetworkProvider sn;
|
||||
|
||||
late final SnNetworkProvider _sn;
|
||||
late final FlutterSecureStorage _storage = FlutterSecureStorage();
|
||||
|
||||
UserProvider(BuildContext context) {
|
||||
sn = context.read<SnNetworkProvider>();
|
||||
_sn = context.read<SnNetworkProvider>();
|
||||
|
||||
_storage.read(key: kAtkStoreKey).then((value) {
|
||||
isAuthorized = value != null;
|
||||
@ -31,7 +30,7 @@ class UserProvider extends ChangeNotifier {
|
||||
Future<SnAccount?> refreshUser() async {
|
||||
if (!isAuthorized) return null;
|
||||
|
||||
final resp = await sn.client.get('/cgi/id/users/me');
|
||||
final resp = await _sn.client.get('/cgi/id/users/me');
|
||||
final out = SnAccount.fromJson(resp.data);
|
||||
|
||||
user = out;
|
||||
@ -39,4 +38,11 @@ class UserProvider extends ChangeNotifier {
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void logoutUser() async {
|
||||
_sn.clearTokenPair();
|
||||
isAuthorized = false;
|
||||
user = null;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
@ -1,44 +1,153 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:surface/providers/userinfo.dart';
|
||||
import 'package:surface/widgets/account/account_image.dart';
|
||||
import 'package:surface/widgets/dialog.dart';
|
||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||
|
||||
class AccountScreen extends StatefulWidget {
|
||||
class AccountScreen extends StatelessWidget {
|
||||
const AccountScreen({super.key});
|
||||
|
||||
@override
|
||||
State<AccountScreen> createState() => _AccountScreenState();
|
||||
}
|
||||
|
||||
class _AccountScreenState extends State<AccountScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ua = context.watch<UserProvider>();
|
||||
|
||||
return AppScaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("screenAccount").tr(),
|
||||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text('screenAuthLogin').tr(),
|
||||
subtitle: Text('screenAuthLoginSubtitle').tr(),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () {
|
||||
GoRouter.of(context).pushNamed('authLogin');
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text('screenAuthRegister').tr(),
|
||||
subtitle: Text('screenAuthRegisterSubtitle').tr(),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () {
|
||||
GoRouter.of(context).pushNamed('authRegister');
|
||||
},
|
||||
)
|
||||
],
|
||||
body: SingleChildScrollView(
|
||||
child: ua.isAuthorized
|
||||
? _AuthorizedAccountScreen()
|
||||
: _UnauthorizedAccountScreen(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AuthorizedAccountScreen extends StatelessWidget {
|
||||
const _AuthorizedAccountScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ua = context.watch<UserProvider>();
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Card(
|
||||
child: Builder(builder: (context) {
|
||||
if (ua.user == null) {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: 120,
|
||||
child: CircularProgressIndicator().center(),
|
||||
);
|
||||
}
|
||||
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AccountImage(content: ua.user!.avatar, radius: 28),
|
||||
const Gap(8),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||
textBaseline: TextBaseline.alphabetic,
|
||||
children: [
|
||||
Text(ua.user!.nick)
|
||||
.textStyle(Theme.of(context).textTheme.titleLarge!),
|
||||
const Gap(4),
|
||||
Text('@${ua.user!.name}')
|
||||
.textStyle(Theme.of(context).textTheme.bodySmall!),
|
||||
],
|
||||
),
|
||||
Text(ua.user!.description)
|
||||
.textStyle(Theme.of(context).textTheme.bodyMedium!),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).padding(all: 20),
|
||||
).padding(horizontal: 8, top: 16, bottom: 4),
|
||||
ListTile(
|
||||
title: Text('accountPublishers').tr(),
|
||||
subtitle: Text('accountPublishersSubtitle').tr(),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
leading: const Icon(Symbols.face),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () {},
|
||||
),
|
||||
ListTile(
|
||||
title: Text('accountLogout').tr(),
|
||||
subtitle: Text('accountLogoutSubtitle').tr(),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
leading: const Icon(Symbols.logout),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () {
|
||||
context
|
||||
.showConfirmDialog(
|
||||
'accountLogoutConfirmTitle'.tr(),
|
||||
'accountLogoutConfirm'.tr(),
|
||||
)
|
||||
.then((value) {
|
||||
if (value) ua.logoutUser();
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _UnauthorizedAccountScreen extends StatelessWidget {
|
||||
const _UnauthorizedAccountScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Card(
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Icon(Symbols.waving_hand, size: 32),
|
||||
const Gap(8),
|
||||
Text('accountIntroTitle')
|
||||
.tr()
|
||||
.textStyle(Theme.of(context).textTheme.titleLarge!),
|
||||
Text('accountIntroSubtitle').tr(),
|
||||
],
|
||||
).padding(all: 20),
|
||||
),
|
||||
).padding(horizontal: 8, top: 16, bottom: 4),
|
||||
ListTile(
|
||||
title: Text('screenAuthLogin').tr(),
|
||||
subtitle: Text('screenAuthLoginSubtitle').tr(),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
leading: const Icon(Symbols.login),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () {
|
||||
GoRouter.of(context).pushNamed('authLogin');
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text('screenAuthRegister').tr(),
|
||||
subtitle: Text('screenAuthRegisterSubtitle').tr(),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
leading: const Icon(Symbols.person_add),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () {
|
||||
GoRouter.of(context).pushNamed('authRegister');
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user