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
2 changes: 2 additions & 0 deletions firebase-dataconnect/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
([#8024](https://github.com/firebase/firebase-android-sdk/pull/8024))
- [changed] Internal refactor to use more descriptive variable names.
([#8025](https://github.com/firebase/firebase-android-sdk/pull/8025))
- [changed] Internal refactor to use token objects instead of strings.
([#8027](https://github.com/firebase/firebase-android-sdk/pull/8027))

# 17.2.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import com.google.firebase.dataconnect.*
import com.google.firebase.dataconnect.DataConnectPathSegment
import com.google.firebase.dataconnect.DataSource
import com.google.firebase.dataconnect.QueryRef.FetchPolicy
import com.google.firebase.dataconnect.core.DataConnectAppCheck.GetAppCheckTokenResult
import com.google.firebase.dataconnect.core.DataConnectAuth.GetAuthTokenResult
import com.google.firebase.dataconnect.core.DataConnectGrpcClientGlobals.toErrorInfoImpl
import com.google.firebase.dataconnect.core.LoggerGlobals.warn
import com.google.firebase.dataconnect.util.ProtoUtil.decodeFromStruct
Expand Down Expand Up @@ -73,8 +75,9 @@ internal class DataConnectGrpcClient(
}

val executeQueryResult =
grpcRPCs.retryOnGrpcUnauthenticatedError(requestId, "executeQuery") {
executeQuery(requestId, request, callerSdkType, fetchPolicy)
grpcRPCs.retryOnGrpcUnauthenticatedError(requestId, "executeQuery") { authToken, appCheckToken
->
executeQuery(requestId, request, callerSdkType, fetchPolicy, authToken, appCheckToken)
}

return executeQueryResult.toOperationResult()
Expand All @@ -94,7 +97,9 @@ internal class DataConnectGrpcClient(

val response =
grpcRPCs.retryOnGrpcUnauthenticatedError(requestId, "executeMutation") {
executeMutation(requestId, request, callerSdkType)
authToken,
appCheckToken ->
executeMutation(requestId, request, callerSdkType, authToken, appCheckToken)
}

return OperationResult(
Expand All @@ -104,13 +109,16 @@ internal class DataConnectGrpcClient(
)
}

private inline fun <T, R> T.retryOnGrpcUnauthenticatedError(
private suspend inline fun <T, R> T.retryOnGrpcUnauthenticatedError(
requestId: String,
kotlinMethodName: String,
block: T.() -> R
block: T.(GetAuthTokenResult?, GetAppCheckTokenResult?) -> R,
): R {
val authToken1 = dataConnectAuth.getToken(requestId)
val appCheckToken1 = dataConnectAppCheck.getToken(requestId)

return try {
block()
block(authToken1, appCheckToken1)
} catch (e: StatusException) {
if (e.status.code != Status.UNAUTHENTICATED.code) {
throw e
Expand All @@ -125,7 +133,10 @@ internal class DataConnectGrpcClient(
dataConnectAuth.forceRefresh()
dataConnectAppCheck.forceRefresh()

block()
val authToken2 = dataConnectAuth.getToken(requestId)
val appCheckToken2 = dataConnectAppCheck.getToken(requestId)

block(authToken2, appCheckToken2)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ import com.google.protobuf.Struct
import io.grpc.Metadata

internal class DataConnectGrpcMetadata(
val dataConnectAuth: DataConnectAuth,
val dataConnectAppCheck: DataConnectAppCheck,
val connectorLocation: String,
val kotlinVersion: String,
val androidVersion: Int,
Expand All @@ -42,7 +40,6 @@ internal class DataConnectGrpcMetadata(
Logger("DataConnectGrpcMetadata").apply {
debug {
"created by ${parentLogger.nameWithId} with" +
" dataConnectAuth=${dataConnectAuth.instanceId}" +
" connectorLocation=$connectorLocation" +
" kotlinVersion=$kotlinVersion" +
" androidVersion=$androidVersion" +
Expand All @@ -54,7 +51,6 @@ internal class DataConnectGrpcMetadata(
val instanceId: String
get() = logger.nameWithId

@Suppress("SpellCheckingInspection")
private val googRequestParamsHeaderValue = "location=${connectorLocation}&frontend=data"

private fun googApiClientHeaderValue(callerSdkType: FirebaseDataConnect.CallerSdkType): String {
Expand All @@ -76,34 +72,33 @@ internal class DataConnectGrpcMetadata(
return components.joinToString(" ")
}

data class GetGrpcMetadataResult(
val metadata: Metadata,
val authToken: DataConnectAuth.GetAuthTokenResult?,
)

suspend fun get(
requestId: String,
fun get(
authToken: DataConnectAuth.GetAuthTokenResult?,
appCheckToken: DataConnectAppCheck.GetAppCheckTokenResult?,
callerSdkType: FirebaseDataConnect.CallerSdkType
): GetGrpcMetadataResult {
val authToken = dataConnectAuth.getToken(requestId)
val appCheckToken = dataConnectAppCheck.getToken(requestId)

): Metadata {
val metadata =
Metadata().also {
it.put(googRequestParamsHeader, googRequestParamsHeaderValue)
it.put(googApiClientHeader, googApiClientHeaderValue(callerSdkType))
if (appId.isNotBlank()) {
it.put(gmpAppIdHeader, appId)
}
authToken?.token?.let { token -> it.put(firebaseAuthTokenHeader, token) }
appCheckToken?.token?.let { token -> it.put(firebaseAppCheckTokenHeader, token) }

authToken?.token?.let { authTokenString ->
it.put(firebaseAuthTokenHeader, authTokenString)
}
appCheckToken?.token?.let { appCheckTokenString ->
it.put(firebaseAppCheckTokenHeader, appCheckTokenString)
}
}

return GetGrpcMetadataResult(metadata, authToken)
return metadata
}

companion object {
fun Metadata.toStructProto(): Struct = buildStructProto {
// TODO: Move this to ProtoUtil.kt where it would live alongside other related methods.
fun Metadata.toStructProto(authUid: String?): Struct = buildStructProto {
val keys: List<Metadata.Key<String>> = run {
val keySet: MutableSet<String> = keys().toMutableSet()
// Always explicitly include the auth header in the returned string, even if it is absent.
Expand All @@ -119,7 +114,7 @@ internal class DataConnectGrpcMetadata(
else {
values.map {
when (key.name()) {
firebaseAuthTokenHeader.name() -> it.toScrubbedAccessToken()
firebaseAuthTokenHeader.name() -> it.toScrubbedAccessToken() + " (authUid=$authUid)"
firebaseAppCheckTokenHeader.name() -> it.toScrubbedAccessToken()
else -> it
}
Expand All @@ -138,11 +133,9 @@ internal class DataConnectGrpcMetadata(
private val firebaseAppCheckTokenHeader: Metadata.Key<String> =
Metadata.Key.of("x-firebase-appcheck", Metadata.ASCII_STRING_MARSHALLER)

@Suppress("SpellCheckingInspection")
private val googRequestParamsHeader: Metadata.Key<String> =
Metadata.Key.of("x-goog-request-params", Metadata.ASCII_STRING_MARSHALLER)

@Suppress("SpellCheckingInspection")
private val googApiClientHeader: Metadata.Key<String> =
Metadata.Key.of("x-goog-api-client", Metadata.ASCII_STRING_MARSHALLER)

Expand All @@ -152,14 +145,10 @@ internal class DataConnectGrpcMetadata(

fun forSystemVersions(
firebaseApp: FirebaseApp,
dataConnectAuth: DataConnectAuth,
dataConnectAppCheck: DataConnectAppCheck,
connectorLocation: String,
parentLogger: Logger,
): DataConnectGrpcMetadata =
DataConnectGrpcMetadata(
dataConnectAuth = dataConnectAuth,
dataConnectAppCheck = dataConnectAppCheck,
connectorLocation = connectorLocation,
kotlinVersion = "${KotlinVersion.CURRENT}",
androidVersion = Build.VERSION.SDK_INT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,10 @@ internal class DataConnectGrpcRPCs(
requestId: String,
request: ExecuteMutationRequest,
callerSdkType: FirebaseDataConnect.CallerSdkType,
authToken: DataConnectAuth.GetAuthTokenResult?,
appCheckToken: DataConnectAppCheck.GetAppCheckTokenResult?,
): ExecuteMutationResponse {
val metadata = grpcMetadata.get(requestId, callerSdkType).metadata
val metadata = grpcMetadata.get(authToken, appCheckToken, callerSdkType)
val kotlinMethodName = "executeMutation(${request.operationName})"

logger.logGrpcSending(
Expand All @@ -198,6 +200,7 @@ internal class DataConnectGrpcRPCs(
metadata = metadata,
request = request.toStructProto(),
requestTypeName = "ExecuteMutationRequest",
authUid = authToken?.authUid,
)

val result = lazyGrpcStub.get().runCatching { executeMutation(request, metadata) }
Expand Down Expand Up @@ -252,15 +255,10 @@ internal class DataConnectGrpcRPCs(
request: ExecuteQueryRequest,
callerSdkType: FirebaseDataConnect.CallerSdkType,
fetchPolicy: FetchPolicy,
authToken: DataConnectAuth.GetAuthTokenResult?,
appCheckToken: DataConnectAppCheck.GetAppCheckTokenResult?,
): ExecuteQueryResult {
require(
fetchPolicy == FetchPolicy.PREFER_CACHE ||
fetchPolicy == FetchPolicy.CACHE_ONLY ||
fetchPolicy == FetchPolicy.SERVER_ONLY
) {
"Only PREFER_CACHE, CACHE_ONLY, and SERVER_ONLY are supported for now"
}
val (metadata, authToken) = grpcMetadata.get(requestId, callerSdkType)
val metadata = grpcMetadata.get(authToken, appCheckToken, callerSdkType)
val kotlinMethodName = "executeQuery(${request.operationName})"

logger.logGrpcSending(
Expand All @@ -270,6 +268,7 @@ internal class DataConnectGrpcRPCs(
metadata = metadata,
request = request.toStructProto(),
requestTypeName = "ExecuteQueryRequest",
authUid = authToken?.authUid,
)

val cacheInfo = queryCacheInfo(authToken, request)
Expand Down Expand Up @@ -478,11 +477,12 @@ internal class DataConnectGrpcRPCs(
grpcMethod: MethodDescriptor<*, *>,
metadata: Metadata,
request: Struct,
requestTypeName: String
requestTypeName: String,
authUid: String?,
) = debug {
val struct = buildStructProto {
put("RPC", grpcMethod.fullMethodName)
put("Metadata", metadata.toStructProto())
put("Metadata", metadata.toStructProto(authUid))
put(requestTypeName, request)
}
// Sort the keys in the output string to be more meaningful than alphabetical.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,6 @@ internal class FirebaseDataConnectImpl(
val grpcMetadata =
DataConnectGrpcMetadata.forSystemVersions(
firebaseApp = app,
dataConnectAuth = dataConnectAuth,
dataConnectAppCheck = dataConnectAppCheck,
connectorLocation = config.location,
parentLogger = logger,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class DataConnectAuthUnitTest {
@get:Rule val dataConnectLogLevelRule = DataConnectLogLevelRule()

private val rs = RandomSource.default()
private val accessTokenGenerator = Arb.dataConnect.accessToken()
private val accessTokenGenerator = Arb.dataConnect.authToken()
private val accessToken: String = accessTokenGenerator.next(rs)
private val requestId = Arb.dataConnect.requestId().next(rs)
private val mockInternalAuthProvider: InternalAuthProvider =
Expand Down
Loading
Loading