1
0
forked from baron/baron-sso

headless login 접속환경 Headless(Server)로 표시

This commit is contained in:
2026-04-14 11:24:17 +09:00
parent 92f8e9a61a
commit c5317abada
3 changed files with 115 additions and 6 deletions

View File

@@ -0,0 +1,31 @@
import 'package:userfront/features/dashboard/domain/models.dart';
const headlessServerUserAgentSentinel = '__headless_server__';
bool looksLikeInternalAuditUserAgent(String userAgent) {
final lower = userAgent.trim().toLowerCase();
return lower.startsWith('go-http-client/') ||
lower.startsWith('fasthttp') ||
lower.startsWith('fiber') ||
lower.startsWith('undici') ||
lower.startsWith('node');
}
String preferredAuditLogUserAgent(AuditLogEntry log) {
final userAgent = log.userAgent.trim();
final path = log.path.toLowerCase();
final isHeadlessLinkLog =
path.contains('/api/v1/auth/magic-link/verify') ||
path.contains('/api/v1/auth/login/code/verify');
final isHeadlessPasswordLog = path.contains(
'/api/v1/auth/headless/password/login',
);
if ((isHeadlessLinkLog || isHeadlessPasswordLog) &&
looksLikeInternalAuditUserAgent(userAgent)) {
return headlessServerUserAgentSentinel;
}
return userAgent;
}

View File

@@ -25,6 +25,7 @@ import '../../../../core/ui/toast_service.dart';
import '../../profile/domain/notifiers/profile_notifier.dart';
import '../domain/dashboard_providers.dart';
import '../domain/models.dart' hide LinkedRp;
import 'audit_device_utils.dart';
import 'package:userfront/i18n.dart';
class DashboardScreen extends ConsumerStatefulWidget {
@@ -690,6 +691,9 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
if (userAgent.isEmpty) {
return tr('ui.common.hyphen', fallback: '-');
}
if (userAgent == headlessServerUserAgentSentinel) {
return 'Headless(Server)';
}
final ua = userAgent.toLowerCase();
if (ua.contains('iphone') || ua.contains('ipad') || ua.contains('ipod')) {
return tr('ui.userfront.device.ios', fallback: 'Mobile(iOS)');
@@ -1234,6 +1238,9 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
}
String _sessionBrowserLabel(String userAgent) {
if (userAgent == headlessServerUserAgentSentinel) {
return '';
}
final lower = userAgent.toLowerCase();
if (lower.isEmpty || _looksLikeInternalUserAgent(lower)) {
return '';
@@ -2164,10 +2171,15 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
final authMethod = log.authMethod.isNotEmpty
? log.authMethod
: _authMethodLabel();
final deviceLabel = _deviceLabelFromUserAgent(
log.userAgent,
final preferredUserAgent = preferredAuditLogUserAgent(
log,
);
final deviceLabel = _deviceLabelFromUserAgent(
preferredUserAgent,
);
final browserLabel = _sessionBrowserLabel(
preferredUserAgent,
);
final browserLabel = _sessionBrowserLabel(log.userAgent);
return DataRow(
cells: [
DataCell(
@@ -2387,7 +2399,9 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
tr(
'msg.userfront.audit.device',
params: {
'value': _deviceLabelFromUserAgent(log.userAgent),
'value': _deviceLabelFromUserAgent(
preferredAuditLogUserAgent(log),
),
},
),
),
@@ -2395,9 +2409,14 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
tr(
'msg.userfront.audit.browser',
params: {
'value': _sessionBrowserLabel(log.userAgent).isEmpty
'value':
_sessionBrowserLabel(
preferredAuditLogUserAgent(log),
).isEmpty
? tr('ui.common.hyphen', fallback: '-')
: _sessionBrowserLabel(log.userAgent),
: _sessionBrowserLabel(
preferredAuditLogUserAgent(log),
),
},
),
),