diff --git a/auth0_flutter/EXAMPLES.md b/auth0_flutter/EXAMPLES.md index ada7e7e26..7300bd1db 100644 --- a/auth0_flutter/EXAMPLES.md +++ b/auth0_flutter/EXAMPLES.md @@ -443,6 +443,8 @@ final auth0 = Auth0('YOUR_AUTH0_DOMAIN', 'YOUR_AUTH0_CLIENT_ID', You can enable an additional level of user authentication before retrieving credentials using the local authentication supported by the device, for example PIN or fingerprint on Android, and Face ID or Touch ID on iOS. +To enable this, pass a `LocalAuthentication` instance when you create your `Auth0` object. + ```dart const localAuthentication = LocalAuthentication(title: 'Please authenticate to continue'); @@ -450,6 +452,7 @@ final auth0 = Auth0('YOUR_AUTH0_DOMAIN', 'YOUR_AUTH0_CLIENT_ID', localAuthentication: localAuthentication); final credentials = await auth0.credentialsManager.credentials(); ``` +> ⚠️ On Android, your app's MainActivity.kt file must extend FlutterFragmentActivity instead of FlutterActivity for biometric prompts to work. Check the [API documentation](https://pub.dev/documentation/auth0_flutter_platform_interface/latest/auth0_flutter_platform_interface/LocalAuthentication-class.html) to learn more about the available `LocalAuthentication` properties. @@ -490,10 +493,16 @@ The Credentials Manager will only throw `CredentialsManagerException` exceptions ```dart try { - final credentials = await auth0.credentialsManager.credentials(); - // ... +final credentials = await auth0.credentialsManager.credentials(); +// ... } on CredentialsManagerException catch (e) { - print(e); +if (e.isNoCredentialsFound) { +print("No credentials stored."); +} else if (e.isTokenRenewFailed) { +print("Failed to renew tokens."); +} else { +print(e); +} } ``` diff --git a/auth0_flutter/android/build.gradle b/auth0_flutter/android/build.gradle index 4d6a6b607..1b5b8415b 100644 --- a/auth0_flutter/android/build.gradle +++ b/auth0_flutter/android/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath "com.android.tools.build:gradle:8.4.0" + classpath "com.android.tools.build:gradle:8.3.0" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -31,18 +31,18 @@ rootProject.allprojects { } android { - compileSdk 34 + compileSdk 35 if (project.android.hasProperty("namespace")) { namespace libApplicationId } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '11' } sourceSets { @@ -51,9 +51,10 @@ android { } defaultConfig { - minSdkVersion 21 + minSdkVersion 24 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - manifestPlaceholders = [auth0Domain: "test-domain", auth0Scheme: "test"] + manifestPlaceholders = [auth0Domain: "dev-z0xy0f8x5xj51m2q.us.auth0.com", auth0Scheme: "https"] + consumerProguardFiles '../proguard/proguard-gson.pro', '../proguard/proguard-okio.pro', '../proguard/proguard-jetpack.pro' } buildTypes { @@ -71,15 +72,20 @@ android { } dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - //noinspection GradleDynamicVersion - implementation 'com.auth0.android:auth0:2.11.0' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + implementation 'com.auth0.android:auth0:3.9.0' + implementation "androidx.biometric:biometric:1.1.0" + implementation 'com.squareup.okhttp3:okhttp:4.12.0' + implementation 'androidx.browser:browser:1.4.0' + implementation 'androidx.core:core-ktx:1.6.0' + implementation 'androidx.appcompat:appcompat:1.6.0' + implementation 'com.google.androidbrowserhelper:androidbrowserhelper:2.4.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.hamcrest:java-hamcrest:2.0.0.0' testImplementation "org.mockito.kotlin:mockito-kotlin:4.0.0" testImplementation 'com.jayway.awaitility:awaitility:1.7.0' - testImplementation 'org.robolectric:robolectric:4.6.1' + testImplementation 'org.robolectric:robolectric:4.8.1' testImplementation 'androidx.test.espresso:espresso-intents:3.5.1' testImplementation 'com.auth0:java-jwt:3.19.1' -} +} \ No newline at end of file diff --git a/auth0_flutter/android/gradle/wrapper/gradle-wrapper.properties b/auth0_flutter/android/gradle/wrapper/gradle-wrapper.properties index 17655d0ef..d951fac2b 100644 --- a/auth0_flutter/android/gradle/wrapper/gradle-wrapper.properties +++ b/auth0_flutter/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/Auth0FlutterPlugin.kt b/auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/Auth0FlutterPlugin.kt index 0673768c1..6edbc843e 100644 --- a/auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/Auth0FlutterPlugin.kt +++ b/auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/Auth0FlutterPlugin.kt @@ -73,8 +73,6 @@ class Auth0FlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { override fun onAttachedToActivity(binding: ActivityPluginBinding) { webAuthCallHandler.activity = binding.activity credentialsManagerCallHandler.activity = binding.activity - - binding.addActivityResultListener(credentialsManagerCallHandler) } override fun onDetachedFromActivityForConfigChanges() { diff --git a/auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/CredentialsManagerMethodCallHandler.kt b/auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/CredentialsManagerMethodCallHandler.kt index d2a36d726..e71607318 100644 --- a/auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/CredentialsManagerMethodCallHandler.kt +++ b/auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/CredentialsManagerMethodCallHandler.kt @@ -2,68 +2,70 @@ package com.auth0.auth0_flutter import android.app.Activity import android.content.Context -import android.content.Intent import androidx.annotation.NonNull -import com.auth0.android.authentication.AuthenticationAPIClient +import androidx.fragment.app.FragmentActivity +import com.auth0.android.authentication.storage.AuthenticationLevel +import com.auth0.android.authentication.storage.LocalAuthenticationOptions import com.auth0.android.authentication.storage.SecureCredentialsManager import com.auth0.android.authentication.storage.SharedPreferencesStorage import com.auth0.auth0_flutter.request_handlers.MethodCallRequest import com.auth0.auth0_flutter.request_handlers.credentials_manager.CredentialsManagerRequestHandler -import com.auth0.auth0_flutter.utils.RequestCodes import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result -import io.flutter.plugin.common.PluginRegistry -class CredentialsManagerMethodCallHandler(private val requestHandlers: List) : - MethodCallHandler, PluginRegistry.ActivityResultListener { +class CredentialsManagerMethodCallHandler(private val requestHandlers: List) : MethodCallHandler { lateinit var activity: Activity lateinit var context: Context - var credentialsManager: SecureCredentialsManager? = null - - override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { +override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { val requestHandler = requestHandlers.find { it.method == call.method } if (requestHandler != null) { val request = MethodCallRequest.fromCall(call) + val activity = this.activity - val configuration = - request.data["credentialsManagerConfiguration"] as Map<*, *>? + val configuration = request.data["credentialsManagerConfiguration"] as Map<*, *>? val sharedPreferenceConfiguration = configuration?.get("android") val sharedPreferenceName: String? = if (sharedPreferenceConfiguration != null) { - (sharedPreferenceConfiguration as Map).get("sharedPreferencesName") + (sharedPreferenceConfiguration as Map)["sharedPreferencesName"] } else null - val api = AuthenticationAPIClient(request.account) val storage = sharedPreferenceName?.let { SharedPreferencesStorage(context, it) } ?: SharedPreferencesStorage(context) - credentialsManager = - credentialsManager ?: SecureCredentialsManager(context, api, storage) - val credentialsManager = credentialsManager as SecureCredentialsManager - val localAuthentication = - request.data.get("localAuthentication") as Map? + val localAuthentication = request.data["localAuthentication"] as Map? + val credentialsManager: SecureCredentialsManager if (localAuthentication != null) { - val title = localAuthentication["title"] - val description = localAuthentication["description"] - credentialsManager.requireAuthentication( - activity, - RequestCodes.AUTH_REQ_CODE, - title, - description - ) + if (activity !is FragmentActivity) { + result.error( + "credentialsManager#biometric-error", + "The Activity is not a FragmentActivity, which is required for biometric authentication.", + null + ) + return + } + + val builder = LocalAuthenticationOptions.Builder() + localAuthentication["title"]?.let { builder.setTitle(it) } + localAuthentication["description"]?.let { builder.setDescription(it) } + localAuthentication["cancelTitle"]?.let { builder.setNegativeButtonText(it) } + + builder.setAuthenticationLevel(AuthenticationLevel.STRONG) + builder.setDeviceCredentialFallback(true) + + + credentialsManager = SecureCredentialsManager(context, request.account, storage, activity, builder.build()) + } else { + credentialsManager = SecureCredentialsManager(context, request.account, storage) } + requestHandler.handle(credentialsManager, context, request, result) } else { result.notImplemented() } } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { - return credentialsManager?.checkAuthenticationResult(requestCode, resultCode) ?: true - } } diff --git a/auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/MethodCallRequest.kt b/auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/MethodCallRequest.kt index a038569b2..3c9b386f3 100644 --- a/auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/MethodCallRequest.kt +++ b/auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/MethodCallRequest.kt @@ -31,7 +31,7 @@ class MethodCallRequest { ) val accountMap = args["_account"] as Map - val account = Auth0( + val account = Auth0.getInstance( accountMap["clientId"] as String, accountMap["domain"] as String ) diff --git a/auth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/Auth0FlutterPluginTest.kt b/auth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/Auth0FlutterPluginTest.kt index 5c095f892..f04dde68c 100644 --- a/auth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/Auth0FlutterPluginTest.kt +++ b/auth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/Auth0FlutterPluginTest.kt @@ -8,6 +8,7 @@ import io.flutter.plugin.common.MethodChannel import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.* +import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.mock import org.robolectric.RobolectricTestRunner @@ -32,7 +33,7 @@ class Auth0FlutterPluginTest { ) } - assertMethodcallHandler(0) + assertMethodcallHandler(0) assertMethodcallHandler(1) assertMethodcallHandler(2) @@ -89,7 +90,7 @@ class Auth0FlutterPluginTest { fun getHandler(i: Int): TMethodCallHandler { val captor = argumentCaptor() - verify(constructed[i]).setMethodCallHandler(captor.capture()) + verify(constructed[i], atLeastOnce()).setMethodCallHandler(captor.capture()) @Suppress("UNCHECKED_CAST") return captor.firstValue as TMethodCallHandler @@ -104,7 +105,7 @@ class Auth0FlutterPluginTest { } @Test - fun `should call binding addActivityResultListener for CredentialsManager on onAttachedToActivity`() { + fun `should NOT call binding addActivityResultListener on onAttachedToActivity`() { mockConstruction(MethodChannel::class.java).use { val plugin = Auth0FlutterPlugin() @@ -120,7 +121,7 @@ class Auth0FlutterPluginTest { plugin.onAttachedToActivity(mockActivityBindings) - verify(mockActivityBindings).addActivityResultListener( + verify(mockActivityBindings, never()).addActivityResultListener( any() ) } diff --git a/auth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/CredentialsManagerMethodCallHandlerTest.kt b/auth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/CredentialsManagerMethodCallHandlerTest.kt index fbcd4fbb9..34cc04a20 100644 --- a/auth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/CredentialsManagerMethodCallHandlerTest.kt +++ b/auth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/CredentialsManagerMethodCallHandlerTest.kt @@ -2,17 +2,20 @@ package com.auth0.auth0_flutter import android.app.Activity import android.content.Context -import android.content.SharedPreferences +import androidx.fragment.app.FragmentActivity +import com.auth0.android.Auth0 +import com.auth0.android.authentication.storage.AuthenticationLevel +import com.auth0.android.authentication.storage.LocalAuthenticationOptions +import com.auth0.android.authentication.storage.SecureCredentialsManager import com.auth0.auth0_flutter.request_handlers.credentials_manager.ClearCredentialsRequestHandler import com.auth0.auth0_flutter.request_handlers.credentials_manager.CredentialsManagerRequestHandler -import com.auth0.auth0_flutter.request_handlers.credentials_manager.HasValidCredentialsRequestHandler import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel.Result -import org.hamcrest.CoreMatchers -import org.hamcrest.MatcherAssert +import org.hamcrest.CoreMatchers.`is` +import org.hamcrest.MatcherAssert.assertThat import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mockito.`when` +import org.mockito.Mockito.mockConstruction import org.mockito.kotlin.* import org.robolectric.RobolectricTestRunner @@ -33,176 +36,88 @@ class CredentialsManagerMethodCallHandlerTest { method: String, arguments: HashMap = defaultArguments, requestHandlers: List, - activity: Activity? = null, - context: Context? = null, + activity: Activity, onResult: (Result) -> Unit, ) { val handler = CredentialsManagerMethodCallHandler(requestHandlers) val mockResult = mock() - handler.activity = if (activity === null) mock() else activity - handler.context = if (context === null) mock() else context - + handler.activity = activity + handler.context = mock() handler.onMethodCall(MethodCall(method, arguments), mockResult) onResult(mockResult) } @Test - fun `handler should result in 'notImplemented' if no handlers`() { - runCallHandler("credentialsManager#clearCredentials", requestHandlers = listOf()) { result -> - verify(result).notImplemented() - } - } - - @Test - fun `handler should result in 'notImplemented' if no matching handler`() { - val clearCredentialsHandler = mock() - - `when`(clearCredentialsHandler.method).thenReturn("credentialsManager#clearCredentials") - - runCallHandler("credentialsManager#saveCredentials", requestHandlers = listOf(clearCredentialsHandler)) { result -> - verify(result).notImplemented() - } - } - - @Test - fun `handler should not call credentialsManager requireAuthentication`() { + fun `handler should instantiate SecureCredentialsManager without biometrics`() { val clearCredentialsHandler = mock() - `when`(clearCredentialsHandler.method).thenReturn("credentialsManager#clearCredentials") - val activity: Activity = mock() - val context: Context = mock() - val mockPrefs: SharedPreferences = mock() - - `when`(context.getSharedPreferences(any(), any())) - .thenReturn(mockPrefs) - - val handler = CredentialsManagerMethodCallHandler(listOf(clearCredentialsHandler)) - val mockResult = mock() - - handler.activity = activity - handler.context = context - handler.credentialsManager = mock() - handler.onMethodCall(MethodCall(clearCredentialsHandler.method, defaultArguments), mockResult) - - verify(handler.credentialsManager, never())?.requireAuthentication(any(), any(), any(), any()) + mockConstruction(SecureCredentialsManager::class.java).use { + runCallHandler("credentialsManager#clearCredentials", activity = activity, requestHandlers = listOf(clearCredentialsHandler)) {} + + // Verify the simple constructor was called, without FragmentActivity or LocalAuthenticationOptions + val constructorInvocations = it.constructorInvocations() + assertThat(constructorInvocations.size, `is`(1)) + val constructorArgs = constructorInvocations[0].arguments() + assertThat(constructorArgs[0], isA(Context::class.java)) + assertThat(constructorArgs[1], isA(Auth0::class.java)) + assertThat(constructorArgs[2], isA(com.auth0.android.authentication.storage.Storage::class.java)) + assertThat(constructorArgs.size, `is`(3)) // Should only have 3 arguments + } } @Test - fun `handler should extract sharedPreferenceName correctly`() { + fun `handler should instantiate SecureCredentialsManager with biometrics`() { val clearCredentialsHandler = mock() - `when`(clearCredentialsHandler.method).thenReturn("credentialsManager#clearCredentials") - - val activity: Activity = mock() - val context: Context = mock() - val mockPrefs: SharedPreferences = mock() - - `when`(context.getSharedPreferences(any(), any())) - .thenReturn(mockPrefs) + val activity: FragmentActivity = mock() // Use FragmentActivity val arguments = defaultArguments + hashMapOf( - "credentialsManagerConfiguration" to mapOf( - "android" to mapOf("sharedPreferencesName" to "test_prefs") + "localAuthentication" to hashMapOf( + "title" to "Test Title", + "description" to "Test Description" ) ) - runCallHandler(clearCredentialsHandler.method, arguments as HashMap, listOf(clearCredentialsHandler), activity, context) { - verify(context).getSharedPreferences(eq("test_prefs"), any()) + mockConstruction(SecureCredentialsManager::class.java).use { + runCallHandler("credentialsManager#clearCredentials", arguments = arguments, activity = activity, requestHandlers = listOf(clearCredentialsHandler)) {} + + // Verify the complex constructor for biometrics was called + val constructorInvocations = it.constructorInvocations() + assertThat(constructorInvocations.size, `is`(1)) + val constructorArgs = constructorInvocations[0].arguments() + assertThat(constructorArgs[0], isA(Context::class.java)) + assertThat(constructorArgs[1], isA(Auth0::class.java)) + assertThat(constructorArgs[2], isA(com.auth0.android.authentication.storage.Storage::class.java)) + assertThat(constructorArgs[3], isA(FragmentActivity::class.java)) + assertThat(constructorArgs[4], isA(LocalAuthenticationOptions::class.java)) + + // Verify the options passed to the constructor + val localAuthOptions = constructorArgs[4] as LocalAuthenticationOptions + assertThat(localAuthOptions.title, `is`("Test Title")) + assertThat(localAuthOptions.description, `is`("Test Description")) + assertThat(localAuthOptions.authenticationLevel, `is`(AuthenticationLevel.STRONG)) } } @Test - fun `handler should call credentialsManager requireAuthentication`() { - val clearCredentialsHandler = mock() - - `when`(clearCredentialsHandler.method).thenReturn("credentialsManager#clearCredentials") - - val activity: Activity = mock() - val context: Context = mock() - val mockPrefs: SharedPreferences = mock() - - `when`(context.getSharedPreferences(any(), any())) - .thenReturn(mockPrefs) - - val handler = CredentialsManagerMethodCallHandler(listOf(clearCredentialsHandler)) - val mockResult = mock() - - handler.activity = activity - handler.context = context - handler.credentialsManager = mock() - - handler.onMethodCall(MethodCall(clearCredentialsHandler.method, defaultArguments + hashMapOf("localAuthentication" to hashMapOf("title" to "test", "description" to "test description"))), mockResult) - - verify(handler.credentialsManager)?.requireAuthentication(eq(activity), eq(111), eq("test"), eq("test description")) - } - - @Test - fun `handler should call credentialsManager requireAuthentication with default values`() { - val clearCredentialsHandler = mock() - - `when`(clearCredentialsHandler.method).thenReturn("credentialsManager#clearCredentials") - - val activity: Activity = mock() - val context: Context = mock() - val mockPrefs: SharedPreferences = mock() - - `when`(context.getSharedPreferences(any(), any())) - .thenReturn(mockPrefs) - - val handler = CredentialsManagerMethodCallHandler(listOf(clearCredentialsHandler)) - val mockResult = mock() - - handler.activity = activity - handler.context = context - handler.credentialsManager = mock() - - handler.onMethodCall(MethodCall(clearCredentialsHandler.method, defaultArguments + hashMapOf("localAuthentication" to hashMapOf())), mockResult) - - verify(handler.credentialsManager)?.requireAuthentication(eq(activity), eq(111), isNull(), isNull()) - } - - @Test - fun `handler should only run the correct handler`() { + fun `handler should throw error if biometrics are requested but activity is not FragmentActivity`() { val clearCredentialsHandler = mock() - val hasValidCredentialsHandler = mock() - `when`(clearCredentialsHandler.method).thenReturn("credentialsManager#clearCredentials") - `when`(hasValidCredentialsHandler.method).thenReturn("credentialsManager#hasValidCredentials") + val activity: Activity = mock() // Use standard Activity - val activity: Activity = mock() - val context: Context = mock() - val mockPrefs: SharedPreferences = mock() - - `when`(context.getSharedPreferences(any(), any())) - .thenReturn(mockPrefs) + val arguments = defaultArguments + hashMapOf( + "localAuthentication" to hashMapOf("title" to "Test Title") + ) - runCallHandler(clearCredentialsHandler.method, activity = activity, context = context, requestHandlers = listOf(clearCredentialsHandler, hasValidCredentialsHandler)) { - verify(clearCredentialsHandler).handle(any(), eq(context), any(), any()) - verify(hasValidCredentialsHandler, times(0)).handle(any(), eq(context), any(), any()) + runCallHandler("credentialsManager#clearCredentials", arguments = arguments, activity = activity, requestHandlers = listOf(clearCredentialsHandler)) { result -> + val codeCaptor = argumentCaptor() + val messageCaptor = argumentCaptor() + verify(result).error(codeCaptor.capture(), messageCaptor.capture(), isNull()) + assertThat(codeCaptor.firstValue, `is`("credentialsManager#biometric-error")) + assertThat(messageCaptor.firstValue, `is`("The Activity is not a FragmentActivity, which is required for biometric authentication.")) } } - - @Test - fun `should call checkAuthenticationResult in onActivityResult`() { - val handler = CredentialsManagerMethodCallHandler(listOf()) - - handler.credentialsManager = mock() - handler.onActivityResult(1, 2, null) - - verify(handler.credentialsManager)?.checkAuthenticationResult(1, 2) - } - - @Test - fun `should return true in onActivityResult when no credentialsManager`() { - val handler = CredentialsManagerMethodCallHandler(listOf()) - val result = handler.onActivityResult(1, 2, null) - - MatcherAssert.assertThat( - result, - CoreMatchers.equalTo(true) - ) - } } diff --git a/auth0_flutter/example/android/app/build.gradle b/auth0_flutter/example/android/app/build.gradle index 5efde64f1..78294176a 100644 --- a/auth0_flutter/example/android/app/build.gradle +++ b/auth0_flutter/example/android/app/build.gradle @@ -25,17 +25,18 @@ if (flutterVersionName == null) { } android { - compileSdk 34 + compileSdk 35 + if (project.android.hasProperty("namespace")) { namespace exampleAppApplicationId } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '11' } sourceSets { @@ -47,7 +48,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId exampleAppApplicationId minSdkVersion 24 - targetSdk 34 + targetSdk 35 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -93,6 +94,9 @@ flutter { dependencies { kover(project(":auth0_flutter")) + implementation "androidx.credentials:credentials-play-services-auth:1.3.0" + implementation "androidx.credentials:credentials:1.3.0" + implementation 'com.google.code.gson:gson:2.8.9' } koverReport { diff --git a/auth0_flutter/example/android/app/src/main/kotlin/com/auth0/auth0_flutter_example/MainActivity.kt b/auth0_flutter/example/android/app/src/main/kotlin/com/auth0/auth0_flutter_example/MainActivity.kt index 91f2b18b9..3726d88b8 100644 --- a/auth0_flutter/example/android/app/src/main/kotlin/com/auth0/auth0_flutter_example/MainActivity.kt +++ b/auth0_flutter/example/android/app/src/main/kotlin/com/auth0/auth0_flutter_example/MainActivity.kt @@ -1,6 +1,6 @@ package com.auth0.auth0_flutter_example -import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.android.FlutterFragmentActivity -class MainActivity: FlutterActivity() { +class MainActivity: FlutterFragmentActivity() { } diff --git a/auth0_flutter/example/android/gradle/wrapper/gradle-wrapper.properties b/auth0_flutter/example/android/gradle/wrapper/gradle-wrapper.properties index 052e0719f..d951fac2b 100644 --- a/auth0_flutter/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/auth0_flutter/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Fri Jan 03 12:46:32 IST 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/auth0_flutter/example/android/settings.gradle b/auth0_flutter/example/android/settings.gradle index bf2328f82..df37de8ab 100644 --- a/auth0_flutter/example/android/settings.gradle +++ b/auth0_flutter/example/android/settings.gradle @@ -18,8 +18,8 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "7.4.2" apply false + id "com.android.application" version "8.3.0" apply false id "org.jetbrains.kotlin.android" version "1.9.10" apply false } -include ':app' +include ':app' \ No newline at end of file