From 1b7e668b3fa5d234eeb9cea1592f4de0363ebb9a Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sat, 15 Mar 2025 21:11:49 +0800 Subject: [PATCH] :sparkles: Dispose current session when logout --- assets/translations/en-US.json | 3 +- assets/translations/zh-CN.json | 3 +- assets/translations/zh-HK.json | 3 +- assets/translations/zh-TW.json | 3 +- lib/providers/userinfo.dart | 36 ++++++++++++++++++- lib/router.dart | 2 +- .../{tickets.dart => auth_tickets.dart} | 22 ++++++++++-- 7 files changed, 63 insertions(+), 9 deletions(-) rename lib/screens/account/{tickets.dart => auth_tickets.dart} (89%) diff --git a/assets/translations/en-US.json b/assets/translations/en-US.json index 5b57083..55be92b 100644 --- a/assets/translations/en-US.json +++ b/assets/translations/en-US.json @@ -813,5 +813,6 @@ "eventMetadata": "Metadata", "authTicketCreatedAt": "Issued at {}", "authTicketExpiredAt": "Expired at {}", - "authTicketLastGrantAt": "Last granted at {}" + "authTicketLastGrantAt": "Last granted at {}", + "authTicketCurrent": "Current" } diff --git a/assets/translations/zh-CN.json b/assets/translations/zh-CN.json index 1e13166..cd0353d 100644 --- a/assets/translations/zh-CN.json +++ b/assets/translations/zh-CN.json @@ -813,5 +813,6 @@ "accountAuthTicketsDescription": "查看和管理你的授权会话。", "authTicketCreatedAt": "签发于 {}", "authTicketExpiredAt": "到期于 {}", - "authTicketLastGrantAt": "上次刷新于 {}" + "authTicketLastGrantAt": "上次刷新于 {}", + "authTicketCurrent": "当前会话" } diff --git a/assets/translations/zh-HK.json b/assets/translations/zh-HK.json index a887fcc..a61d579 100644 --- a/assets/translations/zh-HK.json +++ b/assets/translations/zh-HK.json @@ -813,5 +813,6 @@ "accountAuthTicketsDescription": "查看和管理你的授權會話。", "authTicketCreatedAt": "簽發於 {}", "authTicketExpiredAt": "到期於 {}", - "authTicketLastGrantAt": "上次刷新於 {}" + "authTicketLastGrantAt": "上次刷新於 {}", + "authTicketCurrent": "當前會話" } diff --git a/assets/translations/zh-TW.json b/assets/translations/zh-TW.json index ba23621..eaa07d2 100644 --- a/assets/translations/zh-TW.json +++ b/assets/translations/zh-TW.json @@ -813,5 +813,6 @@ "accountAuthTicketsDescription": "查看和管理你的授權會話。", "authTicketCreatedAt": "簽發於 {}", "authTicketExpiredAt": "到期於 {}", - "authTicketLastGrantAt": "上次刷新於 {}" + "authTicketLastGrantAt": "上次刷新於 {}", + "authTicketCurrent": "當前會話" } diff --git a/lib/providers/userinfo.dart b/lib/providers/userinfo.dart index 15e966e..fac3563 100644 --- a/lib/providers/userinfo.dart +++ b/lib/providers/userinfo.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -35,6 +37,32 @@ class UserProvider extends ChangeNotifier { }); } + Future?> get atkClaims async { + final tk = (await atk); + if (tk == null) return null; + final atkParts = tk.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); + return jsonDecode(b64.decode(rawPayload)); + } + Future refreshUser() async { final resp = await _sn.client.get('/cgi/id/users/me'); final out = SnAccount.fromJson(resp.data); @@ -47,7 +75,13 @@ class UserProvider extends ChangeNotifier { } void logoutUser() async { - _sn.clearTokenPair(); + atkClaims.then((value) async { + if (value != null) { + await _sn.client.delete('/cgi/id/users/me/tickets/${value['sed']}'); + logging.info('[Auth] Current session has been destroyed.'); + } + _sn.clearTokenPair(); + }); isAuthorized = false; user = null; notifyListeners(); diff --git a/lib/router.dart b/lib/router.dart index f4f2d0f..7c32e7b 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -13,7 +13,7 @@ import 'package:surface/screens/account/profile_edit.dart'; import 'package:surface/screens/account/publishers/publisher_edit.dart'; import 'package:surface/screens/account/publishers/publisher_new.dart'; import 'package:surface/screens/account/publishers/publishers.dart'; -import 'package:surface/screens/account/tickets.dart'; +import 'package:surface/screens/account/auth_tickets.dart'; import 'package:surface/screens/album.dart'; import 'package:surface/screens/auth/login.dart'; import 'package:surface/screens/auth/register.dart'; diff --git a/lib/screens/account/tickets.dart b/lib/screens/account/auth_tickets.dart similarity index 89% rename from lib/screens/account/tickets.dart rename to lib/screens/account/auth_tickets.dart index 5b7b11e..eb59662 100644 --- a/lib/screens/account/tickets.dart +++ b/lib/screens/account/auth_tickets.dart @@ -5,6 +5,7 @@ import 'package:material_symbols_icons/symbols.dart'; import 'package:provider/provider.dart'; import 'package:styled_widget/styled_widget.dart'; import 'package:surface/providers/sn_network.dart'; +import 'package:surface/providers/userinfo.dart'; import 'package:surface/types/auth.dart'; import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/loading_indicator.dart'; @@ -73,10 +74,18 @@ class _AccountAuthTicketState extends State { } } + int? _currentTicketId; + @override void initState() { super.initState(); _fetchAuthTickets(); + + final ua = context.read(); + ua.atkClaims.then((value) { + if (value == null) return; + _currentTicketId = int.parse(value['sed']); + }); } @override @@ -142,6 +151,11 @@ class _AccountAuthTicketState extends State { .format(ticket.lastGrantAt!.toLocal())) ])).fontSize(12).opacity(0.75), const Gap(4), + if (_currentTicketId == ticket.id) + Text('authTicketCurrent'.tr()) + .fontSize(11) + .bold() + .opacity(0.75), Text('#${ticket.id}').fontSize(11).opacity(0.75), ], ), @@ -153,9 +167,11 @@ class _AccountAuthTicketState extends State { constraints: const BoxConstraints(), padding: EdgeInsets.zero, icon: const Icon(Symbols.logout), - onPressed: () { - _deleteAuthTicket(ticket); - }, + onPressed: _currentTicketId == ticket.id + ? null + : () { + _deleteAuthTicket(ticket); + }, ), ], ).padding(horizontal: 16, vertical: 12);