Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,21 @@ 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.result.Challenge
import com.auth0.android.result.Credentials
Expand Down Expand Up @@ -749,13 +760,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)
}

/**
Expand Down Expand Up @@ -1043,13 +1056,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)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

/**
Expand Down Expand Up @@ -74,4 +73,15 @@ public interface AuthenticationRequest : Request<Credentials, AuthenticationExce
* @return the current builder instance
*/
public fun withIdTokenVerificationIssuer(issuer: String): AuthenticationRequest

/**
* 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
*/
override fun addValidator(validator: RequestValidator): AuthenticationRequest {
return this
}
}
11 changes: 11 additions & 0 deletions auth0/src/main/java/com/auth0/android/request/Request.kt
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,15 @@ public interface Request<T, U : Auth0Exception> {
* @return itself
*/
public fun addHeader(name: String, value: String): Request<T, U>

/**
* 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<T, U> {
return this
}
}
Original file line number Diff line number Diff line change
@@ -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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Credentials, AuthenticationException>,
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -40,6 +41,8 @@ internal open class BaseRequest<T, U : Auth0Exception>(

private val options: RequestOptions = RequestOptions(method)

private val validators = mutableListOf<RequestValidator>()

override fun addHeader(name: String, value: String): Request<T, U> {
options.headers[name] = value
return this
Expand Down Expand Up @@ -70,6 +73,11 @@ internal open class BaseRequest<T, U : Auth0Exception>(
return this
}

override fun addValidator(validator: RequestValidator): Request<T, U> {
validators.add(validator)
return this
}

/**
* Runs asynchronously and executes the network request, without blocking the current thread.
* The result is parsed into a <T> value and posted in the callback's onSuccess method or a <U>
Expand Down Expand Up @@ -126,6 +134,7 @@ internal open class BaseRequest<T, U : Auth0Exception>(
*/
@kotlin.jvm.Throws(Auth0Exception::class)
override fun execute(): T {
runClientValidation()
val response: ServerResponse
try {
if (dPoP?.shouldGenerateProof(url, options.parameters) == true) {
Expand Down Expand Up @@ -176,4 +185,10 @@ internal open class BaseRequest<T, U : Auth0Exception>(
}
}

private fun runClientValidation() {
validators.forEach { validator ->
validator.validate(options)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2298,7 +2298,7 @@ public class AuthenticationAPIClientTest {
public fun shouldCustomTokenExchange() {
mockAPI.willReturnSuccessfulLogin()
val callback = MockAuthenticationCallback<Credentials>()
client.customTokenExchange("subject-token-type", "subject-token")
client.customTokenExchange("subject-token-type", "subject-token", "org_12345")
.start(callback)
ShadowLooper.idleMainLooper()
val request = mockAPI.takeRequest()
Expand All @@ -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(
Expand All @@ -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(
Expand All @@ -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()))
}
Expand All @@ -2371,6 +2373,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.not(Matchers.hasKey("organization")))
assertThat(body, Matchers.hasEntry("scope", "openid profile email"))
assertThat(credentials, Matchers.`is`(Matchers.notNullValue()))
}
Expand Down Expand Up @@ -2871,7 +2874,10 @@ public class AuthenticationAPIClientTest {
assertThat(request.path, Matchers.equalTo("/oauth/token"))

val body = bodyFromRequest<String>(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
Expand Down Expand Up @@ -2935,7 +2941,10 @@ public class AuthenticationAPIClientTest {
assertThat(request.path, Matchers.equalTo("/oauth/token"))

val body = bodyFromRequest<String>(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
Expand Down Expand Up @@ -3114,9 +3123,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 <T> bodyFromRequest(request: RecordedRequest): Map<String, T> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -62,4 +64,9 @@ public T execute() throws Auth0Exception {
public Request<T, U> addParameter(@NonNull String name, @NonNull Object value) {
return this;
}

@Override
public @NotNull Request<T, @NotNull U> addValidator(@NotNull RequestValidator validator) {
return this;
}
}