6177214e-ce7c-49e3-99de-ff9721b26f63 — Commit 2bbd7e7e

AuthorEdmir Suljic<esu@dwarf.dk>
Date2025-09-02 15:37:44 +0200
Moved stuff out from login cubit to auth repo and updated interceptor and app router

Changed files

comwell_key_app/ios/Runner/AdyenLocalization.swift | 17 ++++++
 comwell_key_app/ios/Runner/AdyenOverrides.strings  |  9 ++++
 .../authentication/authentication_repository.dart  | 62 ++++++++++++++++++++--
 comwell_key_app/lib/login/cubit/login_cubit.dart   | 42 ++-------------
 comwell_key_app/lib/main.dart                      |  6 ++-
 comwell_key_app/lib/routing/app_router.dart        |  7 +--
 .../interceptors/response_handle_interceptor.dart  | 12 ++---
 7 files changed, 102 insertions(+), 53 deletions(-)

Diff

diff --git a/comwell_key_app/ios/Runner/AdyenLocalization.swift b/comwell_key_app/ios/Runner/AdyenLocalization.swift
new file mode 100644
index 00000000..1fc365dd
--- /dev/null
+++ b/comwell_key_app/ios/Runner/AdyenLocalization.swift
@@ -0,0 +1,17 @@
+import Foundation
+import Adyen
+import AdyenComponents
+import Flutter
+
+/// Simple helper you can call from Dart via a MethodChannel if you want.
+/// Or, if you fork `adyen_checkout`, set this when building the CardComponent.
+class AdyenLocalizationHelper {
+ static func apply() {
+ // Nothing global to set; you’ll inject these parameters when you create the component.
+ }
+
+ static func localizationParameters() -> LocalizationParameters {
+ // Use your app bundle and the table you created.
+ return LocalizationParameters(tableName: "AdyenOverrides", keySeparator: nil, bundle: .main)
+ }
+}
diff --git a/comwell_key_app/ios/Runner/AdyenOverrides.strings b/comwell_key_app/ios/Runner/AdyenOverrides.strings
new file mode 100644
index 00000000..55534468
--- /dev/null
+++ b/comwell_key_app/ios/Runner/AdyenOverrides.strings
@@ -0,0 +1,9 @@
+//
+// Localizable.strings
+// Runner
+//
+// Created by Edmir Suljic on 02/09/2025.
+//
+
+/* Submit button shown for zero-amount (preauthorisation) */
+"adyen.confirmPreauthorization" = "Save card";
diff --git a/comwell_key_app/lib/authentication/authentication_repository.dart b/comwell_key_app/lib/authentication/authentication_repository.dart
index df66444e..d2776d55 100644
--- a/comwell_key_app/lib/authentication/authentication_repository.dart
+++ b/comwell_key_app/lib/authentication/authentication_repository.dart
@@ -8,6 +8,9 @@ import 'package:comwell_key_app/utils/secure_storage.dart';
import 'package:comwell_key_app/common/const.dart' as constants;
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:flutter/foundation.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_dotenv/flutter_dotenv.dart';
+import 'package:msal_auth/msal_auth.dart';
import '../utils/locator.dart';
import '../utils/seos_repository.dart';
@@ -20,6 +23,38 @@ class AuthenticationRepository {
final seos = locator<SeosRepository>();
final database = locator<ComwellDatabase>();
final api = Api();
+ late final MultipleAccountPca msAuth;
+ final scopes = [
+ 'api://19a8eb05-01e0-4076-9db3-34bcfefd67d8/Apim.Access'
+ ];
+
+ Future<void> init() async {
+ final clientId = dotenv.env["ENTRA_ID_CLIENT_ID"]!;
+ final redirect = dotenv
+ .env["ENTRA_ID_REDIRECT_URL"]!; // should probably be an env variable
+
+ final String configFilePath;
+ switch (appFlavor) {
+ case "Develop":
+ configFilePath = 'assets/msal/msal_config_dev.json';
+ case "Stage":
+ configFilePath = 'assets/msal/msal_config_stage.json';
+ case "Prod":
+ configFilePath = 'assets/msal/msal_config_prod.json';
+ default:
+ throw Exception("Missing config file for flavor $appFlavor");
+ }
+ msAuth = await MultipleAccountPca.create(
+ clientId: clientId,
+ androidConfig: AndroidConfig(
+ configFilePath: configFilePath,
+ redirectUri: redirect,
+ ),
+ appleConfig: AppleConfig(
+ authorityType: AuthorityType.aad,
+ broker: Broker.webView,
+ ));
+ }
AuthenticationRepository() {
broadcast.listen((status) {
@@ -86,14 +121,35 @@ class AuthenticationRepository {
void dispose() => _controller.close();
+ Future<void> openAuth(Prompt prompt) async {
+ try {
+ final token = await msAuth.acquireToken(scopes: scopes, prompt: prompt);
+ print('token.accessToken ${token}');
+ await loginWithCode(token.accessToken);
+ } catch (e) {
+ final accounts = await msAuth.getAccounts();
+ if (accounts.isNotEmpty) {
+ print("qqq accounts=${accounts.map((e) => e.id).join(", ")}");
+ }
+ print("qqq e=$e");
+ }
+ }
+
Future<bool> doesTokenExist() async {
- final refreshToken = await secureStorage.read(constants.refreshToken);
- final accessToken = await secureStorage.read(constants.accessToken);
- return accessToken != null;
+ try {
+ await msAuth.acquireTokenSilent(scopes: scopes);
+ return true;
+ } catch (e) {
+ return false;
+ }
}
Future<void> loginWithCode(String code) async {
await secureStorage.write(constants.accessToken, code);
await logIn();
}
+
+ Future<String> get accessToken async {
+ return (await msAuth.acquireTokenSilent(scopes: scopes)).accessToken;
+ }
}
diff --git a/comwell_key_app/lib/login/cubit/login_cubit.dart b/comwell_key_app/lib/login/cubit/login_cubit.dart
index c9062420..6d6988dc 100644
--- a/comwell_key_app/lib/login/cubit/login_cubit.dart
+++ b/comwell_key_app/lib/login/cubit/login_cubit.dart
@@ -1,8 +1,6 @@
import 'package:bloc/bloc.dart';
import 'package:comwell_key_app/authentication/authentication_repository.dart';
import 'package:equatable/equatable.dart';
-import 'package:flutter/services.dart';
-import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:msal_auth/msal_auth.dart';
part 'login_state.dart';
@@ -14,46 +12,12 @@ class LoginCubit extends Cubit<LoginState> {
final AuthenticationRepository authRepository;
Future<void> login() async {
- await _openAuth(Prompt.login);
+ await authRepository.openAuth(Prompt.login);
}
Future<void> createAccount() async {
- await _openAuth(Prompt.create);
+ await authRepository.openAuth(Prompt.create);
}
- Future<void> _openAuth(Prompt promt) async {
- final clientId = dotenv.env["ENTRA_ID_CLIENT_ID"]!;
- final redirect = dotenv
- .env["ENTRA_ID_REDIRECT_URL"]!; // should probably be an env variable
-
- final String configFilePath;
- switch (appFlavor) {
- case "Develop":
- configFilePath = 'assets/msal/msal_config_dev.json';
- case "Stage":
- configFilePath = 'assets/msal/msal_config_stage.json';
- case "Prod":
- configFilePath = 'assets/msal/msal_config_prod.json';
- default:
- throw Exception("Missing config file for flavor $appFlavor");
- }
-
- try {
- final msalAuth = await MultipleAccountPca.create(
- clientId: clientId,
- androidConfig: AndroidConfig(
- configFilePath: configFilePath,
- redirectUri: redirect,
- ),
- appleConfig: AppleConfig(
- authorityType: AuthorityType.aad,
- broker: Broker.webView,
- ));
- final token = await msalAuth
- .acquireToken(scopes: ["api://19a8eb05-01e0-4076-9db3-34bcfefd67d8/Apim.Access"], prompt: promt);
- await authRepository.loginWithCode(token.accessToken);
- } catch (e) {
- print("qqq e=$e");
- }
- }
+
}
diff --git a/comwell_key_app/lib/main.dart b/comwell_key_app/lib/main.dart
index f7f70d74..9c6318d8 100644
--- a/comwell_key_app/lib/main.dart
+++ b/comwell_key_app/lib/main.dart
@@ -1,3 +1,4 @@
+import 'package:comwell_key_app/authentication/authentication_repository.dart';
import 'package:comwell_key_app/utils/firebase.dart';
import 'package:comwell_key_app/utils/locator.dart';
import 'package:easy_localization/easy_localization.dart';
@@ -12,7 +13,6 @@ import 'firebase_options_stage.dart' as fb_stage;
import 'firebase_options_prod.dart' as fb_prod;
void main() async {
-
if (appFlavor == 'Develop') {
runMainApp(fb_dev.DefaultFirebaseOptions.currentPlatform, '.env.dev');
} else if (appFlavor == 'Stage') {
@@ -42,7 +42,8 @@ void runMainApp(FirebaseOptions firebaseOptions, String envFile) async {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
setupLocator();
await SentryFlutter.init((options) {
- options.dsn = dotenv.env['SENTRY_DSN']!; // Add your Sentry DSN to your .env file.
+ options.dsn =
+ dotenv.env['SENTRY_DSN']!; // Add your Sentry DSN to your .env file.
// Set tracesSampleRate to 1.0 to capture 100% of transactions for tracing.
// We recommend adjusting this value in production.
options.tracesSampleRate = 1.0;
@@ -51,6 +52,7 @@ void runMainApp(FirebaseOptions firebaseOptions, String envFile) async {
options.profilesSampleRate = 1.0;
options.environment = appFlavor;
});
+ await locator<AuthenticationRepository>().init();
//Setting SysemUIOverlay
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
diff --git a/comwell_key_app/lib/routing/app_router.dart b/comwell_key_app/lib/routing/app_router.dart
index 63c293d2..a5aafbd0 100644
--- a/comwell_key_app/lib/routing/app_router.dart
+++ b/comwell_key_app/lib/routing/app_router.dart
@@ -109,7 +109,7 @@ GoRouter goRouter() {
if (status == AuthenticationStatus.unknown) {
bool doesTokenExist = await authRepo.doesTokenExist();
if (doesTokenExist) {
- authRepo.logIn();
+ //authRepo.logIn();
}
}
@@ -119,9 +119,10 @@ GoRouter goRouter() {
return "/login?forced=$forced";
}
- // Redirect to the booking_details page if the user is authenticated
+ // Redirect to the overview page if the user is authenticated
else if (isAuthenticated && state.matchedLocation.contains("/login")) {
- return "/oauthredirect";
+ print("qqq redirecting to overview");
+ return "/${AppRoutes.overview.name}";
}
return null;
},
diff --git a/comwell_key_app/lib/services/interceptors/response_handle_interceptor.dart b/comwell_key_app/lib/services/interceptors/response_handle_interceptor.dart
index 1394b94e..976a222d 100644
--- a/comwell_key_app/lib/services/interceptors/response_handle_interceptor.dart
+++ b/comwell_key_app/lib/services/interceptors/response_handle_interceptor.dart
@@ -7,6 +7,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:comwell_key_app/common/const.dart' as constants;
import 'package:go_router/go_router.dart';
import 'package:flutter/material.dart';
+import 'package:msal_auth/msal_auth.dart';
class ResponseHandleInterceptor extends Interceptor {
final Dio _dio;
@@ -27,10 +28,8 @@ class ResponseHandleInterceptor extends Interceptor {
final String? accessToken =
await _secureStorageService.read(key: constants.accessToken);
- final String? refreshToken =
- await _secureStorageService.read(key: constants.refreshToken);
- if (accessToken == null || refreshToken == null) {
+ if (accessToken == null) {
//logout user
options.extra["tokenErrorType"] = TokenErrorType.tokenNotFound;
final error = DioException(
@@ -71,9 +70,10 @@ class ResponseHandleInterceptor extends Interceptor {
break;
case 401:
retryCount++;
+ //await _authenticationRepository.openAuth(Prompt.login);
if (retryCount < 10) {
- final newToken = await _refreshToken();
- if (newToken != null) {
+ final newToken = await _authenticationRepository.accessToken;
+ if (newToken.isNotEmpty) {
// Retry the original request with the new token
final options = response.requestOptions;
options.headers['Authorization'] = newToken;
@@ -139,7 +139,7 @@ class ResponseHandleInterceptor extends Interceptor {
Future<String?> _refreshToken() async {
final String? refreshToken =
- await _secureStorageService.read(key: constants.refreshToken);
+ await _secureStorageService.read(key: constants.accessToken);
if (refreshToken == null) {
return null;