Skip to content

Commit

Permalink
chore: minor refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
desusai7 committed Jul 30, 2024
1 parent a64a340 commit 45f0909
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 340 deletions.
121 changes: 49 additions & 72 deletions auth0/src/main/java/com/auth0/android/Auth0.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.auth0.android.util.Auth0UserAgent
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executor
import java.util.concurrent.Executors

/**
Expand All @@ -22,22 +22,24 @@ import java.util.concurrent.Executors
* For more information, please see the [OIDC adoption guide](https://auth0.com/docs/api-auth/tutorials/adoption).
*
* @param clientId of your Auth0 application
* @param domain of your Auth0 account
* @param domainUrl of your Auth0 account
* @param configurationDomain where Auth0's configuration will be fetched, change it if using an on-premise Auth0 server. By default is Auth0 public cloud.
*/
public open class Auth0 private constructor(
/**
* @return your Auth0 application client identifier
*/
public val clientId: String, domain: String, configurationDomain: String? = null
public val clientId: String,
private val domainUrl: HttpUrl,
public val configurationDomain: String? = null,
) {
private val domainUrl: HttpUrl?
private val configurationUrl: HttpUrl
public val domain: String = domainUrl.host
private val configurationUrl: HttpUrl = ensureValidUrl(configurationDomain) ?: domainUrl

/**
* @return Auth0 user agent information sent in every request
*/
public var auth0UserAgent: Auth0UserAgent
public var auth0UserAgent: Auth0UserAgent = Auth0UserAgent()

/**
* The networking client instance used to make HTTP requests.
Expand All @@ -48,19 +50,7 @@ public open class Auth0 private constructor(
/**
* The single thread executor used to run tasks in the background throughout this Auth0 instance.
*/
public val executor: ExecutorService = Executors.newSingleThreadExecutor()

/**
* Creates a new Auth0 instance with the 'com_auth0_client_id' and 'com_auth0_domain' values
* defined in the project String resources file.
* If the values are not found, IllegalArgumentException will raise.
*
* @param context a valid context
*/
public constructor(context: Context) : this(
getResourceFromContext(context, "com_auth0_client_id"),
getResourceFromContext(context, "com_auth0_domain")
)
public val executor: Executor = Executors.newSingleThreadExecutor()

/**
* @return your Auth0 account domain url
Expand Down Expand Up @@ -99,21 +89,30 @@ public open class Auth0 private constructor(
.build()
.toString()

private fun ensureValidUrl(url: String?): HttpUrl? {
if (url == null) {
return null
}
val normalizedUrl = url.lowercase(Locale.ROOT)
require(!normalizedUrl.startsWith("http://")) { "Invalid domain url: '$url'. Only HTTPS domain URLs are supported. If no scheme is passed, HTTPS will be used." }
val safeUrl =
if (normalizedUrl.startsWith("https://")) normalizedUrl else "https://$normalizedUrl"
return safeUrl.toHttpUrlOrNull()
}

public companion object {

private val instances: MutableMap<Pair<String, String>, Auth0> = mutableMapOf()
private var instance: Auth0? = null

/**
* Creates a new Auth0 instance with the 'com_auth0_client_id' and 'com_auth0_domain' values
* defined in the project String resources file, if the instance with the same values doesn't exist yet and returns it.
* If it already exists, it will return the existing instance.
* If the values 'com_auth0_client_id' and 'com_auth0_domain' are not found in project String resources file, IllegalArgumentException will raise.
*
* @param context a valid context
*/
@JvmStatic
public fun getInstance(context: Context): Auth0 {
val clientId = getResourceFromContext(context, "com_auth0_client_id")
val domain = getResourceFromContext(context, "com_auth0_domain")
return getInstance(clientId, domain)
}

/**
* Creates a new Auth0 instance with the given clientId and domain, if it doesn't exist yet and returns it.
* If it already exists, it will return the existing instance.
*/
@JvmStatic
public fun getInstance(
clientId: String,
Expand All @@ -122,49 +121,23 @@ public open class Auth0 private constructor(
return getInstance(clientId, domain, null)
}


/**
* Creates a new Auth0 instance with the given clientId, domain and configurationDomain, if it doesn't exist yet and returns it.
* If it already exists, it will return the existing instance.
*/
@JvmStatic
public fun getInstance(
clientId: String,
domain: String,
configurationDomain: String?
): Auth0 {
return instances.getOrPut(Pair(clientId, domain)) {
Auth0(clientId, domain, configurationDomain)
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) {
instance = Auth0(clientId, domainUrl, configurationDomain)
}
}

@JvmStatic
public fun getInstance(context: Context): Auth0 {
val clientId = getResourceFromContext(context, "com_auth0_client_id")
val domain = getResourceFromContext(context, "com_auth0_domain")
return getInstance(clientId, domain)
}

@JvmStatic
public fun clearInstance(clientId: String, domain: String) {
clearInstance(clientId, domain, null)
}

@JvmStatic
public fun clearInstance(clientId: String, domain: String, configurationDomain: String?) {
instances.remove(Pair(clientId, domain))
}

@JvmStatic
public fun clearInstance(context: Context) {
val clientId = getResourceFromContext(context, "com_auth0_client_id")
val domain = getResourceFromContext(context, "com_auth0_domain")
clearInstance(clientId, domain)
}

@JvmStatic
public fun clearAllInstances() {
instances.clear()
}

@JvmStatic
public fun hasInstance(clientId: String, domain: String): Boolean {
return instances.containsKey(Pair(clientId, domain))
return instance!!
}

private fun getResourceFromContext(context: Context, resName: String): String {
Expand All @@ -177,12 +150,16 @@ public open class Auth0 private constructor(
}
return context.getString(stringRes)
}
}

init {
domainUrl = ensureValidUrl(domain)
requireNotNull(domainUrl) { String.format("Invalid domain url: '%s'", domain) }
configurationUrl = ensureValidUrl(configurationDomain) ?: domainUrl
auth0UserAgent = Auth0UserAgent()
private fun ensureValidUrl(url: String?): HttpUrl? {
if (url == null) {
return null
}
val normalizedUrl = url.lowercase(Locale.ROOT)
require(!normalizedUrl.startsWith("http://")) { "Invalid domain url: '$url'. Only HTTPS domain URLs are supported. If no scheme is passed, HTTPS will be used." }
val safeUrl =
if (normalizedUrl.startsWith("https://")) normalizedUrl else "https://$normalizedUrl"
return safeUrl.toHttpUrlOrNull()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import okhttp3.HttpUrl.Companion.toHttpUrl
import java.io.IOException
import java.io.Reader
import java.security.PublicKey
import java.util.concurrent.ExecutorService

/**
* API client for Auth0 Authentication API.
Expand Down Expand Up @@ -54,8 +53,6 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
get() = auth0.clientId
public val baseURL: String
get() = auth0.getDomainUrl()
public val executor: ExecutorService
get() = auth0.executor

/**
* Log in a user with email/username and password for a connection/realm.
Expand Down Expand Up @@ -744,7 +741,11 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
val credentialsAdapter: JsonAdapter<Credentials> = GsonAdapter(
Credentials::class.java, gson
)
val request = BaseAuthenticationRequest(factory.post(url.toString(), credentialsAdapter), clientId, baseURL)
val request = BaseAuthenticationRequest(
factory.post(url.toString(), credentialsAdapter),
clientId,
baseURL
)
request.addParameters(requestParameters)
return request
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.util.Base64
import android.util.Log
import androidx.annotation.VisibleForTesting
import androidx.fragment.app.FragmentActivity
import com.auth0.android.Auth0
import com.auth0.android.Auth0Exception
import com.auth0.android.authentication.AuthenticationAPIClient
import com.auth0.android.callback.Callback
Expand All @@ -16,9 +17,7 @@ import com.google.gson.Gson
import kotlinx.coroutines.suspendCancellableCoroutine
import java.lang.ref.WeakReference
import java.util.*
import java.util.concurrent.Callable
import java.util.concurrent.ExecutionException
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executor
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

Expand All @@ -31,7 +30,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
storage: Storage,
private val crypto: CryptoUtil,
jwtDecoder: JWTDecoder,
private val serialExecutor: ExecutorService,
private val serialExecutor: Executor,
private val fragmentActivity: WeakReference<FragmentActivity>? = null,
private val localAuthenticationOptions: LocalAuthenticationOptions? = null,
private val localAuthenticationManagerFactory: LocalAuthenticationManagerFactory? = null,
Expand All @@ -43,43 +42,43 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
* Creates a new SecureCredentialsManager to handle Credentials
*
* @param context a valid context
* @param apiClient the Auth0 Authentication API Client to handle token refreshment when needed.
* @param auth0 the Auth0 account information to use
* @param storage the storage implementation to use
*/
public constructor(
context: Context,
apiClient: AuthenticationAPIClient,
auth0: Auth0,
storage: Storage,
) : this(
apiClient,
AuthenticationAPIClient(auth0),
storage,
CryptoUtil(context, storage, KEY_ALIAS),
JWTDecoder(),
apiClient.executor
auth0.executor
)


/**
* Creates a new SecureCredentialsManager to handle Credentials with biometrics Authentication
*
* @param context a valid context
* @param apiClient the Auth0 Authentication API Client to handle token refreshment when needed.
* @param auth0 the Auth0 account information to use
* @param storage the storage implementation to use
* @param fragmentActivity the FragmentActivity to use for the biometric authentication
* @param localAuthenticationOptions the options of type [LocalAuthenticationOptions] to use for the biometric authentication
*/
public constructor(
context: Context,
apiClient: AuthenticationAPIClient,
auth0: Auth0,
storage: Storage,
fragmentActivity: FragmentActivity,
localAuthenticationOptions: LocalAuthenticationOptions
) : this(
apiClient,
AuthenticationAPIClient(auth0),
storage,
CryptoUtil(context, storage, KEY_ALIAS),
JWTDecoder(),
apiClient.executor,
auth0.executor,
WeakReference(fragmentActivity),
localAuthenticationOptions,
DefaultLocalAuthenticationManagerFactory()
Expand All @@ -94,19 +93,6 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
*/
@Throws(CredentialsManagerException::class)
override fun saveCredentials(credentials: Credentials) {
val future = serialExecutor.submit {
saveCredentialsDirect(credentials)
}
try {
future.get()
} catch (e: ExecutionException) {
throw e.cause as CredentialsManagerException
} catch (e: InterruptedException) {
throw e.cause as CredentialsManagerException
}
}

private fun saveCredentialsDirect(credentials: Credentials) {
if (TextUtils.isEmpty(credentials.accessToken) && TextUtils.isEmpty(credentials.idToken)) {
throw CredentialsManagerException.INVALID_CREDENTIALS
}
Expand Down Expand Up @@ -471,21 +457,19 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
* @return whether this manager contains a valid non-expired pair of credentials or not.
*/
override fun hasValidCredentials(minTtl: Long): Boolean {
return serialExecutor.submit(Callable {
val encryptedEncoded = storage.retrieveString(KEY_CREDENTIALS)
var expiresAt = storage.retrieveLong(KEY_EXPIRES_AT)
if (expiresAt == null) {
// Avoids logging out users when this value was not saved (migration scenario)
expiresAt = 0L
}
val canRefresh = storage.retrieveBoolean(KEY_CAN_REFRESH)
val emptyCredentials = TextUtils.isEmpty(encryptedEncoded)
!(emptyCredentials || willExpire(
expiresAt,
minTtl
) &&
(canRefresh == null || !canRefresh))
}).get()
val encryptedEncoded = storage.retrieveString(KEY_CREDENTIALS)
var expiresAt = storage.retrieveLong(KEY_EXPIRES_AT)
if (expiresAt == null) {
// Avoids logging out users when this value was not saved (migration scenario)
expiresAt = 0L
}
val canRefresh = storage.retrieveBoolean(KEY_CAN_REFRESH)
val emptyCredentials = TextUtils.isEmpty(encryptedEncoded)
return !(emptyCredentials || willExpire(
expiresAt,
minTtl
) &&
(canRefresh == null || !canRefresh))
}

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
Expand Down Expand Up @@ -612,7 +596,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
}

try {
saveCredentialsDirect(freshCredentials)
saveCredentials(freshCredentials)
callback.onSuccess(freshCredentials)
} catch (error: CredentialsManagerException) {
val exception = CredentialsManagerException(
Expand All @@ -633,17 +617,21 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT

internal companion object {
private val TAG = SecureCredentialsManager::class.java.simpleName

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal const val KEY_CREDENTIALS = "com.auth0.credentials"

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal const val KEY_EXPIRES_AT = "com.auth0.credentials_access_token_expires_at"

// This is no longer used as we get the credentials expiry from the access token only,
// but we still store it so users can rollback to versions where it is required.
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal const val LEGACY_KEY_CACHE_EXPIRES_AT = "com.auth0.credentials_expires_at"

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal const val KEY_CAN_REFRESH = "com.auth0.credentials_can_refresh"

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal const val KEY_ALIAS = "com.auth0.key"
}
Expand Down
Loading

0 comments on commit 45f0909

Please sign in to comment.