✨ Logout a single session of a authorized device
This commit is contained in:
@@ -1020,6 +1020,8 @@
|
|||||||
"uploadFile": "Upload File",
|
"uploadFile": "Upload File",
|
||||||
"authDeviceChallenges": "Device Usage",
|
"authDeviceChallenges": "Device Usage",
|
||||||
"authDeviceHint": "Swipe left to edit label, swipe right to logout device.",
|
"authDeviceHint": "Swipe left to edit label, swipe right to logout device.",
|
||||||
|
"authSessionLogout": "Logout Session",
|
||||||
|
"authSessionLogoutHint": "Are you sure you want to logout this session? This will terminate this specific login session.",
|
||||||
"settingsMessageDisplayStyle": "Message Display Style",
|
"settingsMessageDisplayStyle": "Message Display Style",
|
||||||
"auto": "Auto",
|
"auto": "Auto",
|
||||||
"manual": "Manual",
|
"manual": "Manual",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:convert';
|
|||||||
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/account.dart';
|
import 'package:island/models/account.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
@@ -37,11 +38,13 @@ class _DeviceListTile extends StatelessWidget {
|
|||||||
final SnAuthDeviceWithSession device;
|
final SnAuthDeviceWithSession device;
|
||||||
final Function(String) updateDeviceLabel;
|
final Function(String) updateDeviceLabel;
|
||||||
final Function(String) logoutDevice;
|
final Function(String) logoutDevice;
|
||||||
|
final Function(String) logoutSession;
|
||||||
|
|
||||||
const _DeviceListTile({
|
const _DeviceListTile({
|
||||||
required this.device,
|
required this.device,
|
||||||
required this.updateDeviceLabel,
|
required this.updateDeviceLabel,
|
||||||
required this.logoutDevice,
|
required this.logoutDevice,
|
||||||
|
required this.logoutSession,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -118,32 +121,47 @@ class _DeviceListTile extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
...device.sessions
|
...device.sessions
|
||||||
.map(
|
.map(
|
||||||
(session) => Column(
|
(session) => Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
spacing: 4,
|
|
||||||
children: [
|
children: [
|
||||||
InfoRow(
|
Expanded(
|
||||||
label: 'createdAt'.tr(
|
child: Column(
|
||||||
args: [session.createdAt.toLocal().formatSystem()],
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
spacing: 4,
|
||||||
|
children: [
|
||||||
|
InfoRow(
|
||||||
|
label: 'createdAt'.tr(
|
||||||
|
args: [session.createdAt.toLocal().formatSystem()],
|
||||||
|
),
|
||||||
|
icon: Symbols.join,
|
||||||
|
),
|
||||||
|
InfoRow(
|
||||||
|
label: 'lastActiveAt'.tr(
|
||||||
|
args: [
|
||||||
|
session.lastGrantedAt.toLocal().formatSystem(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
icon: Symbols.refresh_rounded,
|
||||||
|
),
|
||||||
|
InfoRow(
|
||||||
|
label:
|
||||||
|
'${'location'.tr()} ${session.location?.city ?? 'unknown'.tr()}',
|
||||||
|
icon: Symbols.pin_drop,
|
||||||
|
),
|
||||||
|
InfoRow(
|
||||||
|
label:
|
||||||
|
'${'ipAddress'.tr()} ${session.ipAddress ?? 'unknown'.tr()}',
|
||||||
|
icon: Symbols.dns,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
icon: Symbols.join,
|
|
||||||
),
|
),
|
||||||
InfoRow(
|
IconButton(
|
||||||
label: 'lastActiveAt'.tr(
|
icon: Icon(Icons.logout),
|
||||||
args: [session.lastGrantedAt.toLocal().formatSystem()],
|
tooltip: 'authSessionLogout'.tr(),
|
||||||
),
|
onPressed: () => logoutSession(session.id),
|
||||||
icon: Symbols.refresh_rounded,
|
|
||||||
),
|
|
||||||
InfoRow(
|
|
||||||
label:
|
|
||||||
'${'location'.tr()} ${session.location?.city ?? 'unknown'.tr()}',
|
|
||||||
icon: Symbols.pin_drop,
|
|
||||||
),
|
|
||||||
InfoRow(
|
|
||||||
label:
|
|
||||||
'${'ipAddress'.tr()} ${session.ipAddress ?? 'unknown'.tr()}',
|
|
||||||
icon: Symbols.dns,
|
|
||||||
),
|
),
|
||||||
|
const Gap(4),
|
||||||
],
|
],
|
||||||
).padding(horizontal: 20, vertical: 8),
|
).padding(horizontal: 20, vertical: 8),
|
||||||
)
|
)
|
||||||
@@ -178,6 +196,22 @@ class AccountSessionSheet extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void logoutSession(String sessionId) async {
|
||||||
|
final confirm = await showConfirmAlert(
|
||||||
|
'authSessionLogoutHint'.tr(),
|
||||||
|
'authSessionLogout'.tr(),
|
||||||
|
isDanger: true,
|
||||||
|
);
|
||||||
|
if (!confirm || !context.mounted) return;
|
||||||
|
try {
|
||||||
|
final apiClient = ref.watch(apiClientProvider);
|
||||||
|
await apiClient.delete('/pass/accounts/me/sessions/$sessionId');
|
||||||
|
ref.invalidate(authDevicesProvider);
|
||||||
|
} catch (err) {
|
||||||
|
showErrorAlert(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void updateDeviceLabel(String sessionId) async {
|
void updateDeviceLabel(String sessionId) async {
|
||||||
final controller = TextEditingController();
|
final controller = TextEditingController();
|
||||||
final label = await showDialog<String>(
|
final label = await showDialog<String>(
|
||||||
@@ -265,6 +299,7 @@ class AccountSessionSheet extends HookConsumerWidget {
|
|||||||
device: device,
|
device: device,
|
||||||
updateDeviceLabel: updateDeviceLabel,
|
updateDeviceLabel: updateDeviceLabel,
|
||||||
logoutDevice: logoutDevice,
|
logoutDevice: logoutDevice,
|
||||||
|
logoutSession: logoutSession,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Dismissible(
|
return Dismissible(
|
||||||
@@ -320,6 +355,7 @@ class AccountSessionSheet extends HookConsumerWidget {
|
|||||||
device: device,
|
device: device,
|
||||||
updateDeviceLabel: updateDeviceLabel,
|
updateDeviceLabel: updateDeviceLabel,
|
||||||
logoutDevice: logoutDevice,
|
logoutDevice: logoutDevice,
|
||||||
|
logoutSession: logoutSession,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user