From 556faadd4832fd4ee958fe559abd4b16990cb030 Mon Sep 17 00:00:00 2001 From: Mario Fischer Date: Sun, 15 Oct 2023 12:00:42 +0200 Subject: [PATCH 01/13] Refactor: remove platform specific code --- android/.gitignore | 9 -- android/build.gradle | 51 -------- android/settings.gradle | 1 - android/src/main/AndroidManifest.xml | 3 - .../casdoor_flutter_sdk/CallbackActivity.kt | 34 ----- .../CasdoorFlutterSdkPlugin.kt | 92 ------------- .../casdoor_flutter_sdk/KeepAliveService.kt | 30 ----- ios/.gitignore | 38 ------ ios/Assets/.gitkeep | 0 ios/Classes/CasdoorFlutterSdkPlugin.h | 18 --- ios/Classes/CasdoorFlutterSdkPlugin.m | 29 ----- .../SwiftCasdoorFlutterSdkPlugin.swift | 121 ------------------ ios/casdoor_flutter_sdk.podspec | 23 ---- macos/Classes/CasdoorFlutterSdkPlugin.swift | 74 ----------- macos/casdoor_flutter_sdk.podspec | 23 ---- 15 files changed, 546 deletions(-) delete mode 100644 android/.gitignore delete mode 100644 android/build.gradle delete mode 100644 android/settings.gradle delete mode 100644 android/src/main/AndroidManifest.xml delete mode 100644 android/src/main/kotlin/com/example/casdoor_flutter_sdk/CallbackActivity.kt delete mode 100644 android/src/main/kotlin/com/example/casdoor_flutter_sdk/CasdoorFlutterSdkPlugin.kt delete mode 100644 android/src/main/kotlin/com/example/casdoor_flutter_sdk/KeepAliveService.kt delete mode 100644 ios/.gitignore delete mode 100644 ios/Assets/.gitkeep delete mode 100644 ios/Classes/CasdoorFlutterSdkPlugin.h delete mode 100644 ios/Classes/CasdoorFlutterSdkPlugin.m delete mode 100644 ios/Classes/SwiftCasdoorFlutterSdkPlugin.swift delete mode 100644 ios/casdoor_flutter_sdk.podspec delete mode 100644 macos/Classes/CasdoorFlutterSdkPlugin.swift delete mode 100644 macos/casdoor_flutter_sdk.podspec diff --git a/android/.gitignore b/android/.gitignore deleted file mode 100644 index 161bdcd..0000000 --- a/android/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea/workspace.xml -/.idea/libraries -.DS_Store -/build -/captures -.cxx diff --git a/android/build.gradle b/android/build.gradle deleted file mode 100644 index 62660ba..0000000 --- a/android/build.gradle +++ /dev/null @@ -1,51 +0,0 @@ -group 'com.example.casdoor_flutter_sdk' -version '1.0-SNAPSHOT' - -buildscript { - ext.kotlin_version = '1.6.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.1.2' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -rootProject.allprojects { - repositories { - google() - mavenCentral() - } -} - -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' - -android { - compileSdkVersion 31 - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - defaultConfig { - minSdkVersion 16 - } -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.browser:browser:1.3.0' -} diff --git a/android/settings.gradle b/android/settings.gradle deleted file mode 100644 index e65643a..0000000 --- a/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'casdoor_flutter_sdk' diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml deleted file mode 100644 index ef6b80c..0000000 --- a/android/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - diff --git a/android/src/main/kotlin/com/example/casdoor_flutter_sdk/CallbackActivity.kt b/android/src/main/kotlin/com/example/casdoor_flutter_sdk/CallbackActivity.kt deleted file mode 100644 index 9e3444c..0000000 --- a/android/src/main/kotlin/com/example/casdoor_flutter_sdk/CallbackActivity.kt +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2022 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.example.casdoor_flutter_sdk - -import android.app.Activity -import android.net.Uri -import android.os.Bundle - -class CallbackActivity: Activity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - val url = intent?.data - val scheme = url?.scheme - - if (scheme != null) { - CasdoorFlutterSdkPlugin.callbacks.remove(scheme)?.success(url.toString()) - } - - finish() - } -} diff --git a/android/src/main/kotlin/com/example/casdoor_flutter_sdk/CasdoorFlutterSdkPlugin.kt b/android/src/main/kotlin/com/example/casdoor_flutter_sdk/CasdoorFlutterSdkPlugin.kt deleted file mode 100644 index e816b2c..0000000 --- a/android/src/main/kotlin/com/example/casdoor_flutter_sdk/CasdoorFlutterSdkPlugin.kt +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2022 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.example.casdoor_flutter_sdk - -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.os.Build - -import androidx.browser.customtabs.CustomTabsIntent - -import io.flutter.embedding.engine.plugins.FlutterPlugin -import io.flutter.plugin.common.BinaryMessenger -import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel -import io.flutter.plugin.common.MethodChannel.MethodCallHandler -import io.flutter.plugin.common.MethodChannel.Result -import io.flutter.plugin.common.PluginRegistry.Registrar - -class CasdoorFlutterSdkPlugin(private var context: Context? = null, private var channel: MethodChannel? = null): MethodCallHandler, FlutterPlugin { - - companion object { - val callbacks = mutableMapOf() - - @JvmStatic - fun registerWith(registrar: Registrar) { - val plugin = CasdoorFlutterSdkPlugin() - plugin.initInstance(registrar.messenger(), registrar.context()) - } - } - - private fun initInstance(messenger: BinaryMessenger, context: Context) { - this.context = context - channel = MethodChannel(messenger, "casdoor_flutter_sdk") - channel?.setMethodCallHandler(this) - } - - override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { - initInstance(binding.binaryMessenger, binding.applicationContext) - } - - override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { - context = null - channel = null - } - - override fun onMethodCall(call: MethodCall, resultCallback: Result) { - when (call.method) { - "authenticate" -> { - val url = Uri.parse(call.argument("url")) - val callbackUrlScheme = call.argument("callbackUrlScheme")!! - val preferEphemeral = call.argument("preferEphemeral")!! - - callbacks[callbackUrlScheme] = resultCallback - - val intent = CustomTabsIntent.Builder().build() - val keepAliveIntent = Intent(context, KeepAliveService::class.java) - - intent.intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) - if (preferEphemeral) { - intent.intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY) - } - intent.intent.putExtra("android.support.customs.extra.KEEP_ALIVE", keepAliveIntent) - - intent.launchUrl(context!!, url) - } - "cleanUpDanglingCalls" -> { - callbacks.forEach{ (_, danglingResultCallback) -> - danglingResultCallback.error("CANCELED", "User canceled login", null) - } - callbacks.clear() - resultCallback.success(null) - } - "getPlatformVersion" -> { - resultCallback.success("Android ${Build.VERSION.RELEASE}") - } - else -> resultCallback.notImplemented() - } - } -} diff --git a/android/src/main/kotlin/com/example/casdoor_flutter_sdk/KeepAliveService.kt b/android/src/main/kotlin/com/example/casdoor_flutter_sdk/KeepAliveService.kt deleted file mode 100644 index cc1495f..0000000 --- a/android/src/main/kotlin/com/example/casdoor_flutter_sdk/KeepAliveService.kt +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2022 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.example.casdoor_flutter_sdk - -import android.app.Service -import android.content.Intent -import android.os.Binder -import android.os.IBinder - -class KeepAliveService: Service() { - companion object { - val binder = Binder() - } - - override fun onBind(intent: Intent): IBinder { - return binder - } -} diff --git a/ios/.gitignore b/ios/.gitignore deleted file mode 100644 index 0c88507..0000000 --- a/ios/.gitignore +++ /dev/null @@ -1,38 +0,0 @@ -.idea/ -.vagrant/ -.sconsign.dblite -.svn/ - -.DS_Store -*.swp -profile - -DerivedData/ -build/ -GeneratedPluginRegistrant.h -GeneratedPluginRegistrant.m - -.generated/ - -*.pbxuser -*.mode1v3 -*.mode2v3 -*.perspectivev3 - -!default.pbxuser -!default.mode1v3 -!default.mode2v3 -!default.perspectivev3 - -xcuserdata - -*.moved-aside - -*.pyc -*sync/ -Icon? -.tags* - -/Flutter/Generated.xcconfig -/Flutter/ephemeral/ -/Flutter/flutter_export_environment.sh \ No newline at end of file diff --git a/ios/Assets/.gitkeep b/ios/Assets/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/ios/Classes/CasdoorFlutterSdkPlugin.h b/ios/Classes/CasdoorFlutterSdkPlugin.h deleted file mode 100644 index 49e08d1..0000000 --- a/ios/Classes/CasdoorFlutterSdkPlugin.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2022 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import - -@interface CasdoorFlutterSdkPlugin : NSObject -@end diff --git a/ios/Classes/CasdoorFlutterSdkPlugin.m b/ios/Classes/CasdoorFlutterSdkPlugin.m deleted file mode 100644 index f963f6d..0000000 --- a/ios/Classes/CasdoorFlutterSdkPlugin.m +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2022 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "CasdoorFlutterSdkPlugin.h" -#if __has_include() -#import -#else -// Support project import fallback if the generated compatibility header -// is not copied when this plugin is created as a library. -// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 -#import "casdoor_flutter_sdk-Swift.h" -#endif - -@implementation CasdoorFlutterSdkPlugin -+ (void)registerWithRegistrar:(NSObject*)registrar { - [SwiftCasdoorFlutterSdkPlugin registerWithRegistrar:registrar]; -} -@end diff --git a/ios/Classes/SwiftCasdoorFlutterSdkPlugin.swift b/ios/Classes/SwiftCasdoorFlutterSdkPlugin.swift deleted file mode 100644 index 090b7c4..0000000 --- a/ios/Classes/SwiftCasdoorFlutterSdkPlugin.swift +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2022 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import Flutter -import UIKit -import AuthenticationServices -import SafariServices - -public class SwiftCasdoorFlutterSdkPlugin: NSObject, FlutterPlugin { - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "casdoor_flutter_sdk", binaryMessenger: registrar.messenger()) - let instance = SwiftCasdoorFlutterSdkPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - if call.method == "authenticate", - let arguments = call.arguments as? Dictionary, - let urlString = arguments["url"] as? String, - let url = URL(string: urlString), - let callbackURLScheme = arguments["callbackUrlScheme"] as? String, - let preferEphemeral = arguments["preferEphemeral"] as? Bool - { - - var sessionToKeepAlive: Any? = nil // if we do not keep the session alive, it will get closed immediately while showing the dialog - let completionHandler = { (url: URL?, err: Error?) in - sessionToKeepAlive = nil - - if let err = err { - if #available(iOS 12, *) { - if case ASWebAuthenticationSessionError.canceledLogin = err { - result(FlutterError(code: "CANCELED", message: "User canceled login", details: nil)) - return - } - } - - if #available(iOS 11, *) { - if case SFAuthenticationError.canceledLogin = err { - result(FlutterError(code: "CANCELED", message: "User canceled login", details: nil)) - return - } - } - - result(FlutterError(code: "EUNKNOWN", message: err.localizedDescription, details: nil)) - return - } - - guard let url = url else { - result(FlutterError(code: "EUNKNOWN", message: "URL was null, but no error provided.", details: nil)) - return - } - - result(url.absoluteString) - } - - if #available(iOS 12, *) { - let session = ASWebAuthenticationSession(url: url, callbackURLScheme: callbackURLScheme, completionHandler: completionHandler) - - if #available(iOS 13, *) { - guard var topController = UIApplication.shared.keyWindow?.rootViewController else { - result(FlutterError.aquireRootViewControllerFailed) - return - } - - while let presentedViewController = topController.presentedViewController { - topController = presentedViewController - } - if let nav = topController as? UINavigationController { - topController = nav.visibleViewController ?? topController - } - - guard let contextProvider = topController as? ASWebAuthenticationPresentationContextProviding else { - result(FlutterError.aquireRootViewControllerFailed) - return - } - session.presentationContextProvider = contextProvider - session.prefersEphemeralWebBrowserSession = preferEphemeral - } - - session.start() - sessionToKeepAlive = session - } else if #available(iOS 11, *) { - let session = SFAuthenticationSession(url: url, callbackURLScheme: callbackURLScheme, completionHandler: completionHandler) - session.start() - sessionToKeepAlive = session - } else { - result(FlutterError(code: "FAILED", message: "This plugin does currently not support iOS lower than iOS 11" , details: nil)) - } - } else if (call.method == "cleanUpDanglingCalls") { - // we do not keep track of old callbacks on iOS, so nothing to do here - result(nil) - } else if (call.method == "getPlatformVersion") { - result("iOS " + ProcessInfo.processInfo.operatingSystemVersionString) - } else { - result(FlutterMethodNotImplemented) - } - } -} -@available(iOS 13, *) -extension FlutterViewController: ASWebAuthenticationPresentationContextProviding { - public func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { - return self.view.window! - } -} - -fileprivate extension FlutterError { - static var aquireRootViewControllerFailed: FlutterError { - return FlutterError(code: "AQUIRE_ROOT_VIEW_CONTROLLER_FAILED", message: "Failed to aquire root view controller" , details: nil) - } -} \ No newline at end of file diff --git a/ios/casdoor_flutter_sdk.podspec b/ios/casdoor_flutter_sdk.podspec deleted file mode 100644 index 9322cd8..0000000 --- a/ios/casdoor_flutter_sdk.podspec +++ /dev/null @@ -1,23 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint casdoor_flutter_sdk.podspec` to validate before publishing. -# -Pod::Spec.new do |s| - s.name = 'casdoor_flutter_sdk' - s.version = '0.0.1' - s.summary = 'A new Flutter project.' - s.description = <<-DESC -A new Flutter project. - DESC - s.homepage = 'http://example.com' - s.license = { :file => '../LICENSE' } - s.author = { 'Your Company' => 'email@example.com' } - s.source = { :path => '.' } - s.source_files = 'Classes/**/*' - s.dependency 'Flutter' - s.platform = :ios, '9.0' - - # Flutter.framework does not contain a i386 slice. - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } - s.swift_version = '5.0' -end diff --git a/macos/Classes/CasdoorFlutterSdkPlugin.swift b/macos/Classes/CasdoorFlutterSdkPlugin.swift deleted file mode 100644 index d3f8e58..0000000 --- a/macos/Classes/CasdoorFlutterSdkPlugin.swift +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2022 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import AuthenticationServices -import SafariServices -import FlutterMacOS - -@available(OSX 10.15, *) -public class CasdoorFlutterSdkPlugin: NSObject, FlutterPlugin { - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "casdoor_flutter_sdk", binaryMessenger: registrar.messenger) - let instance = CasdoorFlutterSdkPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - if call.method == "authenticate" { - let url = URL(string: (call.arguments as! Dictionary)["url"] as! String)! - let callbackURLScheme = (call.arguments as! Dictionary)["callbackUrlScheme"] as! String - - var keepMe: Any? = nil - let completionHandler = { (url: URL?, err: Error?) in - keepMe = nil - - if let err = err { - if case ASWebAuthenticationSessionError.canceledLogin = err { - result(FlutterError(code: "CANCELED", message: "User canceled login", details: nil)) - return - } - - result(FlutterError(code: "EUNKNOWN", message: err.localizedDescription, details: nil)) - return - } - - result(url!.absoluteString) - } - - let session = ASWebAuthenticationSession(url: url, callbackURLScheme: callbackURLScheme, completionHandler: completionHandler) - - guard let provider = NSApplication.shared.keyWindow!.contentViewController as? FlutterViewController else { - result(FlutterError(code: "FAILED", message: "Failed to aquire root FlutterViewController" , details: nil)) - return - } - - session.presentationContextProvider = provider - - session.start() - keepMe = session - }else if (call.method == "getPlatformVersion") { - result("macOS " + ProcessInfo.processInfo.operatingSystemVersionString) - } - else { - result(FlutterMethodNotImplemented) - } - } -} - -@available(OSX 10.15, *) -extension FlutterViewController: ASWebAuthenticationPresentationContextProviding { - public func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { - return self.view.window! - } -} diff --git a/macos/casdoor_flutter_sdk.podspec b/macos/casdoor_flutter_sdk.podspec deleted file mode 100644 index ec40bef..0000000 --- a/macos/casdoor_flutter_sdk.podspec +++ /dev/null @@ -1,23 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint casdoor_flutter_sdk.podspec` to validate before publishing. -# -Pod::Spec.new do |s| - s.name = 'casdoor_flutter_sdk' - s.version = '0.0.1' - s.summary = 'A new Flutter project.' - s.description = <<-DESC -A new Flutter project. - DESC - s.homepage = 'http://example.com' - s.license = { :file => '../LICENSE' } - s.author = { 'Your Company' => 'email@example.com' } - - s.source = { :path => '.' } - s.source_files = 'Classes/**/*' - s.dependency 'FlutterMacOS' - - s.platform = :osx, '10.11' - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } - s.swift_version = '5.0' -end From 755544ed5b31ef70ee95a3a064faa6dd819152fc Mon Sep 17 00:00:00 2001 From: Mario Fischer Date: Sun, 15 Oct 2023 12:22:21 +0200 Subject: [PATCH 02/13] refactor: move files to src subdirectory --- lib/{ => src}/casdoor.dart | 0 lib/{ => src}/casdoor_flutter_sdk.dart | 0 lib/{ => src}/casdoor_flutter_sdk_config.dart | 0 lib/{ => src}/casdoor_flutter_sdk_method_channel.dart | 0 lib/{ => src}/casdoor_flutter_sdk_oauth.dart | 0 lib/{ => src}/casdoor_flutter_sdk_platform_interface.dart | 0 lib/{ => src}/casdoor_flutter_sdk_web.dart | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename lib/{ => src}/casdoor.dart (100%) rename lib/{ => src}/casdoor_flutter_sdk.dart (100%) rename lib/{ => src}/casdoor_flutter_sdk_config.dart (100%) rename lib/{ => src}/casdoor_flutter_sdk_method_channel.dart (100%) rename lib/{ => src}/casdoor_flutter_sdk_oauth.dart (100%) rename lib/{ => src}/casdoor_flutter_sdk_platform_interface.dart (100%) rename lib/{ => src}/casdoor_flutter_sdk_web.dart (100%) diff --git a/lib/casdoor.dart b/lib/src/casdoor.dart similarity index 100% rename from lib/casdoor.dart rename to lib/src/casdoor.dart diff --git a/lib/casdoor_flutter_sdk.dart b/lib/src/casdoor_flutter_sdk.dart similarity index 100% rename from lib/casdoor_flutter_sdk.dart rename to lib/src/casdoor_flutter_sdk.dart diff --git a/lib/casdoor_flutter_sdk_config.dart b/lib/src/casdoor_flutter_sdk_config.dart similarity index 100% rename from lib/casdoor_flutter_sdk_config.dart rename to lib/src/casdoor_flutter_sdk_config.dart diff --git a/lib/casdoor_flutter_sdk_method_channel.dart b/lib/src/casdoor_flutter_sdk_method_channel.dart similarity index 100% rename from lib/casdoor_flutter_sdk_method_channel.dart rename to lib/src/casdoor_flutter_sdk_method_channel.dart diff --git a/lib/casdoor_flutter_sdk_oauth.dart b/lib/src/casdoor_flutter_sdk_oauth.dart similarity index 100% rename from lib/casdoor_flutter_sdk_oauth.dart rename to lib/src/casdoor_flutter_sdk_oauth.dart diff --git a/lib/casdoor_flutter_sdk_platform_interface.dart b/lib/src/casdoor_flutter_sdk_platform_interface.dart similarity index 100% rename from lib/casdoor_flutter_sdk_platform_interface.dart rename to lib/src/casdoor_flutter_sdk_platform_interface.dart diff --git a/lib/casdoor_flutter_sdk_web.dart b/lib/src/casdoor_flutter_sdk_web.dart similarity index 100% rename from lib/casdoor_flutter_sdk_web.dart rename to lib/src/casdoor_flutter_sdk_web.dart From e29cb2d7c59ee1d2cfd8703d1181c6e220ff4e35 Mon Sep 17 00:00:00 2001 From: Mario Fischer Date: Sun, 15 Oct 2023 12:33:31 +0200 Subject: [PATCH 03/13] feat: add file for simpler import --- lib/casdoor_flutter_sdk.dart | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 lib/casdoor_flutter_sdk.dart diff --git a/lib/casdoor_flutter_sdk.dart b/lib/casdoor_flutter_sdk.dart new file mode 100644 index 0000000..1c92889 --- /dev/null +++ b/lib/casdoor_flutter_sdk.dart @@ -0,0 +1,3 @@ +export 'src/casdoor.dart'; +export 'src/casdoor_flutter_sdk_config.dart'; +export 'src/casdoor_flutter_sdk_platform_interface.dart'; From 78708e572030e81b1cac52aaa0b0c035fde1fe37 Mon Sep 17 00:00:00 2001 From: Mario Fischer Date: Sun, 15 Oct 2023 12:35:40 +0200 Subject: [PATCH 04/13] feat: add parameter class --- lib/src/casdoor_flutter_sdk_config.dart | 41 +++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/lib/src/casdoor_flutter_sdk_config.dart b/lib/src/casdoor_flutter_sdk_config.dart index 2a063a2..342a404 100644 --- a/lib/src/casdoor_flutter_sdk_config.dart +++ b/lib/src/casdoor_flutter_sdk_config.dart @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +import 'package:flutter/widgets.dart'; + class AuthConfig { final String clientId; final String serverUrl; @@ -28,3 +30,42 @@ class AuthConfig { this.redirectUri = "casdoor://callback", this.callbackUrlScheme = "casdoor"}); } + +class CasdoorSdkParams { + CasdoorSdkParams({ + required this.url, + required this.callbackUrlScheme, + this.buildContext, + this.showFullscreen, + this.isMaterialStyle, + this.preferEphemeral, + this.clearCache, + }); + + final String url; + final String callbackUrlScheme; + BuildContext? buildContext; + bool? showFullscreen; + bool? isMaterialStyle; + bool? preferEphemeral; + bool? clearCache; + + CasdoorSdkParams copyWith({ + String? url, + String? callbackUrlScheme, + BuildContext? buildContext, + bool? showFullscreen, + bool? isMaterialStyle, + bool? preferEphemeral, + bool? clearCache, + }) => + CasdoorSdkParams( + url: url ?? this.url, + callbackUrlScheme: callbackUrlScheme ?? this.callbackUrlScheme, + buildContext: buildContext ?? this.buildContext, + showFullscreen: showFullscreen ?? this.showFullscreen, + isMaterialStyle: isMaterialStyle ?? this.isMaterialStyle, + preferEphemeral: preferEphemeral ?? this.preferEphemeral, + clearCache: clearCache ?? this.clearCache, + ); +} From 1274870555abb4d4a90cdab8875c6b870d0d7fde Mon Sep 17 00:00:00 2001 From: Mario Fischer Date: Sun, 15 Oct 2023 12:43:25 +0200 Subject: [PATCH 05/13] refactor: simplify method invocation and prepare for Dart plugins --- lib/src/casdoor.dart | 125 +++++++++++------- lib/src/casdoor_flutter_sdk.dart | 2 +- .../casdoor_flutter_sdk_method_channel.dart | 25 +++- lib/src/casdoor_flutter_sdk_oauth.dart | 55 ++------ ...asdoor_flutter_sdk_platform_interface.dart | 27 ++-- lib/src/casdoor_flutter_sdk_web.dart | 44 +++--- 6 files changed, 143 insertions(+), 135 deletions(-) diff --git a/lib/src/casdoor.dart b/lib/src/casdoor.dart index c8331fd..3ae9bca 100644 --- a/lib/src/casdoor.dart +++ b/lib/src/casdoor.dart @@ -14,10 +14,12 @@ import 'dart:convert'; import 'dart:math'; -import 'package:http/http.dart' as http; -import 'package:casdoor_flutter_sdk/casdoor_flutter_sdk_config.dart'; -import 'package:casdoor_flutter_sdk/casdoor_flutter_sdk_oauth.dart'; + +import 'package:casdoor_flutter_sdk/src/casdoor_flutter_sdk_config.dart'; +import 'package:casdoor_flutter_sdk/src/casdoor_flutter_sdk_oauth.dart'; import 'package:crypto/crypto.dart'; +import 'package:flutter/widgets.dart'; +import 'package:http/http.dart' as http; import 'package:jwt_decoder/jwt_decoder.dart'; class Casdoor { @@ -31,8 +33,8 @@ class Casdoor { } String parseScheme() { - String scheme = "https"; - var uri = Uri.parse(config.serverUrl); + String scheme = 'https'; + final uri = Uri.parse(config.serverUrl); if (uri.hasScheme) { scheme = uri.scheme; } @@ -40,55 +42,74 @@ class Casdoor { } String parseHost() { - var uri = Uri.parse(config.serverUrl); + final uri = Uri.parse(config.serverUrl); return uri.host; } int parsePort() { - var uri = Uri.parse(config.serverUrl); + final uri = Uri.parse(config.serverUrl); return uri.port; } - Uri getSigninUrl({String scope = "read", String? state}) { + Uri getSigninUrl({String scope = 'read', String? state}) { return Uri( scheme: parseScheme(), host: parseHost(), port: parsePort(), - path: "login/oauth/authorize", + path: 'login/oauth/authorize', queryParameters: { - "client_id": config.clientId, - "response_type": "code", - "scope": scope, - "state": state ?? config.appName, - "code_challenge_method": "S256", - "nonce": nonce, - "code_challenge": generateCodeChallenge(codeVerifier), - "redirect_uri": config.redirectUri + 'client_id': config.clientId, + 'response_type': 'code', + 'scope': scope, + 'state': state ?? config.appName, + 'code_challenge_method': 'S256', + 'nonce': nonce, + 'code_challenge': generateCodeChallenge(codeVerifier), + 'redirect_uri': config.redirectUri }); } - Uri getSignupUrl({String scope = "read", String? state}) { + Uri getSignupUrl({String scope = 'read', String? state}) { return Uri( scheme: parseScheme(), host: parseHost(), port: parsePort(), - path: "/signup/oauth/authorize", + path: '/signup/oauth/authorize', queryParameters: { - "client_id": config.clientId, - "response_type": "code", - "scope": scope, - "state": state ?? config.appName, - "code_challenge_method": "S256", - "nonce": nonce, - "code_challenge": generateCodeChallenge(codeVerifier), - "redirect_uri": config.redirectUri + 'client_id': config.clientId, + 'response_type': 'code', + 'scope': scope, + 'state': state ?? config.appName, + 'code_challenge_method': 'S256', + 'nonce': nonce, + 'code_challenge': generateCodeChallenge(codeVerifier), + 'redirect_uri': config.redirectUri }); } - Future show({String scope = "read", String? state}) async { - return CasdoorOauth.authenticate( - url: getSigninUrl(scope: scope, state: state).toString(), - callbackUrlScheme: config.callbackUrlScheme); + Future show({ + String scope = 'read', + String? state, + }) async { + return CasdoorOauth.authenticate(CasdoorSdkParams( + url: getSigninUrl(scope: scope, state: state).toString(), + callbackUrlScheme: config.callbackUrlScheme, + )); + } + + Future showFullscreen( + BuildContext buildContext, { + bool? isMaterialStyle, + String scope = 'read', + String? state, + }) { + return CasdoorOauth.authenticate(CasdoorSdkParams( + url: getSigninUrl(scope: scope, state: state).toString(), + callbackUrlScheme: config.callbackUrlScheme, + buildContext: buildContext, + showFullscreen: true, + isMaterialStyle: isMaterialStyle ?? true, + )); } Future requestOauthAccessToken(String code) async { @@ -97,7 +118,7 @@ class Casdoor { scheme: parseScheme(), host: parseHost(), port: parsePort(), - path: "api/login/oauth/access_token", + path: 'api/login/oauth/access_token', ), body: { 'client_id': config.clientId, @@ -108,13 +129,13 @@ class Casdoor { } Future refreshToken(String refreshToken, String? clientSecret, - {String scope = "read"}) async { + {String scope = 'read'}) async { return await http.post( Uri( scheme: parseScheme(), host: parseHost(), port: parsePort(), - path: "api/login/oauth/refresh_token", + path: 'api/login/oauth/refresh_token', ), body: { 'grant_type': 'refresh_token', @@ -126,19 +147,27 @@ class Casdoor { } Future tokenLogout( - String idTokenHint, String? postLogoutRedirectUri, String state) async { - return await http.post( + String idTokenHint, + String? postLogoutRedirectUri, + String state, { + bool clearCache = false, + }) async { + final http.Response resp = await http.post( Uri( scheme: parseScheme(), host: parseHost(), port: parsePort(), - path: "api/login/oauth/logout", + path: 'api/login/oauth/logout', ), body: { - 'id_token_hint ': idTokenHint, + 'id_token_hint': idTokenHint, 'post_logout_redirect_uri': postLogoutRedirectUri, - 'state ': state + 'state': state }); + if (clearCache == true) { + await CasdoorOauth.clearCache(); + } + return resp; } Future getUserInfo(String accessToken) async { @@ -147,25 +176,25 @@ class Casdoor { scheme: parseScheme(), host: parseHost(), port: parsePort(), - path: "api/userinfo", + path: 'api/userinfo', ), - headers: {"Authorization": "Bearer $accessToken"}, + headers: {'Authorization': 'Bearer $accessToken'}, ); } Map decodedToken(String token) { - Map decodedToken = JwtDecoder.decode(token); + final Map decodedToken = JwtDecoder.decode(token); return decodedToken; } bool isTokenExpired(String token) { - bool isTokenExpired = JwtDecoder.isExpired(token); + final bool isTokenExpired = JwtDecoder.isExpired(token); return isTokenExpired; } bool isNonce(String token) { - Map decodedToken = JwtDecoder.decode(token); - bool isNonce = decodedToken["nonce"] == nonce ? true : false; + final Map decodedToken = JwtDecoder.decode(token); + final bool isNonce = (decodedToken['nonce'] == nonce); return isNonce; } } @@ -181,7 +210,7 @@ String generateRandomString(int length) { } String generateCodeChallenge(String verifier) { - var bytes = utf8.encode(verifier); - var digest = sha256.convert(bytes); - return base64UrlEncode(digest.bytes).replaceAll("=", ""); + final bytes = utf8.encode(verifier); + final digest = sha256.convert(bytes); + return base64UrlEncode(digest.bytes).replaceAll('=', ''); } diff --git a/lib/src/casdoor_flutter_sdk.dart b/lib/src/casdoor_flutter_sdk.dart index cac4dcf..b5b7d35 100644 --- a/lib/src/casdoor_flutter_sdk.dart +++ b/lib/src/casdoor_flutter_sdk.dart @@ -9,6 +9,6 @@ import 'casdoor_flutter_sdk_platform_interface.dart'; class CasdoorFlutterSdk { Future getPlatformVersion() { - return CasdoorFlutterSdkPlatform.instance.getPlatformVersion(); + return CasdoorFlutterSdkPlatform().getPlatformVersion(); } } diff --git a/lib/src/casdoor_flutter_sdk_method_channel.dart b/lib/src/casdoor_flutter_sdk_method_channel.dart index bf6f317..02df5de 100644 --- a/lib/src/casdoor_flutter_sdk_method_channel.dart +++ b/lib/src/casdoor_flutter_sdk_method_channel.dart @@ -12,14 +12,33 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'casdoor_flutter_sdk_platform_interface.dart'; +import 'package:casdoor_flutter_sdk/casdoor_flutter_sdk.dart'; +import 'package:flutter/services.dart'; /// An implementation of [CasdoorFlutterSdkPlatform] that uses method channels. class MethodChannelCasdoorFlutterSdk extends CasdoorFlutterSdkPlatform { + MethodChannelCasdoorFlutterSdk() : super.create(); + + static const MethodChannel _channel = MethodChannel('casdoor_flutter_sdk'); + + @override + Future clearCache() async { + return await _channel + .invokeMethod('clearCache') + .catchError((err) => throw (Exception(err))) as bool; + } + + @override + Future authenticate(CasdoorSdkParams params) async { + return await _channel.invokeMethod('authenticate', { + 'params': params, + }).catchError((err) => throw (Exception(err))) as String; + } + @override - Future getPlatformVersion() async { + Future getPlatformVersion() async { final version = - await getMethodChannel().invokeMethod('getPlatformVersion'); + await _channel.invokeMethod('getPlatformVersion') ?? ''; return version; } } diff --git a/lib/src/casdoor_flutter_sdk_oauth.dart b/lib/src/casdoor_flutter_sdk_oauth.dart index 94eb51f..13ef105 100644 --- a/lib/src/casdoor_flutter_sdk_oauth.dart +++ b/lib/src/casdoor_flutter_sdk_oauth.dart @@ -12,55 +12,28 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'package:flutter/cupertino.dart'; -import 'package:flutter/services.dart'; import 'dart:async'; -import 'casdoor_flutter_sdk_platform_interface.dart'; -class _OnAppLifecycleResumeObserver extends WidgetsBindingObserver { - final Function onResumed; - - _OnAppLifecycleResumeObserver(this.onResumed); - - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - if (state == AppLifecycleState.resumed) { - onResumed(); - } - } -} +import 'package:casdoor_flutter_sdk/casdoor_flutter_sdk.dart'; class CasdoorOauth { - static final MethodChannel _channel = - CasdoorFlutterSdkPlatform.instance.getMethodChannel(); - Future getPlatformVersion() { - return CasdoorFlutterSdkPlatform.instance.getPlatformVersion(); + return CasdoorFlutterSdkPlatform().getPlatformVersion(); } - static final _OnAppLifecycleResumeObserver _resumedObserver = - _OnAppLifecycleResumeObserver(() { - _cleanUpDanglingCalls(); // unawaited - }); - static Future authenticate( - {required String url, - required String callbackUrlScheme, - bool? preferEphemeral}) async { - WidgetsBinding.instance.removeObserver( - _resumedObserver); // safety measure so we never add this observer twice - WidgetsBinding.instance.addObserver(_resumedObserver); - return await _channel.invokeMethod('authenticate', { - 'url': url, - 'callbackUrlScheme': callbackUrlScheme, - 'preferEphemeral': preferEphemeral ?? false, - }) as String; + static Future authenticate(CasdoorSdkParams params) async { + try { + return CasdoorFlutterSdkPlatform().authenticate(params); + } catch (_) { + rethrow; + } } - /// On Android, the plugin has to store the Result callbacks in order to pass the result back to the caller of - /// `authenticate`. But if that result never comes the callback will dangle around forever. This can be called to - /// terminate all `authenticate` calls with an error. - static Future _cleanUpDanglingCalls() async { - await _channel.invokeMethod('cleanUpDanglingCalls'); - WidgetsBinding.instance.removeObserver(_resumedObserver); + static Future clearCache() async { + try { + return CasdoorFlutterSdkPlatform().clearCache(); + } catch (_) { + rethrow; + } } } diff --git a/lib/src/casdoor_flutter_sdk_platform_interface.dart b/lib/src/casdoor_flutter_sdk_platform_interface.dart index 63e82a9..8032dfc 100644 --- a/lib/src/casdoor_flutter_sdk_platform_interface.dart +++ b/lib/src/casdoor_flutter_sdk_platform_interface.dart @@ -12,24 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'package:flutter/services.dart'; +import 'package:casdoor_flutter_sdk/casdoor_flutter_sdk.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'casdoor_flutter_sdk_method_channel.dart'; abstract class CasdoorFlutterSdkPlatform extends PlatformInterface { - /// Constructs a CasdoorFlutterSdkPlatform. - CasdoorFlutterSdkPlatform() : super(token: _token); + // Returns singleton instance. + factory CasdoorFlutterSdkPlatform() => _instance; - static final Object _token = Object(); + CasdoorFlutterSdkPlatform.create() : super(token: _token); static CasdoorFlutterSdkPlatform _instance = MethodChannelCasdoorFlutterSdk(); - /// The default instance of [CasdoorFlutterSdkPlatform] to use. - /// - /// Defaults to [MethodChannelCasdoorFlutterSdk]. - static CasdoorFlutterSdkPlatform get instance => _instance; - /// Platform-specific implementations should set this with their own /// platform-specific class that extends [CasdoorFlutterSdkPlatform] when /// they register themselves. @@ -38,11 +33,17 @@ abstract class CasdoorFlutterSdkPlatform extends PlatformInterface { _instance = instance; } - MethodChannel getMethodChannel() { - return const MethodChannel('casdoor_flutter_sdk'); - } + static final Object _token = Object(); - Future getPlatformVersion() { + Future getPlatformVersion() { throw UnimplementedError('platformVersion() has not been implemented.'); } + + Future clearCache() { + throw UnimplementedError('clearCache() has not been implemented.'); + } + + Future authenticate(CasdoorSdkParams params) { + throw UnimplementedError('authenticate() has not been implemented.'); + } } diff --git a/lib/src/casdoor_flutter_sdk_web.dart b/lib/src/casdoor_flutter_sdk_web.dart index 0554706..4e50f38 100644 --- a/lib/src/casdoor_flutter_sdk_web.dart +++ b/lib/src/casdoor_flutter_sdk_web.dart @@ -17,37 +17,20 @@ import 'dart:convert'; import 'dart:html'; import 'dart:js'; +import 'package:casdoor_flutter_sdk/casdoor_flutter_sdk.dart'; import 'package:flutter/services.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; -import 'casdoor_flutter_sdk_platform_interface.dart'; - class CasdoorFlutterSdkWeb extends CasdoorFlutterSdkPlatform { - static void registerWith(Registrar registrar) { - final MethodChannel channel = MethodChannel( - 'casdoor_flutter_sdk', const StandardMethodCodec(), registrar); - final CasdoorFlutterSdkWeb instance = CasdoorFlutterSdkWeb(); - channel.setMethodCallHandler(instance.handleMethodCall); - CasdoorFlutterSdkPlatform.instance = instance; - } + CasdoorFlutterSdkWeb() : super.create(); - Future handleMethodCall(MethodCall call) async { - switch (call.method) { - case 'authenticate': - final String url = call.arguments['url']; - return _authenticate(url); - case 'getPlatformVersion': - return await getPlatformVersion(); - default: - throw PlatformException( - code: 'Unimplemented', - details: "The flutter_web_auth plugin for web doesn't implement " - "the method '${call.method}'"); - } + static void registerWith(Registrar registrar) { + CasdoorFlutterSdkPlatform.instance = CasdoorFlutterSdkWeb(); } - static Future _authenticate(String url) async { - context.callMethod('open', [url]); + @override + Future authenticate(CasdoorSdkParams params) async { + context.callMethod('open', [params.url]); await for (MessageEvent messageEvent in window.onMessage) { if (messageEvent.origin == Uri.base.origin) { final flutterWebAuthMessage = messageEvent.data['casdoor-auth']; @@ -55,14 +38,17 @@ class CasdoorFlutterSdkWeb extends CasdoorFlutterSdkPlatform { return flutterWebAuthMessage; } } - var appleOrigin = Uri(scheme: 'https', host: 'appleid.apple.com'); + final appleOrigin = Uri(scheme: 'https', host: 'appleid.apple.com'); if (messageEvent.origin == appleOrigin.toString()) { try { - Map data = jsonDecode(messageEvent.data); + final Map data = + jsonDecode(messageEvent.data as String) as Map; if (data['method'] == 'oauthDone') { final appleAuth = data['data']['authorization']; if (appleAuth != null) { - final appleAuthQuery = Uri(queryParameters: appleAuth).query; + final appleAuthQuery = + Uri(queryParameters: appleAuth as Map?) + .query; return appleOrigin.replace(fragment: appleAuthQuery).toString(); } } @@ -74,7 +60,7 @@ class CasdoorFlutterSdkWeb extends CasdoorFlutterSdkPlatform { } @override - Future getPlatformVersion() async { - return "web"; + Future getPlatformVersion() async { + return 'web'; } } From 9165518f8373b47fc8da79450d7f70235a32959b Mon Sep 17 00:00:00 2001 From: Mario Fischer Date: Sun, 15 Oct 2023 12:48:19 +0200 Subject: [PATCH 06/13] feat: add desktop plugin for windows and linux --- lib/src/casdoor_flutter_sdk_desktop.dart | 113 +++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 lib/src/casdoor_flutter_sdk_desktop.dart diff --git a/lib/src/casdoor_flutter_sdk_desktop.dart b/lib/src/casdoor_flutter_sdk_desktop.dart new file mode 100644 index 0000000..0ca9c52 --- /dev/null +++ b/lib/src/casdoor_flutter_sdk_desktop.dart @@ -0,0 +1,113 @@ +// Copyright 2022 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:async'; +import 'dart:io'; + +import 'package:casdoor_flutter_sdk/casdoor_flutter_sdk.dart'; +import 'package:desktop_webview_window/desktop_webview_window.dart'; +import 'package:path/path.dart' as p; +import 'package:path_provider/path_provider.dart'; + +class CasdoorFlutterSdkDesktop extends CasdoorFlutterSdkPlatform { + CasdoorFlutterSdkDesktop() : super.create(); + bool isWindowOpen = false; + + /// Registers this class as the default instance of [PathProviderPlatform] + static void registerWith() { + CasdoorFlutterSdkPlatform.instance = CasdoorFlutterSdkDesktop(); + } + + Future _getCacheDirWebPath() async { + final Directory cacheDirRoot = await getApplicationCacheDirectory(); + return p.join( + cacheDirRoot.path, + 'web_cache', + ); + } + + @override + Future clearCache() async { + if (isWindowOpen == true) { + return false; + } + + final String cacheDirWebPath = await _getCacheDirWebPath(); + final Directory cacheDirWeb = Directory(cacheDirWebPath); + if (cacheDirWeb.existsSync()) { + await cacheDirWeb.delete(recursive: true); + } + await cacheDirWeb.create(); + return true; + } + + @override + Future authenticate(CasdoorSdkParams params) async { + final bool isWebviewAvailable = await WebviewWindow.isWebviewAvailable(); + + if (isWindowOpen == true) { + throw CasdoorDesktopWebViewAlreadyOpenException; + } + + if (isWebviewAvailable == true) { + final Completer isWindowClosed = Completer(); + + String? returnUrl; + final String cacheDirWebPath = await _getCacheDirWebPath(); + + final webview = await WebviewWindow.create( + configuration: CreateConfiguration( + windowWidth: 400, + windowHeight: 640, + title: 'Login', + titleBarTopPadding: Platform.isMacOS ? 30 : 0, + titleBarHeight: 0, + userDataFolderWindows: cacheDirWebPath, + ), + ); + webview + ..launch(params.url) + ..addOnUrlRequestCallback((requestUrl) { + final uri = Uri.parse(requestUrl); + if (uri.scheme == params.callbackUrlScheme) { + returnUrl = requestUrl; + webview.close(); + isWindowClosed.complete(returnUrl); + } + }) + ..onClose.whenComplete(() { + if (returnUrl != null) { + if (isWindowClosed.isCompleted == false) { + isWindowClosed.complete(returnUrl); + } + } else { + isWindowClosed.completeError(CasdoorAuthCancelledException); + } + + isWindowOpen = false; + }); + + isWindowOpen = true; + + return isWindowClosed.future; + } else { + throw CasdoorDesktopWebViewNotAvailableException; + } + } + + @override + Future getPlatformVersion() async { + return 'desktop'; + } +} From 1c2d8c84f9cc4f4b84819d247a122eeb2d839798 Mon Sep 17 00:00:00 2001 From: Mario Fischer Date: Sun, 15 Oct 2023 12:49:00 +0200 Subject: [PATCH 07/13] feat: add mobile plugin for android, ios and macos --- lib/src/casdoor_flutter_sdk_mobile.dart | 288 ++++++++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 lib/src/casdoor_flutter_sdk_mobile.dart diff --git a/lib/src/casdoor_flutter_sdk_mobile.dart b/lib/src/casdoor_flutter_sdk_mobile.dart new file mode 100644 index 0000000..855a2fd --- /dev/null +++ b/lib/src/casdoor_flutter_sdk_mobile.dart @@ -0,0 +1,288 @@ +// Copyright 2022 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:async'; +import 'dart:collection'; + +import 'package:casdoor_flutter_sdk/casdoor_flutter_sdk.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_inappwebview/flutter_inappwebview.dart'; + +class InAppAuthBrowser extends InAppBrowser { + InAppAuthBrowser({ + int? windowId, + UnmodifiableListView? initialUserScripts, + }) : super(windowId: windowId, initialUserScripts: initialUserScripts); + + Function? onExitCallback; + Future Function(WebUri? url)? + onShouldOverrideUrlLoadingCallback; + + void setOnExitCallback(Function cb) => (onExitCallback = cb); + + void setOnShouldOverrideUrlLoadingCallback( + Future Function(WebUri? url) cb) => + onShouldOverrideUrlLoadingCallback = cb; + + @override + void onExit() { + if (onExitCallback != null) { + onExitCallback!(); + } + } + + @override + Future shouldOverrideUrlLoading( + NavigationAction navigationAction) async { + if (onShouldOverrideUrlLoadingCallback != null) { + return onShouldOverrideUrlLoadingCallback!(navigationAction.request.url); + } + + return NavigationActionPolicy.ALLOW; + } +} + +// ----------------------------------------------------------------------------- + +class FullScreenAuthPage extends StatefulWidget { + const FullScreenAuthPage({ + super.key, + required this.params, + }); + + final CasdoorSdkParams params; + + @override + State createState() => _FullScreenAuthPageState(); +} + +class _FullScreenAuthPageState extends State { + double progress = 0; + + Widget webViewWidget(BuildContext ctx) { + return Stack( + children: [ + InAppWebView( + initialUrlRequest: URLRequest(url: WebUri(widget.params.url)), + initialSettings: InAppWebViewSettings( + clearCache: widget.params.clearCache, + clearSessionCache: widget.params.clearCache, + ), + shouldOverrideUrlLoading: (controller, navigationAction) async { + final uri = navigationAction.request.url!; + + if (uri.scheme == widget.params.callbackUrlScheme) { + Navigator.pop(ctx, uri.rawValue); + return NavigationActionPolicy.CANCEL; + } + + return NavigationActionPolicy.ALLOW; + }, + onProgressChanged: (controller, progress) { + setState(() { + this.progress = progress / 100; + }); + }, + ), + progress < 1.0 + ? LinearProgressIndicator( + value: progress, + backgroundColor: Colors.grey.shade300, + ) + : Container(), + ], + ); + } + + Widget materialAuthWidget(BuildContext ctx) { + return Scaffold( + appBar: AppBar( + iconTheme: const IconThemeData(color: Colors.black), + centerTitle: false, + backgroundColor: Colors.grey.shade300, + title: const Text( + 'Login', + style: TextStyle(color: Colors.black), + ), + ), + body: webViewWidget(ctx), + ); + } + + Widget cupertinoAuthWidget(BuildContext ctx) { + return CupertinoPageScaffold( + navigationBar: CupertinoNavigationBar( + leading: CupertinoNavigationBarBackButton( + onPressed: () { + Navigator.pop(ctx); + }, + ), + middle: const Text('Login'), + ), + child: webViewWidget(ctx), + ); + } + + @override + Widget build(BuildContext context) { + WebView.debugLoggingSettings.enabled = false; + return (widget.params.isMaterialStyle ?? true) + ? materialAuthWidget(context) + : cupertinoAuthWidget(context); + } +} + +// ----------------------------------------------------------------------------- + +class CasdoorFlutterSdkMobile extends CasdoorFlutterSdkPlatform { + CasdoorFlutterSdkMobile() : super.create(); + + // FixMe: session to find out active browser + WebAuthenticationSession? session; + final InAppAuthBrowser browser = InAppAuthBrowser(); + bool willClearCache = false; + + /// Registers this class as the default instance of [PathProviderPlatform] + static void registerWith() { + CasdoorFlutterSdkPlatform.instance = CasdoorFlutterSdkMobile(); + } + + @override + Future clearCache() async { + willClearCache = true; + return true; + } + + Future _fullScreenAuth(CasdoorSdkParams params) async { + final result = await Navigator.push( + params.buildContext!, + MaterialPageRoute( + builder: (BuildContext ctx) => FullScreenAuthPage( + params: params, + ), + ), + ); + + //debugPrint('Result: $result'); + if (result is String) { + return result; + } + + throw CasdoorAuthCancelledException; + } + + Future _inAppBrowserAuth(CasdoorSdkParams params) async { + final Completer isFinished = Completer(); + + browser.setOnExitCallback(() { + if (!isFinished.isCompleted) { + isFinished.completeError(CasdoorAuthCancelledException); + } + }); + + browser.setOnShouldOverrideUrlLoadingCallback((returnUrl) async { + if (returnUrl != null) { + if (returnUrl.scheme == params.callbackUrlScheme) { + isFinished.complete(returnUrl.rawValue); + browser.close(); + return NavigationActionPolicy.CANCEL; + } + } + return NavigationActionPolicy.ALLOW; + }); + + await browser.openUrlRequest( + urlRequest: URLRequest(url: WebUri(params.url)), + settings: InAppBrowserClassSettings( + browserSettings: InAppBrowserSettings( + hideUrlBar: true, + hideDefaultMenuItems: true, + toolbarTopFixedTitle: 'Login', + ), + webViewSettings: InAppWebViewSettings( + useShouldOverrideUrlLoading: true, + useOnLoadResource: true, + clearCache: params.clearCache, + clearSessionCache: params.clearCache, + ), + ), + ); + + return isFinished.future; + } + + Future _webAuthSession(CasdoorSdkParams params) async { + if ((session == null) && (await WebAuthenticationSession.isAvailable())) { + bool hasStarted = false; + final Completer isFinished = Completer(); + + session = await WebAuthenticationSession.create( + url: WebUri(params.url), + callbackURLScheme: params.callbackUrlScheme, + initialSettings: WebAuthenticationSessionSettings( + prefersEphemeralWebBrowserSession: params.preferEphemeral, + ), + onComplete: (returnUrl, error) async { + if (returnUrl != null) { + isFinished.complete(returnUrl.rawValue); + } + await session?.dispose(); + session = null; + if (!isFinished.isCompleted) { + isFinished.completeError(CasdoorAuthCancelledException); + } + }, + ); + + if (await session?.canStart() ?? false) { + hasStarted = await session?.start() ?? false; + } + if (!hasStarted) { + throw CasdoorMobileWebAuthSessionFailedException; + } + + return isFinished.future; + } else { + throw CasdoorMobileWebAuthSessionNotAvailableException; + } + } + + @override + Future authenticate(CasdoorSdkParams params) async { + final CasdoorSdkParams newParams = + params.copyWith(clearCache: willClearCache); + + if (newParams.clearCache == true) { + willClearCache = false; + } + + if (([TargetPlatform.android, TargetPlatform.iOS] + .contains(defaultTargetPlatform)) && + (params.showFullscreen == true)) { + return _fullScreenAuth(newParams); + } else if ((defaultTargetPlatform == TargetPlatform.android) && + (params.showFullscreen != true)) { + return _inAppBrowserAuth(newParams); + } else { + return _webAuthSession(newParams); + } + } + + @override + Future getPlatformVersion() async { + return 'mobile'; + } +} From 074f80c82441d63449074c9945d41223bac62bf3 Mon Sep 17 00:00:00 2001 From: Mario Fischer Date: Sun, 15 Oct 2023 12:50:08 +0200 Subject: [PATCH 08/13] fix: add exceptions to library import file --- lib/casdoor_flutter_sdk.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/casdoor_flutter_sdk.dart b/lib/casdoor_flutter_sdk.dart index 1c92889..d6ad608 100644 --- a/lib/casdoor_flutter_sdk.dart +++ b/lib/casdoor_flutter_sdk.dart @@ -1,3 +1,4 @@ export 'src/casdoor.dart'; export 'src/casdoor_flutter_sdk_config.dart'; +export 'src/casdoor_flutter_sdk_exceptions.dart'; export 'src/casdoor_flutter_sdk_platform_interface.dart'; From b798d9db53c56edc4afc44c08678a53bffd2465f Mon Sep 17 00:00:00 2001 From: Mario Fischer Date: Sun, 15 Oct 2023 12:50:38 +0200 Subject: [PATCH 09/13] feat: update dependencies --- pubspec.yaml | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 6b8f4e0..f598b59 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ version: 1.3.0 homepage: https://github.com/casdoor/casdoor-flutter-sdk environment: - sdk: ">=2.14.5 <4.0.0" + sdk: ">=2.17.0 <4.0.0" flutter: ">=2.8.0" dependencies: @@ -13,6 +13,11 @@ dependencies: flutter_web_plugins: sdk: flutter plugin_platform_interface: ^2.0.2 + + path: ^1.8.2 + path_provider: ^2.1.1 + desktop_webview_window: ^0.2.3 + flutter_inappwebview: ^6.0.0-beta.25 http: ^0.13.4 crypto: ^3.0.2 jwt_decoder: ^2.0.1 @@ -20,7 +25,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.4 + flutter_lints: ^2.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec @@ -40,15 +45,18 @@ flutter: plugin: platforms: android: - package: com.example.casdoor_flutter_sdk - pluginClass: CasdoorFlutterSdkPlugin + dartPluginClass: CasdoorFlutterSdkMobile ios: - pluginClass: CasdoorFlutterSdkPlugin + dartPluginClass: CasdoorFlutterSdkMobile + linux: + dartPluginClass: CasdoorFlutterSdkDesktop macos: - pluginClass: CasdoorFlutterSdkPlugin + dartPluginClass: CasdoorFlutterSdkMobile + windows: + dartPluginClass: CasdoorFlutterSdkDesktop web: pluginClass: CasdoorFlutterSdkWeb - fileName: casdoor_flutter_sdk_web.dart + fileName: src/casdoor_flutter_sdk_web.dart # To add assets to your plugin package, add an assets section, like this: # assets: From eef31715eba4f7a6b7c8d71119c2e69100bb2b38 Mon Sep 17 00:00:00 2001 From: Mario Fischer Date: Sun, 15 Oct 2023 12:51:15 +0200 Subject: [PATCH 10/13] feat: add exception classes --- lib/src/casdoor_flutter_sdk_exceptions.dart | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 lib/src/casdoor_flutter_sdk_exceptions.dart diff --git a/lib/src/casdoor_flutter_sdk_exceptions.dart b/lib/src/casdoor_flutter_sdk_exceptions.dart new file mode 100644 index 0000000..d378efd --- /dev/null +++ b/lib/src/casdoor_flutter_sdk_exceptions.dart @@ -0,0 +1,23 @@ +// Copyright 2022 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +class CasdoorAuthCancelledException implements Exception {} + +class CasdoorDesktopWebViewNotAvailableException implements Exception {} + +class CasdoorDesktopWebViewAlreadyOpenException implements Exception {} + +class CasdoorMobileWebAuthSessionNotAvailableException implements Exception {} + +class CasdoorMobileWebAuthSessionFailedException implements Exception {} From 9d2dd030f12fbeaad427616a1af2f7e615bc2893 Mon Sep 17 00:00:00 2001 From: Mario Fischer Date: Sun, 15 Oct 2023 12:52:23 +0200 Subject: [PATCH 11/13] fix: update testing stubs --- ...sdoor_flutter_sdk_method_channel_test.dart | 5 ++-- test/casdoor_flutter_sdk_test.dart | 26 +++++++++++-------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/test/casdoor_flutter_sdk_method_channel_test.dart b/test/casdoor_flutter_sdk_method_channel_test.dart index ad3aef4..d385582 100644 --- a/test/casdoor_flutter_sdk_method_channel_test.dart +++ b/test/casdoor_flutter_sdk_method_channel_test.dart @@ -12,12 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +import 'package:casdoor_flutter_sdk/src/casdoor_flutter_sdk_method_channel.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:casdoor_flutter_sdk/casdoor_flutter_sdk_method_channel.dart'; void main() { - MethodChannelCasdoorFlutterSdk platform = MethodChannelCasdoorFlutterSdk(); + final MethodChannelCasdoorFlutterSdk platform = + MethodChannelCasdoorFlutterSdk(); const MethodChannel channel = MethodChannel('casdoor_flutter_sdk'); TestWidgetsFlutterBinding.ensureInitialized(); diff --git a/test/casdoor_flutter_sdk_test.dart b/test/casdoor_flutter_sdk_test.dart index d0ba0ca..efeec90 100644 --- a/test/casdoor_flutter_sdk_test.dart +++ b/test/casdoor_flutter_sdk_test.dart @@ -12,37 +12,41 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'package:flutter/src/services/platform_channel.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:casdoor_flutter_sdk/casdoor_flutter_sdk_platform_interface.dart'; import 'package:casdoor_flutter_sdk/casdoor_flutter_sdk.dart'; -import 'package:casdoor_flutter_sdk/casdoor_flutter_sdk_method_channel.dart'; +import 'package:casdoor_flutter_sdk/src/casdoor_flutter_sdk.dart'; +import 'package:casdoor_flutter_sdk/src/casdoor_flutter_sdk_method_channel.dart'; +import 'package:flutter_test/flutter_test.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; class MockCasdoorFlutterSdkPlatform with MockPlatformInterfaceMixin implements CasdoorFlutterSdkPlatform { @override - Future getPlatformVersion() => Future.value('42'); + Future getPlatformVersion() => Future.value('42'); + + @override + Future clearCache() { + // TODO: implement clearCache + throw UnimplementedError(); + } @override - MethodChannel getMethodChannel() { - // TODO: implement getMethodChannel + Future authenticate(CasdoorSdkParams params) { + // TODO: implement authenticate throw UnimplementedError(); } } void main() { - final CasdoorFlutterSdkPlatform initialPlatform = - CasdoorFlutterSdkPlatform.instance; + final CasdoorFlutterSdkPlatform initialPlatform = CasdoorFlutterSdkPlatform(); test('$MethodChannelCasdoorFlutterSdk is the default instance', () { expect(initialPlatform, isInstanceOf()); }); test('getPlatformVersion', () async { - CasdoorFlutterSdk casdoorFlutterSdkPlugin = CasdoorFlutterSdk(); - MockCasdoorFlutterSdkPlatform fakePlatform = + final CasdoorFlutterSdk casdoorFlutterSdkPlugin = CasdoorFlutterSdk(); + final MockCasdoorFlutterSdkPlatform fakePlatform = MockCasdoorFlutterSdkPlatform(); CasdoorFlutterSdkPlatform.instance = fakePlatform; From 419e7dc01b8e25c9803f05a658509a4e0ff3738a Mon Sep 17 00:00:00 2001 From: Mario Fischer Date: Sun, 15 Oct 2023 13:08:28 +0200 Subject: [PATCH 12/13] fix: update readme --- README.md | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 48ef919..3de9422 100644 --- a/README.md +++ b/README.md @@ -103,24 +103,27 @@ dependencies: casdoor_flutter_sdk: ^1.0.0 ``` -Note here that for Android and Web +Notes for different platforms: + +## Android and iOS + +Please check the [documentation](https://inappwebview.dev/docs/intro) of the InAppWebView package for more details. ## Android -In order to capture the callback url, the following activity needs to be added to your AndroidManifest.xml. Be sure to relpace YOUR_CALLBACK_URL_SCHEME_HERE with your actual callback url scheme. +Increase the SDK version in `android/app/build.gradle` to 34: ``` - - - - - - - - +... +android { + compileSdkVersion 34 +... ``` +## Windows and Linux + +Please check the [documentation](https://pub.dev/packages/desktop_webview_window) of the desktop_webview_window package for more details. + ## Web On the Web platform an endpoint needs to be created that captures the callback URL and sends it to the application using the JavaScript postMessage() method. In the ./web folder of the project, create an HTML file with the name e.g. callback.html with content: @@ -157,12 +160,18 @@ getSignupUrl(enablePassword) getSigninUrl() ``` -#### Get code +#### Get code in a new window (all platforms) ```typescript show() ``` +#### Get code inside the app (Android and iOS) + +```typescript +showFullscreen() +``` + #### Get token ```typescript From 80ef7af209d7798738bddae7329750c2d7635de1 Mon Sep 17 00:00:00 2001 From: Mario Fischer Date: Sun, 15 Oct 2023 13:10:27 +0200 Subject: [PATCH 13/13] fix: add missing platforms to readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3de9422..18f592e 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,10 @@ The following platforms are supported: - Android - iOS +- Linux - macOS - Web +- Windows | **Android** | **iOS** | **Web** | | ------------------------------ | ---------------------- | ---------------------- |