diff --git a/example/ios/.swiftlint.yml b/example/ios/.swiftlint.yml index e36d81bd..39c7f2d1 100644 --- a/example/ios/.swiftlint.yml +++ b/example/ios/.swiftlint.yml @@ -14,7 +14,7 @@ excluded: # paths to ignore during linting. Takes precedence over `included`. - .swiftlint.yml - OneginiTests - OneginiUITests - - .symlinks/plugins/onegini/ios/Classes/Pigeon.swift + - .symlinks/plugins/onegini/ios/Classes/Pigeon.gen.swift # If true, SwiftLint will not fail if no lintable files are found. allow_zero_lintable_files: false diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 7d71c831..97d8d58e 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -82,7 +82,7 @@ SPEC CHECKSUMS: SwiftLint: 1b7561918a19e23bfed960e40759086e70f4dba5 Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 Typhoon: 1973c93ecfb3edb963d78b10e715bc2911475bd2 - url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de + url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4 PODFILE CHECKSUM: e49c186fd5db1b7547d2a80e7096f4e0713e90f9 diff --git a/example/lib/screens/user_screen.dart b/example/lib/screens/user_screen.dart index 7750e2dd..e2113a22 100644 --- a/example/lib/screens/user_screen.dart +++ b/example/lib/screens/user_screen.dart @@ -151,7 +151,7 @@ class _UserScreenState extends State with RouteAware { }); } - Widget biometricAuthenticatorWidget() { + Widget _buildBiometricAuthenticatorWidget() { final authenticator = _biometricAuthenticator; if (authenticator != null) { return ListTile( @@ -177,32 +177,38 @@ class _UserScreenState extends State with RouteAware { return SizedBox.shrink(); } - Widget preferredAuthenticatorSelectorWidget() { + Widget _buildPreferredAuthenticatorWidget() { final biometricAuthenticator = _biometricAuthenticator; - return PopupMenuButton( - child: ListTile( - title: Text("set preferred authenticator"), - leading: Icon(Icons.add_to_home_screen), - ), - onSelected: (value) { - Onegini.instance.userClient - .setPreferredAuthenticator(value) - .whenComplete(() => getAuthenticators()); - }, - itemBuilder: (context) { - return [ - PopupMenuItem( - child: Text("Pin"), - value: OWAuthenticatorType.pin, - ), - if (biometricAuthenticator != null && - biometricAuthenticator.isRegistered) + return Column(mainAxisSize: MainAxisSize.min, children: [ + ListTile( + title: + Text("Preferred Authenticator: ${_preferredAuthenticator?.name} "), + ), + PopupMenuButton( + child: ListTile( + title: Text("set preferred authenticator"), + leading: Icon(Icons.add_to_home_screen), + ), + onSelected: (value) { + Onegini.instance.userClient + .setPreferredAuthenticator(value) + .whenComplete(() => getAuthenticators()); + }, + itemBuilder: (context) { + return [ PopupMenuItem( - child: Text(biometricAuthenticator.name), - value: OWAuthenticatorType.biometric, + child: Text("Pin"), + value: OWAuthenticatorType.pin, ), - ]; - }); + if (biometricAuthenticator != null && + biometricAuthenticator.isRegistered) + PopupMenuItem( + child: Text(biometricAuthenticator.name), + value: OWAuthenticatorType.biometric, + ), + ]; + }) + ]); } @override @@ -239,12 +245,8 @@ class _UserScreenState extends State with RouteAware { title: Text("Pin"), leading: Switch(value: true, onChanged: null), ), - biometricAuthenticatorWidget(), - ListTile( - title: Text( - "Preferred Authenticator: ${_preferredAuthenticator?.name} "), - ), - preferredAuthenticatorSelectorWidget(), + _buildBiometricAuthenticatorWidget(), + _buildPreferredAuthenticatorWidget(), Divider(), ListTile( title: Text("Change pin"), @@ -422,29 +424,102 @@ class Info extends StatefulWidget { } class _InfoState extends State { - Future getApplicationDetails() async { + Future _getApplicationDetails() async { await Onegini.instance.userClient .authenticateDevice(["read", "write", "application-details"]); - var response = await Onegini.instance.resourcesMethods.requestResource( + final response = await Onegini.instance.resourcesMethods.requestResource( ResourceRequestType.anonymous, RequestDetails( path: "application-details", method: HttpRequestMethod.get)); return applicationDetailsFromJson(response.body); } - Future getClientResource() async { - var response = await Onegini.instance.resourcesMethods + Future _getClientResource() async { + final response = await Onegini.instance.resourcesMethods .requestResourceAuthenticated( - RequestDetails(path: "devices", method: HttpRequestMethod.get)) - // Will be fixed in FP-51 - // ignore: body_might_complete_normally_catch_error - .catchError((error) { - print('Caught error: $error'); + RequestDetails(path: "devices", method: HttpRequestMethod.get)); + return clientResourceFromJson(response.body); + } - showFlutterToast(error.message); - }); + FutureBuilder _buildDeviceInfoList() { + return FutureBuilder( + future: _getClientResource(), + builder: (context, snapshot) { + final snapshotData = snapshot.data; + return snapshotData != null + ? ListView.builder( + itemCount: snapshotData.devices.length, + itemBuilder: (BuildContext context, int index) { + return ExpansionTile( + title: Text(snapshotData.devices[index].name), + expandedCrossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListTile( + title: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Id => ${snapshotData.devices[index].id}", + style: TextStyle(fontSize: 15), + ), + SizedBox(height: 10), + Text( + "Application => ${snapshotData.devices[index].application}", + style: TextStyle(fontSize: 15), + ), + SizedBox(height: 10), + Text( + "Mobile authentication enabled => ${snapshotData.devices[index].mobileAuthenticationEnabled.toString()}", + style: TextStyle(fontSize: 15), + ), + SizedBox(height: 10), + Text( + "Platform => ${snapshotData.devices[index].platform}", + style: TextStyle(fontSize: 15), + ), + SizedBox(), + ], + )) + ], + ); + }, + ) + : Center( + child: SizedBox( + child: CircularProgressIndicator(), + ), + ); + }, + ); + } - return clientResourceFromJson(response.body); + FutureBuilder _buildApplicationDetails() { + return FutureBuilder( + future: _getApplicationDetails(), + builder: (context, snapshot) { + return snapshot.hasData + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "application identifier => ${snapshot.data?.applicationIdentifier}", + style: TextStyle(fontSize: 15), + ), + SizedBox(height: 10), + Text( + "application platform => ${snapshot.data?.applicationPlatform}", + style: TextStyle(fontSize: 15), + ), + SizedBox(height: 10), + Text( + "application version => ${snapshot.data?.applicationVersion}", + style: TextStyle(fontSize: 15), + ), + ], + ) + : CircularProgressIndicator(); + }, + ); } @override @@ -455,118 +530,11 @@ class _InfoState extends State { margin: EdgeInsets.all(20), child: Column( children: [ - SizedBox( - height: 20, - ), - FutureBuilder( - future: getApplicationDetails(), - builder: (context, snapshot) { - return snapshot.hasData - ? Column( - children: [ - Row( - children: [ - Text( - "application identifier => ", - style: TextStyle(fontSize: 15), - ), - Text( - snapshot.data?.applicationIdentifier ?? "", - style: TextStyle(fontSize: 15), - ) - ], - ), - SizedBox( - height: 10, - ), - Row( - children: [ - Text( - "application platform => ", - style: TextStyle(fontSize: 15), - ), - Text( - snapshot.data?.applicationPlatform ?? "", - style: TextStyle(fontSize: 15), - ) - ], - ), - SizedBox( - height: 10, - ), - Row( - children: [ - Text( - "application version => ", - style: TextStyle(fontSize: 15), - ), - Text( - snapshot.data?.applicationVersion ?? "", - style: TextStyle(fontSize: 15), - ) - ], - ), - SizedBox( - height: 10, - ), - ], - ) - : Text(""); - }, - ), + SizedBox(height: 20), + _buildApplicationDetails(), + Divider(), Expanded( - child: FutureBuilder( - future: getClientResource(), - builder: (context, snapshot) { - final snapshotData = snapshot.data; - return snapshotData != null - ? ListView.builder( - itemCount: snapshotData.devices.length, - itemBuilder: (BuildContext context, int index) { - return ExpansionTile( - title: Text(snapshotData.devices[index].name), - expandedCrossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - "Id => ${snapshotData.devices[index].id}", - style: TextStyle(fontSize: 18), - ), - SizedBox( - height: 10, - ), - Text( - "Application => ${snapshotData.devices[index].application}", - style: TextStyle(fontSize: 18), - ), - SizedBox( - height: 10, - ), - Text( - "Mobile authentication enabled => ${snapshotData.devices[index].mobileAuthenticationEnabled.toString()}", - style: TextStyle(fontSize: 18), - ), - SizedBox( - height: 10, - ), - Text( - "Platform => ${snapshotData.devices[index].platform}", - style: TextStyle(fontSize: 18), - ), - SizedBox( - height: 10, - ), - ], - ); - }, - ) - : Center( - child: SizedBox( - child: CircularProgressIndicator(), - ), - ); - }, - ), + child: _buildDeviceInfoList(), ), ], ), diff --git a/ios/Classes/NativeBridge/Errors/SdkError.swift b/ios/Classes/NativeBridge/Errors/SdkError.swift index 39906131..596e957f 100644 --- a/ios/Classes/NativeBridge/Errors/SdkError.swift +++ b/ios/Classes/NativeBridge/Errors/SdkError.swift @@ -40,7 +40,7 @@ class SdkError: Error { } // Error codes with httResponse information - init(code: Int, errorDescription: String, response: ONGResourceResponse?, iosCode: Int? = nil, iosMessage: String? = nil) { + init(code: Int, errorDescription: String, response: ResourceResponse?, iosCode: Int? = nil, iosMessage: String? = nil) { self.code = code self.errorDescription = errorDescription @@ -48,7 +48,7 @@ class SdkError: Error { setResponseDetails(response, iosCode, iosMessage) } - init(_ wrapperError: OneWelcomeWrapperError, response: ONGResourceResponse?, iosCode: Int? = nil, iosMessage: String? = nil) { + init(_ wrapperError: OneWelcomeWrapperError, response: ResourceResponse?, iosCode: Int? = nil, iosMessage: String? = nil) { self.code = wrapperError.code() self.errorDescription = wrapperError.message() @@ -82,7 +82,7 @@ private extension SdkError { } } - func setResponseDetails(_ response: ONGResourceResponse?, _ iosCode: Int?, _ iosMessage: String?) { + func setResponseDetails(_ response: ResourceResponse?, _ iosCode: Int?, _ iosMessage: String?) { if response == nil { details["response"] = [String: Any?]() } else { @@ -100,11 +100,11 @@ private extension SdkError { } } -private extension ONGResourceResponse { +private extension ResourceResponse { func toJSON() -> [String: Any?] { return ["statusCode": statusCode, "headers": allHeaderFields, - "url": rawResponse.url?.absoluteString, + "url": response.url, "body": data != nil ? String(data: data!, encoding: .utf8) : nil ] } diff --git a/ios/Classes/NativeBridge/Handlers/ResourcesHandler.swift b/ios/Classes/NativeBridge/Handlers/ResourcesHandler.swift index f1627a33..d45cfcd5 100644 --- a/ios/Classes/NativeBridge/Handlers/ResourcesHandler.swift +++ b/ios/Classes/NativeBridge/Handlers/ResourcesHandler.swift @@ -5,7 +5,7 @@ typealias FlutterDataCallback = (Any?, SdkError?) -> Void protocol FetchResourcesHandlerProtocol: AnyObject { func authenticateDevice(_ scopes: [String]?, completion: @escaping (Result) -> Void) - func authenticateUserImplicitly(_ profile: ONGUserProfile, scopes: [String]?, completion: @escaping (Result) -> Void) + func authenticateUserImplicitly(_ profile: UserProfile, scopes: [String]?, completion: @escaping (Result) -> Void) func requestResource(_ type: ResourceRequestType, _ details: OWRequestDetails, completion: @escaping (Result) -> Void) } @@ -14,7 +14,7 @@ class ResourcesHandler: FetchResourcesHandlerProtocol { func authenticateDevice(_ scopes: [String]?, completion: @escaping (Result) -> Void) { Logger.log("authenticateDevice", sender: self) - ONGDeviceClient.sharedInstance().authenticateDevice(scopes) { _, error in + SharedDeviceClient.instance.authenticateDevice(with: scopes) { error in if let error = error { let mappedError = FlutterError(ErrorMapper().mapError(error)) completion(.failure(mappedError)) @@ -24,22 +24,20 @@ class ResourcesHandler: FetchResourcesHandlerProtocol { } } - func authenticateUserImplicitly(_ profile: ONGUserProfile, scopes: [String]?, completion: @escaping (Result) -> Void) { - Logger.log("authenticateImplicitly", sender: self) - ONGUserClient.sharedInstance().implicitlyAuthenticateUser(profile, scopes: scopes) { success, error in - if success { - completion(.success) - } else { - // This error construction is obviously not good, but it will work for now till we refactor this later - let mappedError = FlutterError(error.flatMap { ErrorMapper().mapError($0) } ?? SdkError(.genericError)) + func authenticateUserImplicitly(_ profile: UserProfile, scopes: [String]?, completion: @escaping (Result) -> Void) { + Logger.log("authenticateUserImplicitly", sender: self) + SharedUserClient.instance.implicitlyAuthenticate(user: profile, with: scopes) { error in + if let error = error { + let mappedError = FlutterError(ErrorMapper().mapError(error)) completion(.failure(mappedError)) + } else { + completion(.success) } } } func requestResource(_ requestType: ResourceRequestType, _ details: OWRequestDetails, completion: @escaping (Result) -> Void) { Logger.log("requestResource", sender: self) - // Additional check for valid url let resourceUrl = ONGClient.sharedInstance().configModel.resourceBaseURL ?? "" if isValidUrl(details.path) == false && isValidUrl(resourceUrl + details.path) == false { @@ -47,24 +45,23 @@ class ResourcesHandler: FetchResourcesHandlerProtocol { return } - let request = generateONGResourceRequest(details) + let request = generateResourceRequest(details) let requestCompletion = getRequestCompletion(completion) switch requestType { case ResourceRequestType.implicit: // For consistency with Android we perform this step - if ONGUserClient.sharedInstance().implicitlyAuthenticatedUserProfile() == nil { + if SharedUserClient.instance.implicitlyAuthenticatedUserProfile == nil { completion(.failure(FlutterError(SdkError(.notAuthenticatedImplicit)))) return } - - ONGUserClient.sharedInstance().fetchImplicitResource(request, completion: requestCompletion) + SharedUserClient.instance.sendImplicitRequest(request, completion: requestCompletion) case ResourceRequestType.anonymous: - ONGDeviceClient.sharedInstance().fetchResource(request, completion: requestCompletion) + SharedDeviceClient.instance.sendRequest(request, completion: requestCompletion) case ResourceRequestType.authenticated: - ONGUserClient.sharedInstance().fetchResource(request, completion: requestCompletion) + SharedUserClient.instance.sendAuthenticatedRequest(request, completion: requestCompletion) case ResourceRequestType.unauthenticated: - ONGDeviceClient.sharedInstance().fetchUnauthenticatedResource(request, completion: requestCompletion) + SharedDeviceClient.instance.sendUnauthenticatedRequest(request, completion: requestCompletion) } } } @@ -78,11 +75,11 @@ private extension ResourcesHandler { return false } - func generateONGResourceRequest(_ details: OWRequestDetails) -> ONGResourceRequest { - Logger.log("generateONGResourceRequest", sender: self) + func generateResourceRequest(_ details: OWRequestDetails) -> ResourceRequest { + Logger.log("generateResourceRequest", sender: self) - return ONGResourceRequest(path: details.path, - method: details.method.stringValue, + return ResourceRequestFactory.makeResourceRequest(path: details.path, + method: details.method.toHTTPMethod(), body: details.body?.data(using: .utf8), headers: getRequestHeaders(details.headers) ) @@ -96,9 +93,9 @@ private extension ResourcesHandler { return headers.filter { $0.key != nil && $0.value != nil } as? [String: String] } - func getRequestCompletion(_ completion: @escaping (Result) -> Void) -> ((ONGResourceResponse?, Error?) -> Void)? { + func getRequestCompletion(_ completion: @escaping (Result) -> Void) -> ((ResourceResponse?, Error?) -> Void) { Logger.log("getCompletionRequest", sender: self) - let completionRequest: ((ONGResourceResponse?, Error?) -> Void)? = { response, error in + let completionRequest: ((ResourceResponse?, Error?) -> Void) = { response, error in if let error = error { if response != nil { let flutterError = FlutterError(SdkError(.httpRequestErrorCode, response: response, iosCode: error.code, iosMessage: error.localizedDescription)) @@ -121,12 +118,12 @@ private extension ResourcesHandler { } private extension HttpRequestMethod { - var stringValue: String { + func toHTTPMethod() -> HTTPMethod { switch self { - case .get: return "GET" - case .post: return "POST" - case .delete: return "DELETE" - case .put: return "PUT" + case .get: return .get + case .post: return .post + case .delete: return .delete + case .put: return .put } } } diff --git a/ios/Classes/NativeBridge/ModuleExtensions/OneginiModuleSwift+Auth.swift b/ios/Classes/NativeBridge/ModuleExtensions/OneginiModuleSwift+Auth.swift index adf5d250..3daaf9a7 100644 --- a/ios/Classes/NativeBridge/ModuleExtensions/OneginiModuleSwift+Auth.swift +++ b/ios/Classes/NativeBridge/ModuleExtensions/OneginiModuleSwift+Auth.swift @@ -15,8 +15,8 @@ extension OneginiModuleSwift { public func authenticateUserImplicitly(_ profileId: String, _ scopes: [String]?, completion: @escaping (Result) -> Void) { - guard let profile = ONGClient.sharedInstance().userClient.userProfiles().first(where: { $0.profileId == profileId }) else { - completion(.failure(FlutterError(.notAuthenticatedUser))) + guard let profile = SharedUserClient.instance.userProfiles.first(where: { $0.profileId == profileId }) else { + completion(.failure(SdkError(.notFoundUserProfile).flutterError())) return } diff --git a/ios/Classes/SwiftOneginiPlugin.swift b/ios/Classes/SwiftOneginiPlugin.swift index 5deba451..5e211e1b 100644 --- a/ios/Classes/SwiftOneginiPlugin.swift +++ b/ios/Classes/SwiftOneginiPlugin.swift @@ -43,7 +43,7 @@ extension OWIdentityProvider { } extension OWRequestResponse { - init(_ response: ONGResourceResponse) { + init(_ response: ResourceResponse) { headers = toOWRequestHeaders(response.allHeaderFields) body = String(data: response.data ?? Data(), encoding: .utf8) ?? "" status = Int64(response.statusCode)