Skip to content

Commit

Permalink
Merge pull request #433 from auth0/ft-gson-singleton
Browse files Browse the repository at this point in the history
Move to a JSON client singleton
  • Loading branch information
lbalmaceda authored Jan 15, 2021
2 parents 50db306 + 21bcfaf commit 255e682
Show file tree
Hide file tree
Showing 26 changed files with 1,090 additions and 866 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
) : this(
auth0,
RequestFactory<AuthenticationException>(networkingClient, createErrorAdapter()),
GsonProvider.buildGson()
GsonProvider.gson
)

public val clientId: String
Expand Down Expand Up @@ -678,7 +678,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
private const val WELL_KNOWN_PATH = ".well-known"
private const val JWKS_FILE_PATH = "jwks.json"
private fun createErrorAdapter(): ErrorAdapter<AuthenticationException> {
val mapAdapter = forMap(Gson())
val mapAdapter = forMap(GsonProvider.gson)
return object : ErrorAdapter<AuthenticationException> {
override fun fromRawResponse(
statusCode: Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
private val crypto: CryptoUtil,
jwtDecoder: JWTDecoder
) : BaseCredentialsManager(apiClient, storage, jwtDecoder) {
private val gson: Gson = GsonProvider.buildGson()
private val gson: Gson = GsonProvider.gson

//Changeable by the user
private var authenticateBeforeDecrypt: Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public class UsersAPIClient @VisibleForTesting(otherwise = VisibleForTesting.PRI
) : this(
auth0,
factoryForToken(token, networkingClient),
GsonProvider.buildGson()
GsonProvider.gson
)

public val clientId: String
Expand Down Expand Up @@ -236,7 +236,7 @@ public class UsersAPIClient @VisibleForTesting(otherwise = VisibleForTesting.PRI
private const val USER_METADATA_KEY = "user_metadata"

private fun createErrorAdapter(): ErrorAdapter<ManagementException> {
val mapAdapter = forMap(Gson())
val mapAdapter = forMap(GsonProvider.gson)
return object : ErrorAdapter<ManagementException> {
override fun fromRawResponse(
statusCode: Int,
Expand Down
10 changes: 5 additions & 5 deletions auth0/src/main/java/com/auth0/android/request/DefaultClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ import java.util.concurrent.TimeUnit
* @param enableLogging whether HTTP request and response info should be logged. This should only be set to `true` for debugging purposes in non-production environments, as sensitive information is included in the logs. Defaults to `false`.
*/
public class DefaultClient(
private val connectTimeout: Int = DEFAULT_TIMEOUT_SECONDS,
private val readTimeout: Int = DEFAULT_TIMEOUT_SECONDS,
private val defaultHeaders: Map<String, String> = mapOf(),
private val enableLogging: Boolean = false
private val connectTimeout: Int = DEFAULT_TIMEOUT_SECONDS,
private val readTimeout: Int = DEFAULT_TIMEOUT_SECONDS,
private val defaultHeaders: Map<String, String> = mapOf(),
private val enableLogging: Boolean = false
) : NetworkingClient {

//TODO: receive this via internal constructor parameters
private val gson: Gson = GsonProvider.buildGson()
private val gson: Gson = GsonProvider.gson

@get:VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal val okHttpClient: OkHttpClient
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ internal class GsonAdapter<T> private constructor(private val adapter: TypeAdapt
return GsonAdapter(typeToken, gson)
}

private fun supplyDefaultGson() = GsonProvider.buildGson()
private fun supplyDefaultGson() = GsonProvider.gson
}

internal constructor(
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.auth0.android.request.internal

import androidx.annotation.VisibleForTesting
import com.auth0.android.result.Credentials
import com.auth0.android.result.UserProfile
import com.auth0.android.util.JsonRequiredTypeAdapterFactory
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import java.security.PublicKey
import java.text.SimpleDateFormat
import java.util.*

internal object GsonProvider {
internal val gson: Gson
private var sdf: SimpleDateFormat
private const val DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"

init {
val jwksType = TypeToken.getParameterized(
Map::class.java,
String::class.java,
PublicKey::class.java
).type
gson = GsonBuilder()
.registerTypeAdapterFactory(JsonRequiredTypeAdapterFactory())
.registerTypeAdapter(UserProfile::class.java, UserProfileDeserializer())
.registerTypeAdapter(Credentials::class.java, CredentialsDeserializer())
.registerTypeAdapter(jwksType, JwksDeserializer())
.setDateFormat(DATE_FORMAT)
.create()
sdf = SimpleDateFormat(DATE_FORMAT, Locale.US)
}

@JvmStatic
@VisibleForTesting
fun formatDate(date: Date): String {
return sdf.format(date)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.auth0.android.request.internal

import android.util.Base64
import android.util.Log
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonParseException
import java.lang.reflect.Type
import java.math.BigInteger
import java.security.KeyFactory
import java.security.NoSuchAlgorithmException
import java.security.PublicKey
import java.security.spec.InvalidKeySpecException
import java.security.spec.RSAPublicKeySpec

internal class JwksDeserializer : JsonDeserializer<Map<String, PublicKey>> {
@Throws(JsonParseException::class)
override fun deserialize(
json: JsonElement,
typeOfT: Type,
context: JsonDeserializationContext
): Map<String, PublicKey> {
if (!json.isJsonObject || json.isJsonNull || json.asJsonObject.entrySet().isEmpty()) {
throw JsonParseException("jwks json must be a valid and non-empty json object")
}
val jwks = mutableMapOf<String, PublicKey>()
val keys = json.asJsonObject.getAsJsonArray("keys")
for (k in keys) {
val currentKey = k.asJsonObject
val keyAlg = context.deserialize<String>(currentKey["alg"], String::class.java)
val keyUse = context.deserialize<String>(currentKey["use"], String::class.java)
if (RSA_ALGORITHM != keyAlg || USE_SIGNING != keyUse) {
//Key not supported at this time
continue
}
val keyType = context.deserialize<String>(currentKey["kty"], String::class.java)
val keyId = context.deserialize<String>(currentKey["kid"], String::class.java)
val keyModulus = context.deserialize<String>(currentKey["n"], String::class.java)
val keyPublicExponent = context.deserialize<String>(currentKey["e"], String::class.java)
try {
val kf = KeyFactory.getInstance(keyType)
val modulus = BigInteger(
1,
Base64.decode(
keyModulus,
Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP
)
)
val exponent = BigInteger(
1,
Base64.decode(
keyPublicExponent,
Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP
)
)
val pub = kf.generatePublic(RSAPublicKeySpec(modulus, exponent))
jwks[keyId] = pub
} catch (e: NoSuchAlgorithmException) {
Log.e(
JwksDeserializer::class.java.simpleName,
"Could not parse the JWK with ID $keyId",
e
)
//Would result in an empty key set
} catch (e: InvalidKeySpecException) {
Log.e(
JwksDeserializer::class.java.simpleName,
"Could not parse the JWK with ID $keyId",
e
)
}
}
return jwks.toMap()
}

companion object {
private const val RSA_ALGORITHM = "RS256"
private const val USE_SIGNING = "sig"
}
}
4 changes: 2 additions & 2 deletions auth0/src/main/java/com/auth0/android/util/Auth0UserAgent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import android.text.TextUtils
import android.util.Base64
import androidx.annotation.VisibleForTesting
import com.auth0.android.auth0.BuildConfig
import com.google.gson.Gson
import com.auth0.android.request.internal.GsonProvider
import java.nio.charset.StandardCharsets
import java.util.*

Expand Down Expand Up @@ -54,7 +54,7 @@ public class Auth0UserAgent public constructor(
values[NAME_KEY] = name
values[VERSION_KEY] = version
values[ENV_KEY] = environment
val json = Gson().toJson(values)
val json = GsonProvider.gson.toJson(values)
val bytes = json.toByteArray(StandardCharsets.UTF_8)
value = String(
Base64.encode(bytes, Base64.URL_SAFE or Base64.NO_WRAP),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,9 @@ public class AuthenticationExceptionTest {
@Test
@Throws(Exception::class)
public fun shouldHaveNotStrongPasswordWithDetailedDescription() {
val gson = GsonProvider.buildGson()
val fr = FileReader(PASSWORD_STRENGTH_ERROR_RESPONSE)
val mapType = object : TypeToken<Map<String?, Any?>?>() {}.type
val mapPayload = gson.fromJson<Map<String, Any>>(fr, mapType)
val mapPayload = GsonProvider.gson.fromJson<Map<String, Any>>(fr, mapType)
val ex = AuthenticationException(mapPayload)
MatcherAssert.assertThat(ex.isPasswordNotStrongEnough, CoreMatchers.`is`(true))
val expectedDescription =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import org.junit.runner.RunWith
import org.mockito.*
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.*
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
Expand Down Expand Up @@ -84,7 +87,7 @@ public class SecureCredentialsManagerTest {
SecureCredentialsManager(client, storage, crypto, jwtDecoder)
manager = Mockito.spy(secureCredentialsManager)
Mockito.doReturn(CredentialsMock.CURRENT_TIME_MS).`when`(manager).currentTimeInMillis
gson = GsonProvider.buildGson()
gson = GsonProvider.gson
}

@Test
Expand Down
Loading

0 comments on commit 255e682

Please sign in to comment.