From eb2b930a2a8c89b8af7a5d0be706daf14dfdd03a Mon Sep 17 00:00:00 2001 From: Volkram Weber Date: Fri, 3 Jan 2025 10:55:58 +0100 Subject: [PATCH] 210: add link to 2FA if not logged in, improve delete authenticator info text --- lib/app/router.dart | 6 +- lib/app/widgets/bottom_navigation.dart | 3 +- lib/features/login/widgets/welcome_view.dart | 142 +++++++++--------- lib/features/mfa/widgets/ready_view.dart | 2 +- lib/i18n/app_de.json | 2 +- .../lib/src/utils/crypto_utils.dart | 1 + 6 files changed, 77 insertions(+), 79 deletions(-) diff --git a/lib/app/router.dart b/lib/app/router.dart index 15f3afd3..bcc594be 100644 --- a/lib/app/router.dart +++ b/lib/app/router.dart @@ -21,7 +21,11 @@ GoRouter createAppRouter(BuildContext context) { final authBloc = context.read(); final isLoggedIn = !Config.useLogin || authBloc.state is Authenticated; final isLoggingIn = state.uri.toString() == Routes.login.path; - final isMfa = state.uri.toString() == Routes.mfa.path; + final isMfa = [ + Routes.mfa.path, + '${Routes.mfa.path}/${Routes.mfaTokenInput.path}', + '${Routes.mfa.path}/${Routes.mfaTokenScan.path}', + ].contains(state.uri.toString()); if (!isLoggedIn && !isLoggingIn && !isMfa) return Routes.login.path; if (isLoggedIn && isLoggingIn) return Routes.news.path; diff --git a/lib/app/widgets/bottom_navigation.dart b/lib/app/widgets/bottom_navigation.dart index f8b619b5..bcf349a6 100644 --- a/lib/app/widgets/bottom_navigation.dart +++ b/lib/app/widgets/bottom_navigation.dart @@ -12,9 +12,10 @@ class BottomNavigation extends StatelessWidget { final route = GoRouter.of(context).routerDelegate.currentConfiguration.matches[0].route as GoRoute; final currentRoute = route.path; final currentRouteIndex = bottomNavigationItems.indexWhere((item) => item.route == currentRoute); + final bottomNavVisible = currentRouteIndex >= 0; final theme = Theme.of(context); - return currentRouteIndex >= 0 + return bottomNavVisible ? SizedBox( height: 64.0, child: BottomNavigationBar( diff --git a/lib/features/login/widgets/welcome_view.dart b/lib/features/login/widgets/welcome_view.dart index 2060492f..634fc319 100644 --- a/lib/features/login/widgets/welcome_view.dart +++ b/lib/features/login/widgets/welcome_view.dart @@ -7,8 +7,6 @@ import 'package:gruene_app/app/constants/routes.dart'; import 'package:gruene_app/app/constants/urls.dart'; import 'package:gruene_app/app/theme/theme.dart'; import 'package:gruene_app/app/utils/open_url.dart'; -import 'package:gruene_app/features/mfa/bloc/mfa_bloc.dart'; -import 'package:gruene_app/features/mfa/bloc/mfa_state.dart'; import 'package:gruene_app/i18n/translations.g.dart'; class WelcomeView extends StatelessWidget { @@ -17,86 +15,80 @@ class WelcomeView extends StatelessWidget { @override Widget build(BuildContext context) { final theme = Theme.of(context); - return BlocBuilder( - builder: (context, state) { - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Spacer(), - SizedBox( - height: 256, - child: SvgPicture.asset('assets/graphics/login.svg'), + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Spacer(), + SizedBox( + height: 256, + child: SvgPicture.asset('assets/graphics/login.svg'), + ), + Center( + child: Text(t.login.welcome, style: theme.textTheme.displayLarge?.apply(color: ThemeColors.text)), + ), + Spacer(), + Container( + padding: const EdgeInsets.symmetric(horizontal: 24), + height: 64, + child: FilledButton( + onPressed: () => context.read().add(SignInRequested()), + child: Text( + t.login.loginMembers, + style: theme.textTheme.titleMedium?.apply(color: theme.colorScheme.surface), ), - Center( - child: Text(t.login.welcome, style: theme.textTheme.displayLarge?.apply(color: ThemeColors.text)), + ), + ), + // TODO #203: Uncomment for guest login + // https://github.com/verdigado/gruene_app/issues/203 + // Container( + // padding: const EdgeInsets.symmetric(horizontal: 24), + // child: OutlinedButton( + // onPressed: () => {}, + // child: Text( + // t.login.loginNonMembers, + // style: theme.textTheme.titleMedium?.apply(color: theme.colorScheme.tertiary), + // ), + // ), + // ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: OutlinedButton( + onPressed: () => { + context.push( + Routes.mfa.path, + ), + }, + child: Text( + t.login.mfa, + style: theme.textTheme.titleMedium?.apply(color: theme.colorScheme.tertiary), ), - Spacer(), - Container( - padding: const EdgeInsets.symmetric(horizontal: 24), - height: 64, - child: FilledButton( - onPressed: () => context.read().add(SignInRequested()), - child: Text( - t.login.loginMembers, - style: theme.textTheme.titleMedium?.apply(color: theme.colorScheme.surface), - ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + onPressed: () => openUrl(dataProtectionStatementUrl, context), + child: Text( + t.login.dataProtection, + style: theme.textTheme.labelSmall, ), ), - // TODO #203: Uncomment for guest login - // https://github.com/verdigado/gruene_app/issues/203 - // Container( - // padding: const EdgeInsets.symmetric(horizontal: 24), - // child: OutlinedButton( - // onPressed: () => {}, - // child: Text( - // t.login.loginNonMembers, - // style: theme.textTheme.titleMedium?.apply(color: theme.colorScheme.tertiary), - // ), - // ), - // ), - if (state.status == MfaStatus.ready) ...[ - Container( - padding: const EdgeInsets.symmetric(horizontal: 24), - child: OutlinedButton( - onPressed: () => { - context.push( - Routes.mfa.path, - ), - }, - child: Text( - t.login.mfa, - style: theme.textTheme.titleMedium?.apply(color: theme.colorScheme.tertiary), - ), - ), + Container( + width: 4, + height: 4, + decoration: BoxDecoration(color: theme.primaryColor, shape: BoxShape.circle), + ), + TextButton( + onPressed: () => openUrl(legalNoticeUrl, context), + child: Text( + t.login.legalNotice, + style: theme.textTheme.labelSmall, ), - ], - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextButton( - onPressed: () => openUrl(dataProtectionStatementUrl, context), - child: Text( - t.login.dataProtection, - style: theme.textTheme.labelSmall, - ), - ), - Container( - width: 4, - height: 4, - decoration: BoxDecoration(color: theme.primaryColor, shape: BoxShape.circle), - ), - TextButton( - onPressed: () => openUrl(legalNoticeUrl, context), - child: Text( - t.login.legalNotice, - style: theme.textTheme.labelSmall, - ), - ), - ], ), ], - ); - }, + ), + ], ); } } diff --git a/lib/features/mfa/widgets/ready_view.dart b/lib/features/mfa/widgets/ready_view.dart index 27cbe8c5..3ee40bc9 100644 --- a/lib/features/mfa/widgets/ready_view.dart +++ b/lib/features/mfa/widgets/ready_view.dart @@ -30,7 +30,7 @@ class _ReadyViewState extends State { } void _startPeriodicRefresh() { - _timer = Timer.periodic(Duration(seconds: 10), (timer) { + _timer = Timer.periodic(Duration(seconds: 5), (timer) { context.read().add(RefreshMfa()); }); } diff --git a/lib/i18n/app_de.json b/lib/i18n/app_de.json index 2f82a328..6b0ca1d5 100644 --- a/lib/i18n/app_de.json +++ b/lib/i18n/app_de.json @@ -160,7 +160,7 @@ "betaVersion": "Diese Funktion befindet sich noch in der Entwicklung. Wir bitten um Verständnis.", "delete": { "title": "App als zweiten Faktor deaktivieren", - "text": "Bitte entferne die App in deiner Account Console bevor du sie hier als zweiten Faktor deaktivierst.", + "text": "Bitte entferne die App in deiner Account Console bevor du sie hier als zweiten Faktor deaktivierst. Sollte diese App dein einziger konfigurierter zweiter Faktor sein, wirst du dich ansonsten nach dem Deaktivieren nicht mehr einloggen können.", "submit": "Jetzt deaktivieren" } }, diff --git a/packages/keycloak_authenticator/lib/src/utils/crypto_utils.dart b/packages/keycloak_authenticator/lib/src/utils/crypto_utils.dart index ea826505..ecfc5795 100644 --- a/packages/keycloak_authenticator/lib/src/utils/crypto_utils.dart +++ b/packages/keycloak_authenticator/lib/src/utils/crypto_utils.dart @@ -1,6 +1,7 @@ import 'package:pointycastle/export.dart'; import "package:pointycastle/pointycastle.dart"; import 'package:pointycastle/ecc/ecc_fp.dart' as ecc_fp; +// ignore: implementation_imports import 'package:pointycastle/src/utils.dart'; import 'package:pointycastle/asn1/object_identifiers.dart'; import 'dart:math';