From 0ea3a10b6ab8d28899639721c2c64b94acadd80d Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Tue, 9 Dec 2025 12:30:06 +0530 Subject: [PATCH 1/5] feat: Added support for organisation in CTE --- .../authentication/AuthenticationAPIClient.kt | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt index f3a8cbd8..d08892bb 100755 --- a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt @@ -749,13 +749,15 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe * * @param subjectTokenType the subject token type that is associated with the existing Identity Provider. e.g. 'http://acme.com/legacy-token' * @param subjectToken the subject token, typically obtained through the Identity Provider's SDK + * @param organization id of the organization the user belongs to * @return a request to configure and start that will yield [Credentials] */ public fun customTokenExchange( subjectTokenType: String, subjectToken: String, + organization: String? = null ): AuthenticationRequest { - return tokenExchange(subjectTokenType, subjectToken) + return tokenExchange(subjectTokenType, subjectToken, organization) } /** @@ -1043,13 +1045,17 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe */ private fun tokenExchange( subjectTokenType: String, - subjectToken: String + subjectToken: String, + organization: String? = null ): AuthenticationRequest { - val parameters = ParameterBuilder.newAuthenticationBuilder() - .setGrantType(ParameterBuilder.GRANT_TYPE_TOKEN_EXCHANGE) - .set(SUBJECT_TOKEN_TYPE_KEY, subjectTokenType) - .set(SUBJECT_TOKEN_KEY, subjectToken) - .asDictionary() + val parameters = ParameterBuilder.newAuthenticationBuilder().apply { + setGrantType(ParameterBuilder.GRANT_TYPE_TOKEN_EXCHANGE) + set(SUBJECT_TOKEN_TYPE_KEY, subjectTokenType) + set(SUBJECT_TOKEN_KEY, subjectToken) + organization?.let { + set(ORGANIZATION_KEY, it) + } + }.asDictionary() return loginWithToken(parameters) } From 3bf591b8106b30e0658fe508220994d73b846bb5 Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Tue, 9 Dec 2025 15:28:42 +0530 Subject: [PATCH 2/5] Added URI validation for subject_token_type in CTE --- .../authentication/AuthenticationAPIClient.kt | 17 +++++- .../android/request/AuthenticationRequest.kt | 12 ++++- .../java/com/auth0/android/request/Request.kt | 11 ++++ .../auth0/android/request/RequestValidator.kt | 18 +++++++ .../auth0/android/request/SignUpRequest.kt | 5 ++ .../internal/BaseAuthenticationRequest.kt | 14 ++++- .../android/request/internal/BaseRequest.kt | 15 ++++++ .../validator/CustomTokenExchangeValidator.kt | 52 +++++++++++++++++++ 8 files changed, 139 insertions(+), 5 deletions(-) create mode 100644 auth0/src/main/java/com/auth0/android/request/RequestValidator.kt create mode 100644 auth0/src/main/java/com/auth0/android/request/internal/validator/CustomTokenExchangeValidator.kt diff --git a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt index d08892bb..01220b66 100755 --- a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt @@ -8,11 +8,23 @@ import com.auth0.android.NetworkErrorException import com.auth0.android.dpop.DPoP import com.auth0.android.dpop.DPoPException import com.auth0.android.dpop.SenderConstraining -import com.auth0.android.request.* -import com.auth0.android.request.internal.* +import com.auth0.android.request.AuthenticationRequest +import com.auth0.android.request.ErrorAdapter +import com.auth0.android.request.JsonAdapter +import com.auth0.android.request.ProfileRequest +import com.auth0.android.request.PublicKeyCredentials +import com.auth0.android.request.Request +import com.auth0.android.request.SignUpRequest +import com.auth0.android.request.UserData +import com.auth0.android.request.internal.BaseAuthenticationRequest +import com.auth0.android.request.internal.BaseRequest +import com.auth0.android.request.internal.GsonAdapter import com.auth0.android.request.internal.GsonAdapter.Companion.forMap import com.auth0.android.request.internal.GsonAdapter.Companion.forMapOf +import com.auth0.android.request.internal.GsonProvider +import com.auth0.android.request.internal.RequestFactory import com.auth0.android.request.internal.ResponseUtils.isNetworkError +import com.auth0.android.request.internal.validator.CustomTokenExchangeValidator import com.auth0.android.result.Challenge import com.auth0.android.result.Credentials import com.auth0.android.result.DatabaseUser @@ -758,6 +770,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe organization: String? = null ): AuthenticationRequest { return tokenExchange(subjectTokenType, subjectToken, organization) + .addValidator(CustomTokenExchangeValidator()) } /** diff --git a/auth0/src/main/java/com/auth0/android/request/AuthenticationRequest.kt b/auth0/src/main/java/com/auth0/android/request/AuthenticationRequest.kt index 0871967f..942a8172 100755 --- a/auth0/src/main/java/com/auth0/android/request/AuthenticationRequest.kt +++ b/auth0/src/main/java/com/auth0/android/request/AuthenticationRequest.kt @@ -2,7 +2,6 @@ package com.auth0.android.request import com.auth0.android.Auth0 import com.auth0.android.authentication.AuthenticationException -import com.auth0.android.callback.Callback import com.auth0.android.result.Credentials /** @@ -74,4 +73,15 @@ public interface AuthenticationRequest : Request { * @return itself */ public fun addHeader(name: String, value: String): Request + + /** + * Adds a validator to be executed before the request is sent. + * Multiple validators can be added and will be executed in order. + * + * @param validator the validator to add + * @return itself + */ + public fun addValidator(validator: RequestValidator): Request { + return this + } } \ No newline at end of file diff --git a/auth0/src/main/java/com/auth0/android/request/RequestValidator.kt b/auth0/src/main/java/com/auth0/android/request/RequestValidator.kt new file mode 100644 index 00000000..ab074b11 --- /dev/null +++ b/auth0/src/main/java/com/auth0/android/request/RequestValidator.kt @@ -0,0 +1,18 @@ +package com.auth0.android.request + +import com.auth0.android.Auth0Exception + +/** + * Interface for validating request parameters before execution. + * Validators are invoked before the network request is made. + */ +public interface RequestValidator { + + /** + * Validates the request options and parameters. + * @param options the request options to validate + * @throws Auth0Exception if validation fails + */ + @Throws(Auth0Exception::class) + public fun validate(options: RequestOptions) +} \ No newline at end of file diff --git a/auth0/src/main/java/com/auth0/android/request/SignUpRequest.kt b/auth0/src/main/java/com/auth0/android/request/SignUpRequest.kt index 285664ef..5f52bc6d 100755 --- a/auth0/src/main/java/com/auth0/android/request/SignUpRequest.kt +++ b/auth0/src/main/java/com/auth0/android/request/SignUpRequest.kt @@ -91,6 +91,11 @@ public class SignUpRequest return this } + override fun addValidator(validator: RequestValidator): AuthenticationRequest { + authenticationRequest.addValidator(validator) + return this + } + override fun withIdTokenVerificationLeeway(leeway: Int): SignUpRequest { authenticationRequest.withIdTokenVerificationLeeway(leeway) return this diff --git a/auth0/src/main/java/com/auth0/android/request/internal/BaseAuthenticationRequest.kt b/auth0/src/main/java/com/auth0/android/request/internal/BaseAuthenticationRequest.kt index a62716de..f8103304 100755 --- a/auth0/src/main/java/com/auth0/android/request/internal/BaseAuthenticationRequest.kt +++ b/auth0/src/main/java/com/auth0/android/request/internal/BaseAuthenticationRequest.kt @@ -7,11 +7,16 @@ import com.auth0.android.Auth0Exception import com.auth0.android.authentication.AuthenticationException import com.auth0.android.authentication.ParameterBuilder import com.auth0.android.callback.Callback -import com.auth0.android.provider.* +import com.auth0.android.provider.IdTokenMissingException +import com.auth0.android.provider.IdTokenVerificationOptions +import com.auth0.android.provider.IdTokenVerifier +import com.auth0.android.provider.TokenValidationException +import com.auth0.android.provider.UnexpectedIdTokenException import com.auth0.android.request.AuthenticationRequest import com.auth0.android.request.Request +import com.auth0.android.request.RequestValidator import com.auth0.android.result.Credentials -import java.util.* +import java.util.Date internal open class BaseAuthenticationRequest( private val request: Request, @@ -97,6 +102,11 @@ internal open class BaseAuthenticationRequest( return this } + override fun addValidator(validator: RequestValidator): AuthenticationRequest { + request.addValidator(validator) + return this + } + override fun validateClaims(): AuthenticationRequest { this.validateClaims = true return this diff --git a/auth0/src/main/java/com/auth0/android/request/internal/BaseRequest.kt b/auth0/src/main/java/com/auth0/android/request/internal/BaseRequest.kt index 1f0c53e2..2d11052e 100755 --- a/auth0/src/main/java/com/auth0/android/request/internal/BaseRequest.kt +++ b/auth0/src/main/java/com/auth0/android/request/internal/BaseRequest.kt @@ -12,6 +12,7 @@ import com.auth0.android.request.JsonAdapter import com.auth0.android.request.NetworkingClient import com.auth0.android.request.Request import com.auth0.android.request.RequestOptions +import com.auth0.android.request.RequestValidator import com.auth0.android.request.ServerResponse import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers @@ -40,6 +41,8 @@ internal open class BaseRequest( private val options: RequestOptions = RequestOptions(method) + private val validators = mutableListOf() + override fun addHeader(name: String, value: String): Request { options.headers[name] = value return this @@ -70,6 +73,11 @@ internal open class BaseRequest( return this } + override fun addValidator(validator: RequestValidator): Request { + validators.add(validator) + return this + } + /** * Runs asynchronously and executes the network request, without blocking the current thread. * The result is parsed into a value and posted in the callback's onSuccess method or a @@ -126,6 +134,7 @@ internal open class BaseRequest( */ @kotlin.jvm.Throws(Auth0Exception::class) override fun execute(): T { + runClientValidation() val response: ServerResponse try { if (dPoP?.shouldGenerateProof(url, options.parameters) == true) { @@ -176,4 +185,10 @@ internal open class BaseRequest( } } + private fun runClientValidation() { + validators.forEach { validator -> + validator.validate(options) + } + } + } diff --git a/auth0/src/main/java/com/auth0/android/request/internal/validator/CustomTokenExchangeValidator.kt b/auth0/src/main/java/com/auth0/android/request/internal/validator/CustomTokenExchangeValidator.kt new file mode 100644 index 00000000..496257f7 --- /dev/null +++ b/auth0/src/main/java/com/auth0/android/request/internal/validator/CustomTokenExchangeValidator.kt @@ -0,0 +1,52 @@ +package com.auth0.android.request.internal.validator + +import com.auth0.android.authentication.AuthenticationException +import com.auth0.android.request.RequestOptions +import com.auth0.android.request.RequestValidator +import java.net.URI + +/** + * Client side validation for custom token exchange + */ +public class CustomTokenExchangeValidator : RequestValidator { + + private val reservedNameSpace = listOf( + "http://auth0.com", + "https://auth0.com", + "http://okta.com", + "https://okta.com", + "urn:ietf", + "urn:auth0", + "urn:okta" + ) + + override fun validate(options: RequestOptions) { + val subjectTokenType = options.parameters["subject_token_type"] as String + + // Check if it's a reserved namespace + if (reservedNameSpace.contains(subjectTokenType)) { + throw AuthenticationException( + "Invalid URI", IllegalArgumentException( + "The passed URI is a reserved namespace and cannot be used" + ) + ) + } + + // If it starts with http:// or https://, validate it as a URI + if (subjectTokenType.startsWith( + "http://", + ignoreCase = true + ) || subjectTokenType.startsWith("https://", ignoreCase = true) + ) { + runCatching { + URI(subjectTokenType) + }.onFailure { error -> + throw AuthenticationException( + "Invalid URI", IllegalArgumentException( + "The subject_token_type is not a valid URI: ${error.message}" + ) + ) + } + } + } +} \ No newline at end of file From e990a6d3e4f3c81751f3d4f73d301ce01c7ec945 Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Tue, 9 Dec 2025 16:17:17 +0530 Subject: [PATCH 3/5] Updated the test cases for CTE --- .../AuthenticationAPIClientTest.kt | 110 +++++++++++++++++- .../request/AuthenticationRequestMock.java | 8 ++ .../authentication/request/RequestMock.java | 9 +- 3 files changed, 120 insertions(+), 7 deletions(-) diff --git a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt index b3e67098..367d66e2 100755 --- a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt @@ -2298,7 +2298,7 @@ public class AuthenticationAPIClientTest { public fun shouldCustomTokenExchange() { mockAPI.willReturnSuccessfulLogin() val callback = MockAuthenticationCallback() - client.customTokenExchange("subject-token-type", "subject-token") + client.customTokenExchange("subject-token-type", "subject-token", "org_12345") .start(callback) ShadowLooper.idleMainLooper() val request = mockAPI.takeRequest() @@ -2316,6 +2316,7 @@ public class AuthenticationAPIClientTest { ) assertThat(body, Matchers.hasEntry("subject_token", "subject-token")) assertThat(body, Matchers.hasEntry("subject_token_type", "subject-token-type")) + assertThat(body, Matchers.hasEntry("organization", "org_12345")) assertThat(body, Matchers.hasEntry("scope", "openid profile email")) assertThat( callback, AuthenticationCallbackMatcher.hasPayloadOfType( @@ -2328,7 +2329,7 @@ public class AuthenticationAPIClientTest { public fun shouldCustomTokenExchangeSync() { mockAPI.willReturnSuccessfulLogin() val credentials = client - .customTokenExchange("subject-token-type", "subject-token") + .customTokenExchange("subject-token-type", "subject-token", "org_abc") .execute() val request = mockAPI.takeRequest() assertThat( @@ -2345,6 +2346,7 @@ public class AuthenticationAPIClientTest { ) assertThat(body, Matchers.hasEntry("subject_token", "subject-token")) assertThat(body, Matchers.hasEntry("subject_token_type", "subject-token-type")) + assertThat(body, Matchers.hasEntry("organization", "org_abc")) assertThat(body, Matchers.hasEntry("scope", "openid profile email")) assertThat(credentials, Matchers.`is`(Matchers.notNullValue())) } @@ -2371,10 +2373,97 @@ public class AuthenticationAPIClientTest { ) assertThat(body, Matchers.hasEntry("subject_token", "subject-token")) assertThat(body, Matchers.hasEntry("subject_token_type", "subject-token-type")) + assertThat(body, Matchers.not(Matchers.hasKey("organization"))) assertThat(body, Matchers.hasEntry("scope", "openid profile email")) assertThat(credentials, Matchers.`is`(Matchers.notNullValue())) } + @Test + public fun shouldFailCustomTokenExchangeWithReservedNamespace() { + val callback = MockAuthenticationCallback() + client.customTokenExchange("https://auth0.com", "subject-token") + .start(callback) + ShadowLooper.idleMainLooper() + + assertThat( + callback.error.message, + Matchers.containsString("Invalid URI") + ) + assertThat(callback.error.cause, Matchers.instanceOf(IllegalArgumentException::class.java)) + assertThat( + callback.error.cause?.message, + Matchers.containsString("reserved namespace") + ) + } + + @Test + public fun shouldFailCustomTokenExchangeSyncWithReservedNamespace() { + val exception = assertThrows(AuthenticationException::class.java) { + client.customTokenExchange("urn:okta", "subject-token") + .execute() + } + + assertThat(exception.message, Matchers.containsString("Invalid URI")) + assertThat(exception.cause, Matchers.instanceOf(IllegalArgumentException::class.java)) + assertThat( + exception.cause?.message, + Matchers.containsString("reserved namespace") + ) + } + + @Test + @ExperimentalCoroutinesApi + public fun shouldFailCustomTokenExchangeAwaitWithReservedNamespace() { + val exception = assertThrows(AuthenticationException::class.java) { + runTest { + client.customTokenExchange("urn:auth0", "subject-token") + .await() + } + } + + assertThat(exception.message, Matchers.containsString("Invalid URI")) + assertThat(exception.cause, Matchers.instanceOf(IllegalArgumentException::class.java)) + assertThat( + exception.cause?.message, + Matchers.containsString("reserved namespace") + ) + } + + @Test + public fun shouldFailCustomTokenExchangeWithInvalidHttpUri() { + val callback = MockAuthenticationCallback() + client.customTokenExchange("http://invalid uri with spaces", "subject-token") + .start(callback) + ShadowLooper.idleMainLooper() + + assertThat(callback, AuthenticationCallbackMatcher.hasError(Credentials::class.java)) + assertThat( + callback.error.message, + Matchers.containsString("Invalid URI") + ) + assertThat( + callback.error.cause?.message, + Matchers.containsString("not a valid URI") + ) + } + + @Test + public fun shouldFailCustomTokenExchangeWithMalformedHttpsUri() { + val callback = MockAuthenticationCallback() + client.customTokenExchange("https://[invalid:uri:format", "subject-token") + .start(callback) + ShadowLooper.idleMainLooper() + + assertThat( + callback.error.message, + Matchers.containsString("Invalid URI") + ) + assertThat( + callback.error.cause?.message, + Matchers.containsString("not a valid URI") + ) + } + @Test public fun shouldSsoExchange() { mockAPI.willReturnSuccessfulLogin() @@ -2871,7 +2960,10 @@ public class AuthenticationAPIClientTest { assertThat(request.path, Matchers.equalTo("/oauth/token")) val body = bodyFromRequest(request) - assertThat(body, Matchers.hasEntry("grant_type", ParameterBuilder.GRANT_TYPE_AUTHORIZATION_CODE)) + assertThat( + body, + Matchers.hasEntry("grant_type", ParameterBuilder.GRANT_TYPE_AUTHORIZATION_CODE) + ) assertThat(body, Matchers.hasEntry("code", "auth-code")) // Verify that key pair generation was attempted @@ -2935,7 +3027,10 @@ public class AuthenticationAPIClientTest { assertThat(request.path, Matchers.equalTo("/oauth/token")) val body = bodyFromRequest(request) - assertThat(body, Matchers.hasEntry("grant_type", ParameterBuilder.GRANT_TYPE_TOKEN_EXCHANGE)) + assertThat( + body, + Matchers.hasEntry("grant_type", ParameterBuilder.GRANT_TYPE_TOKEN_EXCHANGE) + ) assertThat(body, Matchers.hasEntry("subject_token_type", "subject-token-type")) // Verify that key pair generation was attempted @@ -3114,9 +3209,12 @@ public class AuthenticationAPIClientTest { client.useDPoP(mockContext).login(SUPPORT_AUTH0_COM, PASSWORD, MY_CONNECTION) .execute() } - Assert.assertEquals("Key pair is not found in the keystore. Please generate a key pair first.", exception.message) + Assert.assertEquals( + "Key pair is not found in the keystore. Please generate a key pair first.", + exception.message + ) assertThat(exception.cause, Matchers.notNullValue()) - assertThat(exception.cause, Matchers.instanceOf(DPoPException::class.java )) + assertThat(exception.cause, Matchers.instanceOf(DPoPException::class.java)) } private fun bodyFromRequest(request: RecordedRequest): Map { diff --git a/auth0/src/test/java/com/auth0/android/authentication/request/AuthenticationRequestMock.java b/auth0/src/test/java/com/auth0/android/authentication/request/AuthenticationRequestMock.java index 4b5d6d0e..3c8d2381 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/request/AuthenticationRequestMock.java +++ b/auth0/src/test/java/com/auth0/android/authentication/request/AuthenticationRequestMock.java @@ -7,8 +7,11 @@ import com.auth0.android.callback.Callback; import com.auth0.android.request.AuthenticationRequest; import com.auth0.android.request.Request; +import com.auth0.android.request.RequestValidator; import com.auth0.android.result.Credentials; +import org.jetbrains.annotations.NotNull; + import java.util.Map; public class AuthenticationRequestMock implements AuthenticationRequest { @@ -112,4 +115,9 @@ public AuthenticationRequest withIdTokenVerificationLeeway(int leeway) { public AuthenticationRequest withIdTokenVerificationIssuer(@NonNull String issuer) { return this; } + + @Override + public @NotNull AuthenticationRequest addValidator(@NotNull RequestValidator validator) { + return this; + } } diff --git a/auth0/src/test/java/com/auth0/android/authentication/request/RequestMock.java b/auth0/src/test/java/com/auth0/android/authentication/request/RequestMock.java index 39404399..3cad906a 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/request/RequestMock.java +++ b/auth0/src/test/java/com/auth0/android/authentication/request/RequestMock.java @@ -4,8 +4,10 @@ import com.auth0.android.Auth0Exception; import com.auth0.android.callback.Callback; -import com.auth0.android.request.HttpMethod; import com.auth0.android.request.Request; +import com.auth0.android.request.RequestValidator; + +import org.jetbrains.annotations.NotNull; import java.util.Map; @@ -62,4 +64,9 @@ public T execute() throws Auth0Exception { public Request addParameter(@NonNull String name, @NonNull Object value) { return this; } + + @Override + public @NotNull Request addValidator(@NotNull RequestValidator validator) { + return this; + } } From c3be4168914e0406bf4215f102dc3c68795cf34b Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Tue, 9 Dec 2025 18:46:50 +0530 Subject: [PATCH 4/5] Minor changes as per the feedback --- .../validator/CustomTokenExchangeValidator.kt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/auth0/src/main/java/com/auth0/android/request/internal/validator/CustomTokenExchangeValidator.kt b/auth0/src/main/java/com/auth0/android/request/internal/validator/CustomTokenExchangeValidator.kt index 496257f7..f6a5e853 100644 --- a/auth0/src/main/java/com/auth0/android/request/internal/validator/CustomTokenExchangeValidator.kt +++ b/auth0/src/main/java/com/auth0/android/request/internal/validator/CustomTokenExchangeValidator.kt @@ -10,7 +10,7 @@ import java.net.URI */ public class CustomTokenExchangeValidator : RequestValidator { - private val reservedNameSpace = listOf( + private val reservedNamespaces = listOf( "http://auth0.com", "https://auth0.com", "http://okta.com", @@ -23,8 +23,16 @@ public class CustomTokenExchangeValidator : RequestValidator { override fun validate(options: RequestOptions) { val subjectTokenType = options.parameters["subject_token_type"] as String + if(subjectTokenType.isEmpty()){ + throw AuthenticationException( + "Invalid URI", IllegalArgumentException( + "The passed URI must not be an empty String" + ) + ) + } + // Check if it's a reserved namespace - if (reservedNameSpace.contains(subjectTokenType)) { + if (reservedNamespaces.contains(subjectTokenType)) { throw AuthenticationException( "Invalid URI", IllegalArgumentException( "The passed URI is a reserved namespace and cannot be used" From bdf9fb88a6aa896ada3dcc564bb8b7bd916b6e9e Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Wed, 10 Dec 2025 16:09:49 +0530 Subject: [PATCH 5/5] Removed the client side validation --- .../authentication/AuthenticationAPIClient.kt | 2 - .../validator/CustomTokenExchangeValidator.kt | 60 ------------- .../AuthenticationAPIClientTest.kt | 86 ------------------- 3 files changed, 148 deletions(-) delete mode 100644 auth0/src/main/java/com/auth0/android/request/internal/validator/CustomTokenExchangeValidator.kt diff --git a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt index 01220b66..b11dc187 100755 --- a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt @@ -24,7 +24,6 @@ import com.auth0.android.request.internal.GsonAdapter.Companion.forMapOf import com.auth0.android.request.internal.GsonProvider import com.auth0.android.request.internal.RequestFactory import com.auth0.android.request.internal.ResponseUtils.isNetworkError -import com.auth0.android.request.internal.validator.CustomTokenExchangeValidator import com.auth0.android.result.Challenge import com.auth0.android.result.Credentials import com.auth0.android.result.DatabaseUser @@ -770,7 +769,6 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe organization: String? = null ): AuthenticationRequest { return tokenExchange(subjectTokenType, subjectToken, organization) - .addValidator(CustomTokenExchangeValidator()) } /** diff --git a/auth0/src/main/java/com/auth0/android/request/internal/validator/CustomTokenExchangeValidator.kt b/auth0/src/main/java/com/auth0/android/request/internal/validator/CustomTokenExchangeValidator.kt deleted file mode 100644 index f6a5e853..00000000 --- a/auth0/src/main/java/com/auth0/android/request/internal/validator/CustomTokenExchangeValidator.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.auth0.android.request.internal.validator - -import com.auth0.android.authentication.AuthenticationException -import com.auth0.android.request.RequestOptions -import com.auth0.android.request.RequestValidator -import java.net.URI - -/** - * Client side validation for custom token exchange - */ -public class CustomTokenExchangeValidator : RequestValidator { - - private val reservedNamespaces = listOf( - "http://auth0.com", - "https://auth0.com", - "http://okta.com", - "https://okta.com", - "urn:ietf", - "urn:auth0", - "urn:okta" - ) - - override fun validate(options: RequestOptions) { - val subjectTokenType = options.parameters["subject_token_type"] as String - - if(subjectTokenType.isEmpty()){ - throw AuthenticationException( - "Invalid URI", IllegalArgumentException( - "The passed URI must not be an empty String" - ) - ) - } - - // Check if it's a reserved namespace - if (reservedNamespaces.contains(subjectTokenType)) { - throw AuthenticationException( - "Invalid URI", IllegalArgumentException( - "The passed URI is a reserved namespace and cannot be used" - ) - ) - } - - // If it starts with http:// or https://, validate it as a URI - if (subjectTokenType.startsWith( - "http://", - ignoreCase = true - ) || subjectTokenType.startsWith("https://", ignoreCase = true) - ) { - runCatching { - URI(subjectTokenType) - }.onFailure { error -> - throw AuthenticationException( - "Invalid URI", IllegalArgumentException( - "The subject_token_type is not a valid URI: ${error.message}" - ) - ) - } - } - } -} \ No newline at end of file diff --git a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt index 367d66e2..bd6dc0a8 100755 --- a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt @@ -2378,92 +2378,6 @@ public class AuthenticationAPIClientTest { assertThat(credentials, Matchers.`is`(Matchers.notNullValue())) } - @Test - public fun shouldFailCustomTokenExchangeWithReservedNamespace() { - val callback = MockAuthenticationCallback() - client.customTokenExchange("https://auth0.com", "subject-token") - .start(callback) - ShadowLooper.idleMainLooper() - - assertThat( - callback.error.message, - Matchers.containsString("Invalid URI") - ) - assertThat(callback.error.cause, Matchers.instanceOf(IllegalArgumentException::class.java)) - assertThat( - callback.error.cause?.message, - Matchers.containsString("reserved namespace") - ) - } - - @Test - public fun shouldFailCustomTokenExchangeSyncWithReservedNamespace() { - val exception = assertThrows(AuthenticationException::class.java) { - client.customTokenExchange("urn:okta", "subject-token") - .execute() - } - - assertThat(exception.message, Matchers.containsString("Invalid URI")) - assertThat(exception.cause, Matchers.instanceOf(IllegalArgumentException::class.java)) - assertThat( - exception.cause?.message, - Matchers.containsString("reserved namespace") - ) - } - - @Test - @ExperimentalCoroutinesApi - public fun shouldFailCustomTokenExchangeAwaitWithReservedNamespace() { - val exception = assertThrows(AuthenticationException::class.java) { - runTest { - client.customTokenExchange("urn:auth0", "subject-token") - .await() - } - } - - assertThat(exception.message, Matchers.containsString("Invalid URI")) - assertThat(exception.cause, Matchers.instanceOf(IllegalArgumentException::class.java)) - assertThat( - exception.cause?.message, - Matchers.containsString("reserved namespace") - ) - } - - @Test - public fun shouldFailCustomTokenExchangeWithInvalidHttpUri() { - val callback = MockAuthenticationCallback() - client.customTokenExchange("http://invalid uri with spaces", "subject-token") - .start(callback) - ShadowLooper.idleMainLooper() - - assertThat(callback, AuthenticationCallbackMatcher.hasError(Credentials::class.java)) - assertThat( - callback.error.message, - Matchers.containsString("Invalid URI") - ) - assertThat( - callback.error.cause?.message, - Matchers.containsString("not a valid URI") - ) - } - - @Test - public fun shouldFailCustomTokenExchangeWithMalformedHttpsUri() { - val callback = MockAuthenticationCallback() - client.customTokenExchange("https://[invalid:uri:format", "subject-token") - .start(callback) - ShadowLooper.idleMainLooper() - - assertThat( - callback.error.message, - Matchers.containsString("Invalid URI") - ) - assertThat( - callback.error.cause?.message, - Matchers.containsString("not a valid URI") - ) - } - @Test public fun shouldSsoExchange() { mockAPI.willReturnSuccessfulLogin()