From e8a6f09f084bf2f8d5b0553676cb677bc2f435fa Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Thu, 5 Sep 2024 12:22:09 +0530 Subject: [PATCH 1/3] added safety checks around the scenario when intent bundle are null for authentication activity --- .../src/main/java/com/auth0/android/Auth0.kt | 6 +-- .../authentication/AuthenticationException.kt | 18 +++++-- .../provider/AuthenticationActivity.kt | 50 +++++++++++++------ 3 files changed, 54 insertions(+), 20 deletions(-) diff --git a/auth0/src/main/java/com/auth0/android/Auth0.kt b/auth0/src/main/java/com/auth0/android/Auth0.kt index 38cc8483..8bb93c88 100755 --- a/auth0/src/main/java/com/auth0/android/Auth0.kt +++ b/auth0/src/main/java/com/auth0/android/Auth0.kt @@ -72,7 +72,7 @@ public open class Auth0 private constructor( * @return Url to call to perform the web flow of OAuth */ public open val authorizeUrl: String - get() = domainUrl!!.newBuilder() + get() = domainUrl.newBuilder() .addEncodedPathSegment("authorize") .build() .toString() @@ -83,7 +83,7 @@ public open class Auth0 private constructor( * @return Url to call to perform the web logout */ public open val logoutUrl: String - get() = domainUrl!!.newBuilder() + get() = domainUrl.newBuilder() .addEncodedPathSegment("v2") .addEncodedPathSegment("logout") .build() @@ -134,7 +134,7 @@ public open class Auth0 private constructor( ): Auth0 { val domainUrl = ensureValidUrl(domain) requireNotNull(domainUrl) { String.format("Invalid domain url: '%s'", domain) } - if (instance == null || instance!!.clientId != clientId || instance!!.domainUrl.host != domainUrl.host || instance!!.configurationDomain != configurationDomain) { + if (instance == null || instance?.clientId != clientId || instance?.domainUrl?.host != domainUrl.host || instance?.configurationDomain != configurationDomain) { instance = Auth0(clientId, domainUrl, configurationDomain) } return instance!! diff --git a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationException.kt b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationException.kt index c0ec9041..a24a8571 100644 --- a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationException.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationException.kt @@ -26,7 +26,10 @@ public class AuthenticationException : Auth0Exception { public constructor(message: String, cause: Exception? = null) : super(message, cause) - public constructor(code: String, description: String, cause: Exception) : this(DEFAULT_MESSAGE, cause) { + public constructor(code: String, description: String, cause: Exception) : this( + DEFAULT_MESSAGE, + cause + ) { this.code = code this.description = description } @@ -126,7 +129,10 @@ public class AuthenticationException : Auth0Exception { get() = "a0.invalid_configuration" == code // When a user closes the browser app and in turn, cancels the authentication - @Deprecated("This property can refer to both log in and log out actions.", replaceWith = ReplaceWith("isCanceled")) + @Deprecated( + "This property can refer to both log in and log out actions.", + replaceWith = ReplaceWith("isCanceled") + ) public val isAuthenticationCanceled: Boolean get() = isCanceled @@ -183,7 +189,7 @@ public class AuthenticationException : Auth0Exception { /// When authenticating with web-based authentication using prompt=none and the auth0 session had expired public val isLoginRequired: Boolean get() = "login_required" == code - + /// User is deleted public val isRefreshTokenDeleted: Boolean get() = "invalid_grant" == code @@ -205,6 +211,12 @@ public class AuthenticationException : Auth0Exception { internal companion object { internal const val ERROR_VALUE_AUTHENTICATION_CANCELED = "a0.authentication_canceled" + internal const val ERROR_KEY_URI_NULL = "a0.auth.authorize_uri" + internal const val ERROR_VALUE_AUTHORIZE_URI_INVALID = + "Authorization URI is received as null from the intent" + internal const val ERROR_KEY_CT_OPTIONS_NULL = "a0.auth.ct_options" + internal const val ERROR_VALUE_CT_OPTIONS_INVALID = + "Custom tab options are received as null from the intent" private const val ERROR_KEY = "error" private const val CODE_KEY = "code" private const val DESCRIPTION_KEY = "description" diff --git a/auth0/src/main/java/com/auth0/android/provider/AuthenticationActivity.kt b/auth0/src/main/java/com/auth0/android/provider/AuthenticationActivity.kt index 942065c6..6d1c5641 100644 --- a/auth0/src/main/java/com/auth0/android/provider/AuthenticationActivity.kt +++ b/auth0/src/main/java/com/auth0/android/provider/AuthenticationActivity.kt @@ -7,6 +7,10 @@ import android.net.Uri import android.os.Bundle import androidx.annotation.VisibleForTesting import com.auth0.android.authentication.AuthenticationException +import com.auth0.android.authentication.AuthenticationException.Companion.ERROR_KEY_CT_OPTIONS_NULL +import com.auth0.android.authentication.AuthenticationException.Companion.ERROR_KEY_URI_NULL +import com.auth0.android.authentication.AuthenticationException.Companion.ERROR_VALUE_AUTHORIZE_URI_INVALID +import com.auth0.android.authentication.AuthenticationException.Companion.ERROR_VALUE_CT_OPTIONS_INVALID import com.auth0.android.callback.RunnableTask import com.auth0.android.provider.WebAuthProvider.failure import com.auth0.android.provider.WebAuthProvider.resume @@ -68,23 +72,44 @@ public open class AuthenticationActivity : Activity() { } private fun launchAuthenticationIntent() { - val extras = intent.extras - val authorizeUri = extras!!.getParcelable(EXTRA_AUTHORIZE_URI) - val customTabsOptions: CustomTabsOptions = extras.getParcelable(EXTRA_CT_OPTIONS)!! + val extras: Bundle? = intent.extras + + val authorizeUri = extras?.getParcelable(EXTRA_AUTHORIZE_URI) + authorizeUri ?: run { + deliverAuthenticationFailure( + AuthenticationException( + ERROR_KEY_URI_NULL, ERROR_VALUE_AUTHORIZE_URI_INVALID + ) + ) + return + } + + val customTabsOptions: CustomTabsOptions? = extras.getParcelable(EXTRA_CT_OPTIONS) + customTabsOptions ?: run { + deliverAuthenticationFailure( + AuthenticationException( + ERROR_KEY_CT_OPTIONS_NULL, ERROR_VALUE_CT_OPTIONS_INVALID + ) + ) + return + } + val launchAsTwa: Boolean = extras.getBoolean(EXTRA_LAUNCH_AS_TWA, false) customTabsController = createCustomTabsController(this, customTabsOptions) customTabsController!!.bindService() - customTabsController!!.launchUri(authorizeUri!!, launchAsTwa, getInstance(), object : RunnableTask { - override fun apply(error: AuthenticationException) { - deliverAuthenticationFailure(error) - } - }) + customTabsController!!.launchUri(authorizeUri, + launchAsTwa, + getInstance(), + object : RunnableTask { + override fun apply(error: AuthenticationException) { + deliverAuthenticationFailure(error) + } + }) } @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) internal open fun createCustomTabsController( - context: Context, - options: CustomTabsOptions + context: Context, options: CustomTabsOptions ): CustomTabsController { return CustomTabsController(context, options, TwaLauncher(context)) } @@ -107,10 +132,7 @@ public open class AuthenticationActivity : Activity() { @JvmStatic internal fun authenticateUsingBrowser( - context: Context, - authorizeUri: Uri, - launchAsTwa: Boolean, - options: CustomTabsOptions + context: Context, authorizeUri: Uri, launchAsTwa: Boolean, options: CustomTabsOptions ) { val intent = Intent(context, AuthenticationActivity::class.java) intent.putExtra(EXTRA_AUTHORIZE_URI, authorizeUri) From 71bc2d3c7340329d1288252699166c0e8d0123ed Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Thu, 5 Sep 2024 14:05:45 +0530 Subject: [PATCH 2/3] Added test cases to check that exception is thrown in the scenario when intent extras are null --- .../auth0/android/provider/WebAuthProvider.kt | 4 ++ .../provider/AuthenticationActivityMock.kt | 8 +++ .../provider/AuthenticationActivityTest.kt | 72 +++++++++++++++++-- 3 files changed, 80 insertions(+), 4 deletions(-) diff --git a/auth0/src/main/java/com/auth0/android/provider/WebAuthProvider.kt b/auth0/src/main/java/com/auth0/android/provider/WebAuthProvider.kt index 852c3031..9224d971 100644 --- a/auth0/src/main/java/com/auth0/android/provider/WebAuthProvider.kt +++ b/auth0/src/main/java/com/auth0/android/provider/WebAuthProvider.kt @@ -82,6 +82,10 @@ public object WebAuthProvider { } internal fun failure(exception: AuthenticationException) { + if (managerInstance == null) { + Log.w(TAG, "There is no previous instance of this provider.") + return + } managerInstance!!.failure(exception) } diff --git a/auth0/src/test/java/com/auth0/android/provider/AuthenticationActivityMock.kt b/auth0/src/test/java/com/auth0/android/provider/AuthenticationActivityMock.kt index 2033a603..10ad0504 100644 --- a/auth0/src/test/java/com/auth0/android/provider/AuthenticationActivityMock.kt +++ b/auth0/src/test/java/com/auth0/android/provider/AuthenticationActivityMock.kt @@ -2,11 +2,14 @@ package com.auth0.android.provider import android.content.Context import android.content.Intent +import com.auth0.android.authentication.AuthenticationException public class AuthenticationActivityMock : AuthenticationActivity() { internal var customTabsController: CustomTabsController? = null public var deliveredIntent: Intent? = null private set + public var deliveredException: AuthenticationException? = null + private set override fun createCustomTabsController( context: Context, @@ -19,4 +22,9 @@ public class AuthenticationActivityMock : AuthenticationActivity() { deliveredIntent = result super.deliverAuthenticationResult(result) } + + override fun deliverAuthenticationFailure(ex: AuthenticationException) { + deliveredException = ex + super.deliverAuthenticationFailure(ex) + } } \ No newline at end of file diff --git a/auth0/src/test/java/com/auth0/android/provider/AuthenticationActivityTest.kt b/auth0/src/test/java/com/auth0/android/provider/AuthenticationActivityTest.kt index 7128a3ec..b78c3aad 100644 --- a/auth0/src/test/java/com/auth0/android/provider/AuthenticationActivityTest.kt +++ b/auth0/src/test/java/com/auth0/android/provider/AuthenticationActivityTest.kt @@ -9,6 +9,9 @@ import androidx.test.espresso.intent.matcher.IntentMatchers import com.auth0.android.authentication.AuthenticationException import com.auth0.android.callback.RunnableTask import com.auth0.android.provider.AuthenticationActivity +import com.auth0.android.provider.AuthenticationActivity.Companion.EXTRA_AUTHORIZE_URI +import com.auth0.android.provider.AuthenticationActivity.Companion.EXTRA_CT_OPTIONS +import com.auth0.android.provider.AuthenticationActivity.Companion.EXTRA_LAUNCH_AS_TWA import com.auth0.android.provider.CustomTabsOptions import com.nhaarman.mockitokotlin2.any import org.hamcrest.CoreMatchers @@ -92,7 +95,12 @@ public class AuthenticationActivityTest { createActivity(intentCaptor.value) activityController.create().start().resume() Mockito.verify(customTabsController).bindService() - Mockito.verify(customTabsController).launchUri(uriCaptor.capture(), launchAsTwaCaptor.capture(), any(), failureCallbackCaptor.capture()) + Mockito.verify(customTabsController).launchUri( + uriCaptor.capture(), + launchAsTwaCaptor.capture(), + any(), + failureCallbackCaptor.capture() + ) MatcherAssert.assertThat(uriCaptor.value, Is.`is`(Matchers.notNullValue())) MatcherAssert.assertThat(uriCaptor.value, Is.`is`(uri)) MatcherAssert.assertThat(activity.deliveredIntent, Is.`is`(Matchers.nullValue())) @@ -123,7 +131,12 @@ public class AuthenticationActivityTest { createActivity(intentCaptor.value) activityController.create().start().resume() Mockito.verify(customTabsController).bindService() - Mockito.verify(customTabsController).launchUri(uriCaptor.capture(), launchAsTwaCaptor.capture(), any(), failureCallbackCaptor.capture()) + Mockito.verify(customTabsController).launchUri( + uriCaptor.capture(), + launchAsTwaCaptor.capture(), + any(), + failureCallbackCaptor.capture() + ) MatcherAssert.assertThat(uriCaptor.value, Is.`is`(Matchers.notNullValue())) MatcherAssert.assertThat(uriCaptor.value, Is.`is`(uri)) MatcherAssert.assertThat(activity.deliveredIntent, Is.`is`(Matchers.nullValue())) @@ -154,7 +167,12 @@ public class AuthenticationActivityTest { createActivity(intentCaptor.value) activityController.create().start().resume() Mockito.verify(customTabsController).bindService() - Mockito.verify(customTabsController).launchUri(uriCaptor.capture(), launchAsTwaCaptor.capture(), any(), failureCallbackCaptor.capture()) + Mockito.verify(customTabsController).launchUri( + uriCaptor.capture(), + launchAsTwaCaptor.capture(), + any(), + failureCallbackCaptor.capture() + ) MatcherAssert.assertThat(uriCaptor.value, Is.`is`(Matchers.notNullValue())) MatcherAssert.assertThat(uriCaptor.value, Is.`is`(uri)) MatcherAssert.assertThat(launchAsTwaCaptor.value, Is.`is`(Matchers.notNullValue())) @@ -184,7 +202,12 @@ public class AuthenticationActivityTest { createActivity(intentCaptor.value) activityController.create().start().resume() Mockito.verify(customTabsController).bindService() - Mockito.verify(customTabsController).launchUri(uriCaptor.capture(), launchAsTwaCaptor.capture(), any(), failureCallbackCaptor.capture()) + Mockito.verify(customTabsController).launchUri( + uriCaptor.capture(), + launchAsTwaCaptor.capture(), + any(), + failureCallbackCaptor.capture() + ) MatcherAssert.assertThat(uriCaptor.value, Is.`is`(Matchers.notNullValue())) MatcherAssert.assertThat(uriCaptor.value, Is.`is`(uri)) MatcherAssert.assertThat(launchAsTwaCaptor.value, Is.`is`(Matchers.notNullValue())) @@ -243,6 +266,47 @@ public class AuthenticationActivityTest { MatcherAssert.assertThat(controller, Is.`is`(Matchers.notNullValue())) } + + @Test + public fun shouldThrowExceptionIfAuthorizeUriIsNullInIntent() { + AuthenticationActivity.authenticateUsingBrowser( + callerActivity, + uri, + false, + customTabsOptions + ) + Mockito.verify(callerActivity).startActivity(intentCaptor.capture()) + createActivity(intentCaptor.value) + activity.intent = Intent().apply { + putExtra(EXTRA_LAUNCH_AS_TWA, false) + putExtra(EXTRA_CT_OPTIONS, customTabsOptions) + } + activityController.create().start().resume() + MatcherAssert.assertThat(activity.deliveredException, Is.`is`(Matchers.notNullValue())) + MatcherAssert.assertThat(activity.isFinishing, Is.`is`(true)) + activityController.destroy() + } + + @Test + public fun shouldThrowExceptionIfCustomTabsIsNullInIntent() { + AuthenticationActivity.authenticateUsingBrowser( + callerActivity, + uri, + false, + customTabsOptions + ) + Mockito.verify(callerActivity).startActivity(intentCaptor.capture()) + createActivity(intentCaptor.value) + activity.intent = Intent().apply { + putExtra(EXTRA_LAUNCH_AS_TWA, false) + putExtra(EXTRA_AUTHORIZE_URI, uri) + } + activityController.create().start().resume() + MatcherAssert.assertThat(activity.deliveredException, Is.`is`(Matchers.notNullValue())) + MatcherAssert.assertThat(activity.isFinishing, Is.`is`(true)) + activityController.destroy() + } + private fun recreateAndCallNewIntent(data: Intent) { val outState = Bundle() activityController.saveInstanceState(outState) From a88a093ce84ee7fd9e993b0ec33ebc6ed29ab22e Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Thu, 5 Sep 2024 14:23:44 +0530 Subject: [PATCH 3/3] updated snyk expiry time for dokka --- .snyk | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.snyk b/.snyk index 0524dbc6..2c0f940d 100644 --- a/.snyk +++ b/.snyk @@ -5,16 +5,16 @@ ignore: SNYK-JAVA-COMFASTERXMLWOODSTOX-3091135: - '*': reason: Latest version of dokka has this vulnerability - expires: 2024-08-31T12:08:37.765Z + expires: 2024-10-31T12:19:35.000Z created: 2024-08-01T12:08:37.770Z SNYK-JAVA-ORGJETBRAINSKOTLIN-2393744: - '*': reason: Latest version of dokka has this vulnerability - expires: 2024-08-31T12:08:55.924Z + expires: 2024-10-31T12:19:35.000Z created: 2024-08-01T12:08:55.927Z SNYK-JAVA-COMFASTERXMLJACKSONCORE-7569538: - '*': reason: Latest version of dokka has this vulnerability - expires: 2024-08-31T12:08:02.966Z + expires: 2024-10-31T12:19:35.000Z created: 2024-08-01T12:08:02.973Z patch: {}