From a99546fe2e618d066f4fba047a5fb170a33c81bd Mon Sep 17 00:00:00 2001 From: Mahmoud Swehli Date: Thu, 24 Aug 2023 15:20:48 +0800 Subject: [PATCH] some updates and improvements --- example/.flutter-plugins-dependencies | 2 +- .../ios/Flutter/flutter_export_environment.sh | 8 +-- example/ios/Runner.xcodeproj/project.pbxproj | 3 +- example/ios/Runner/Info.plist | 2 + example/lib/main.dart | 19 ++++--- example/pubspec.yaml | 2 +- ios/Classes/MsalExtensions.swift | 3 +- ios/Classes/SwiftMsalFlutterPluginV2.swift | 53 +++++++++++++++---- lib/src/exceptions/msal_exception.dart | 3 +- lib/src/exceptions/msal_exceptions.dart | 3 +- .../msal_user_interaction_required.dart | 5 ++ lib/src/models/msal_android_config.dart | 2 - ...msal_public_client_application_config.dart | 8 ++- lib/src/models/msal_result.dart | 12 +++-- lib/src/msal_public_client_application.dart | 6 ++- .../extensions/map_cleanup_extension.dart | 5 +- pubspec.yaml | 2 +- 17 files changed, 89 insertions(+), 49 deletions(-) create mode 100644 lib/src/exceptions/msal_user_interaction_required.dart diff --git a/example/.flutter-plugins-dependencies b/example/.flutter-plugins-dependencies index 3762054..9236f3e 100644 --- a/example/.flutter-plugins-dependencies +++ b/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"msal_flutter","path":"/Users/omar/Documents/GitHub/msal-flutter/","native_build":true,"dependencies":[]}],"android":[{"name":"msal_flutter","path":"/Users/omar/Documents/GitHub/msal-flutter/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"msal_flutter","dependencies":[]}],"date_created":"2022-08-10 09:42:20.983087","version":"3.0.5"} +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"msal_flutter","path":"/Users/mahmoud/projects/50-msal-flutter/","native_build":true,"dependencies":[]}],"android":[{"name":"msal_flutter","path":"/Users/mahmoud/projects/50-msal-flutter/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"msal_flutter","dependencies":[]}],"date_created":"2023-02-25 11:18:14.286829","version":"3.7.5"} \ No newline at end of file diff --git a/example/ios/Flutter/flutter_export_environment.sh b/example/ios/Flutter/flutter_export_environment.sh index 1203220..f55aaad 100755 --- a/example/ios/Flutter/flutter_export_environment.sh +++ b/example/ios/Flutter/flutter_export_environment.sh @@ -1,9 +1,9 @@ #!/bin/sh # This is a generated file; do not edit or check into version control. -export "FLUTTER_ROOT=/Users/omar/Documents/flutter" -export "FLUTTER_APPLICATION_PATH=/Users/omar/Documents/GitHub/msal-flutter/example" +export "FLUTTER_ROOT=/Users/mahmoud/tools/flutter" +export "FLUTTER_APPLICATION_PATH=/Users/mahmoud/projects/50-msal-flutter/example" export "COCOAPODS_PARALLEL_CODE_SIGN=true" -export "FLUTTER_TARGET=/Users/omar/Documents/GitHub/msal-flutter/example/lib/main.dart" +export "FLUTTER_TARGET=/Users/mahmoud/projects/50-msal-flutter/example/lib/main.dart" export "FLUTTER_BUILD_DIR=build" export "FLUTTER_BUILD_NAME=2.0.0.2" export "FLUTTER_BUILD_NUMBER=2.0.0.2" @@ -11,4 +11,4 @@ export "DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==" export "DART_OBFUSCATION=false" export "TRACK_WIDGET_CREATION=true" export "TREE_SHAKE_ICONS=false" -export "PACKAGE_CONFIG=.dart_tool/package_config.json" +export "PACKAGE_CONFIG=/Users/mahmoud/projects/50-msal-flutter/example/.dart_tool/package_config.json" diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 003b0bf..2c2fa09 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -219,6 +219,7 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index 2508686..604a353 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -60,5 +60,7 @@ CADisableMinimumFrameDurationOnPhone + UIApplicationSupportsIndirectInputEvents + diff --git a/example/lib/main.dart b/example/lib/main.dart index 168e8aa..4215d3f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -15,7 +15,8 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { static const String _authority = "https://msalfluttertest.b2clogin.com/tfp/3fab2993-1fec-4a8c-a6d8-2bfea01e64ea/B2C_1_phonesisu"; - static const String _iosRedirectUri = "msauth.com.muljin.msalflutterv2://auth"; + static const String _iosRedirectUri = + "msauth.com.muljin.msalflutterv2://auth"; static const String _androidRedirectUri = "msauth://uk.co.moodio.msal_flutter_example/TvkGQnk1ERb%2Bl9pB4OeyeWrYmqo%3D"; static const String _clientId = "fc6136e7-43d1-489c-b221-630e9e4402d3"; @@ -97,13 +98,12 @@ class _MyAppState extends State { String res = 'res'; try { - final response = await pca!.acquireTokenSilent( - MSALSilentTokenParameters( - scopes: _scopes, - ), - accounts?.isEmpty==true?null: accounts?.first); - res = response?.account.identifier ?? ''; - + final response = await pca!.acquireTokenSilent( + MSALSilentTokenParameters( + scopes: _scopes, + ), + accounts?.isEmpty == true ? null : accounts?.first); + res = response?.account.identifier ?? ''; } on MsalUserCancelledException { res = "User cancelled"; } on MsalNoAccountException { @@ -137,8 +137,7 @@ class _MyAppState extends State { String res; try { if (accounts?.isNotEmpty == true) { - final resp = - await pca!.logout(MSALSignoutParameters(), accounts!.first); + await pca!.logout(MSALSignoutParameters(), accounts!.first); } res = "Account removed"; } on MsalException { diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 22df955..ba4ac15 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -4,7 +4,7 @@ version: 2.0.0-alpha.2 publish_to: 'none' environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.17.0 <3.0.0" dependencies: flutter: diff --git a/ios/Classes/MsalExtensions.swift b/ios/Classes/MsalExtensions.swift index fe928c3..af50b50 100644 --- a/ios/Classes/MsalExtensions.swift +++ b/ios/Classes/MsalExtensions.swift @@ -5,8 +5,7 @@ import Foundation import MSAL extension MSALAccount { - - + var dictionary: [String: Any?] { return ["username": username, "identifier": identifier, diff --git a/ios/Classes/SwiftMsalFlutterPluginV2.swift b/ios/Classes/SwiftMsalFlutterPluginV2.swift index f671118..cc6eb8c 100644 --- a/ios/Classes/SwiftMsalFlutterPluginV2.swift +++ b/ios/Classes/SwiftMsalFlutterPluginV2.swift @@ -25,14 +25,13 @@ public class SwiftMsalFlutterPluginV2: NSObject, FlutterPlugin { public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch (call.method) { - - case "initialize": initialize(result: result, dict: call.arguments as! NSDictionary) - case "initWebViewParams": initWebViewParams(result: result, dict: call.arguments as! NSDictionary) - case "loadAccounts": loadAccounts( result: result, dict: call.arguments as? NSDictionary) - case "acquireToken": acquireToken(result: result, dict: call.arguments as! NSDictionary) - case "acquireTokenSilent": acquireTokenSilent(result: result, dict: call.arguments as! NSDictionary) - case "logout": logout(result: result,dict: call.arguments as! NSDictionary) - default: result(FlutterError(code: "INVALID_METHOD", message: "The method called is invalid", details: nil)) + case "initialize": initialize(result: result, dict: call.arguments as! NSDictionary) + case "initWebViewParams": initWebViewParams(result: result, dict: call.arguments as! NSDictionary) + case "loadAccounts": loadAccounts( result: result, dict: call.arguments as? NSDictionary) + case "acquireToken": acquireToken(result: result, dict: call.arguments as! NSDictionary) + case "acquireTokenSilent": acquireTokenSilent(result: result, dict: call.arguments as! NSDictionary) + case "logout": logout(result: result,dict: call.arguments as! NSDictionary) + default: result(FlutterError(code: "INVALID_METHOD", message: "The method called is invalid", details: nil)) } } @@ -51,7 +50,6 @@ public class SwiftMsalFlutterPluginV2: NSObject, FlutterPlugin { do { let config: MSALPublicClientApplicationConfig = try MSALPublicClientApplicationConfig.fromDict(dictionary: dict) let application = try MSALPublicClientApplication(configuration: config) -// 'validateAuthority' is deprecated: Use knowAuthorities in MSALPublicClientApplicationConfig instead applicationContext = application result(true) return @@ -93,7 +91,7 @@ public class SwiftMsalFlutterPluginV2: NSObject, FlutterPlugin { let parameters = MSALInteractiveTokenParameters.fromDict(dict: dict, param: webViewParameters) applicationContext.acquireToken(with: parameters) { (token, error) in if let error = error { - result(FlutterError(code: "AUTH_ERROR", message: "Could not acquire token: \(error)", details: error.localizedDescription)) + result(FlutterError(code: self.getErrorCode(error:error), message: "Could not acquire token: \(error)", details: error.localizedDescription)) return } guard let tokenResult: MSALResult = token else { @@ -133,7 +131,7 @@ public class SwiftMsalFlutterPluginV2: NSObject, FlutterPlugin { let silentParameters = MSALSilentTokenParameters.fromDict(dict: dict["tokenParameters"] as! NSDictionary, account: account) self.applicationContext!.acquireTokenSilent(with: silentParameters, completionBlock: { (tokenResult, error) in guard let authResult = tokenResult, error == nil else { - result(FlutterError(code: "AUTH_ERROR", message: "Authentication error \(String(describing: error))", details: error?.localizedDescription)) + result(FlutterError(code: self.getErrorCode(error:error), message: "Authentication error \(String(describing: error))", details: error?.localizedDescription)) return } result(authResult.toDict()) @@ -208,4 +206,37 @@ public class SwiftMsalFlutterPluginV2: NSObject, FlutterPlugin { throw error } } + + private func getErrorCode(error: Error?) -> String + { + guard let error = error as NSError? else { return "AUTH_ERROR"; } + + if error.domain == MSALErrorDomain, let errorCode = MSALError(rawValue: error.code) + { + switch errorCode + { + case .interactionRequired: + return "INTERACTION_REQUIRED" + case .serverDeclinedScopes: + return "SERVER_DECLINED_SCOPES" + case .serverProtectionPoliciesRequired: + return "SERVER_PROTECTION_POLICIES_REQUIRED" + case .userCanceled: + return "CANCELLED" + case .internal: + return "INTERNAL_ERROR" + default: + return "AUTH_ERROR" + } + } + + // Handle no internet connection. + if error.domain == NSURLErrorDomain && error.code == NSURLErrorNotConnectedToInternet + { + return "CONNECTION_ERROR" + } + + return "AUTH_ERROR" + + } } diff --git a/lib/src/exceptions/msal_exception.dart b/lib/src/exceptions/msal_exception.dart index 8069580..a37779d 100644 --- a/lib/src/exceptions/msal_exception.dart +++ b/lib/src/exceptions/msal_exception.dart @@ -1,4 +1,5 @@ class MsalException implements Exception { String errorMessage; - MsalException(this.errorMessage); + String? errorDetails; + MsalException(this.errorMessage, {this.errorDetails}); } diff --git a/lib/src/exceptions/msal_exceptions.dart b/lib/src/exceptions/msal_exceptions.dart index 6699689..83cf45d 100644 --- a/lib/src/exceptions/msal_exceptions.dart +++ b/lib/src/exceptions/msal_exceptions.dart @@ -5,6 +5,7 @@ export 'msal_invalid_configuration_exception.dart'; export 'msal_invalid_scope_exception.dart'; export 'msal_no_account_exception.dart'; export 'msal_uninitialized_exception.dart'; -export 'msal_user_cancelled_exception.dart'; export 'msal_invalid_grant_exception.dart'; export 'msal_invalid_request_exception.dart'; +export 'msal_user_cancelled_exception.dart'; +export 'msal_user_interaction_required.dart'; diff --git a/lib/src/exceptions/msal_user_interaction_required.dart b/lib/src/exceptions/msal_user_interaction_required.dart new file mode 100644 index 0000000..f7efa1c --- /dev/null +++ b/lib/src/exceptions/msal_user_interaction_required.dart @@ -0,0 +1,5 @@ +import 'msal_exceptions.dart'; + +class MsalUserInteractionRequired extends MsalException { + MsalUserInteractionRequired() : super("User interaction required to login."); +} diff --git a/lib/src/models/msal_android_config.dart b/lib/src/models/msal_android_config.dart index bc8debd..c7decac 100644 --- a/lib/src/models/msal_android_config.dart +++ b/lib/src/models/msal_android_config.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:msal_flutter/src/models/android_account_mode.dart'; import 'package:msal_flutter/src/models/android_logger_config.dart'; import 'package:msal_flutter/src/models/authorization_agent.dart'; diff --git a/lib/src/models/msal_public_client_application_config.dart b/lib/src/models/msal_public_client_application_config.dart index f50748b..44a791b 100644 --- a/lib/src/models/msal_public_client_application_config.dart +++ b/lib/src/models/msal_public_client_application_config.dart @@ -1,4 +1,3 @@ -import 'dart:developer'; import 'dart:io'; import 'package:msal_flutter/src/models/authority.dart'; @@ -38,7 +37,7 @@ class MSALPublicClientApplicationConfig { this.sliceConfig, this.tokenExpirationBuffer, this.androidConfig, - }){ + }) { if (Platform.isAndroid) { redirectUri = androidRedirectUri; } else { @@ -49,13 +48,12 @@ class MSALPublicClientApplicationConfig { Map _toMapAndroid() { return { 'client_id': clientId, - 'redirect_uri' : redirectUri, + 'redirect_uri': redirectUri, 'client_capabilities': clientApplicationCapabilities, ...androidConfig?.toMap() ?? {}, }.cleanup(); } - Map _toMapIos() { return { 'clientId': clientId, @@ -69,9 +67,9 @@ class MSALPublicClientApplicationConfig { 'multipleCloudsSupported': multipleCloudsSupported, 'sliceConfig': sliceConfig?.toMap(), 'tokenExpirationBuffer': tokenExpirationBuffer, - }.cleanup(); } + Map toMap() { if (Platform.isAndroid) { return _toMapAndroid(); diff --git a/lib/src/models/msal_result.dart b/lib/src/models/msal_result.dart index 8c6a6a0..1f9dc0b 100644 --- a/lib/src/models/msal_result.dart +++ b/lib/src/models/msal_result.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - import 'package:msal_flutter/src/models/msal_account.dart'; import 'msal_tenant_profile.dart'; @@ -33,7 +31,8 @@ class MSALResult { MSALResult.fromMap(Map map) : this( accessToken: map['accessToken'] ?? '', - account: MSALAccount.fromMap(Map.from(map['account'])), + account: + MSALAccount.fromMap(Map.from(map['account'])), authenticationScheme: map['authenticationScheme'] ?? '', authority: Uri.parse(map['authority']), authorizationHeader: map['authorizationHeader'] ?? '', @@ -43,7 +42,10 @@ class MSALResult { : null, extendedLifeTimeToken: map['extendedLifeTimeToken'], idToken: map['idToken'], - scopes: List.from(map['scopes']??[]), - tenantProfile:map['tenantProfile']==null?null: MSALTenantProfile.fromMap(Map.from(map['tenantProfile'])), + scopes: List.from(map['scopes'] ?? []), + tenantProfile: map['tenantProfile'] == null + ? null + : MSALTenantProfile.fromMap( + Map.from(map['tenantProfile'])), ); } diff --git a/lib/src/msal_public_client_application.dart b/lib/src/msal_public_client_application.dart index be21587..3f2c99f 100644 --- a/lib/src/msal_public_client_application.dart +++ b/lib/src/msal_public_client_application.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:flutter/services.dart'; +import 'package:msal_flutter/src/exceptions/msal_user_interaction_required.dart'; import '../msal_flutter.dart'; import 'exceptions/msal_scope_error_exception.dart'; @@ -109,6 +110,8 @@ class MSALPublicClientApplication { return MsalNoAccountException(); case "NO_CLIENTID": return MsalInvalidConfigurationException("Client Id not set"); + case "INTERACTION_REQUIRED": + return MsalUserInteractionRequired(); case "INVALID_AUTHORITY": return MsalInvalidConfigurationException("Invalid authroity set."); case "INVALID_GRANT": @@ -125,11 +128,12 @@ class MSALPublicClientApplication { case "INIT_ERROR": return MsalInitializationException(); case "SCOPE_ERROR": + case "SERVER_DECLINED_SCOPES": return MsalScopeErrorException(); case "AUTH_ERROR": case "UNKNOWN": default: - return MsalException("Authentication error"); + return MsalException("Authentication error", errorDetails: e.message); } } } diff --git a/lib/src/utility/extensions/map_cleanup_extension.dart b/lib/src/utility/extensions/map_cleanup_extension.dart index 49e24ca..4fcfa3d 100644 --- a/lib/src/utility/extensions/map_cleanup_extension.dart +++ b/lib/src/utility/extensions/map_cleanup_extension.dart @@ -2,9 +2,8 @@ extension MapCleanup on Map { Map cleanup() { removeWhere((key, value) { - if (value is List ) { - print(key); - if(value.isEmpty) { + if (value is List) { + if (value.isEmpty) { return true; } return false; diff --git a/pubspec.yaml b/pubspec.yaml index b54b5f6..1963793 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: msal_flutter description: A Microsoft Authentication Library wrapper for Android and iOS -version: 3.0.0-alpha1 +version: 3.0.0-beta1 homepage: https://github.com/muljin/msal-flutter repository: https://github.com/muljin/msal-flutter environment: