TP-69604 | Firebase auth helper integrated (#11224)

Co-authored-by: Varun Jain <varun.jain@navi.com>
Co-authored-by: Shivam Goyal <shivam.goyal@navi.com>
This commit is contained in:
Kishan Kumar
2024-06-13 14:38:51 +05:30
committed by GitHub
parent e009d30d09
commit 83e50a7fe9
8 changed files with 233 additions and 20 deletions

View File

@@ -84,6 +84,8 @@ import com.navi.chat.utils.NaviChatAnalytics.Companion.CHAT_DEEPLINK_CLICK
import com.navi.chat.utils.NaviChatAnalytics.Companion.CHAT_MESSAGE
import com.navi.chat.utils.NaviChatAnalytics.Companion.CHAT_MESSAGE_WIDGET_WITH_RECEIPTS_NOT_NULL
import com.navi.chat.utils.NaviChatAnalytics.Companion.EXISTING_CHAT_TRIGGERED
import com.navi.chat.utils.NaviChatAnalytics.Companion.FIREBASE_AUTHENTICATION_FAILURE
import com.navi.chat.utils.NaviChatAnalytics.Companion.FIREBASE_AUTHENTICATION_SUCCESS
import com.navi.chat.utils.NaviChatAnalytics.Companion.INSERTING_NEW_MESSAGE_TO_CHAT_ADAPTER
import com.navi.chat.utils.NaviChatAnalytics.Companion.NAVI_CHAT_WIDGET
import com.navi.chat.utils.NaviChatAnalytics.Companion.NETWORK_AVAILABLE_IN_CHAT
@@ -101,6 +103,9 @@ import com.navi.common.constants.EMPTY
import com.navi.common.model.common.NetworkConnectivityNudgeData
import com.navi.common.ui.fragment.BaseFragment
import com.navi.common.utils.Constants
import com.navi.common.utils.FirebaseAuthHelper
import com.navi.common.utils.FirebaseAuthStatus
import com.navi.common.utils.getErrorData
import com.navi.common.utils.getSessionId
import com.navi.common.utils.log
import com.navi.common.utils.observeNonNull
@@ -189,6 +194,8 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
private var latestMessageLocation: Int? = null
private var productConfigId: String? = null
@Inject lateinit var firebaseAuthHelper: FirebaseAuthHelper
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -212,7 +219,6 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
readArguments()
initUI()
initObservers()
showChatLoader()
fetchChatData()
fetchChatConfig()
}
@@ -868,7 +874,26 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
}
private fun fetchChatData() {
fetchChatConversations(conversationId.orEmpty())
showChatLoader()
firebaseAuthHelper.isLoggedIn { status ->
if (status is FirebaseAuthStatus.Success) {
fetchChatConversations(conversationId.orEmpty())
crmEventTracker.sendEvent(
FIREBASE_AUTHENTICATION_SUCCESS,
hashMapOf(SESSION_ID to getSessionId().orEmpty())
)
} else {
setFirebaseAuthError()
crmEventTracker.sendEvent(
FIREBASE_AUTHENTICATION_FAILURE,
hashMapOf(SESSION_ID to getSessionId().orEmpty())
)
}
}
}
private fun setFirebaseAuthError() {
naviChatViewModel.setError(listOf(getErrorData(context = requireContext())))
}
private fun fetchChatConfig() {

View File

@@ -159,6 +159,8 @@ class NaviChatAnalytics private constructor() {
const val CHATSCREEN_TOP_BACK_BUTTON_CLICK = "chatscreen_top_back_button_click"
const val SESSION_ID = "session_id"
const val LISTEN_TO_FIRESTORE_SERVER_UPDATES = "listen_to_firestore_server_updates"
const val FIREBASE_AUTHENTICATION_SUCCESS = "firebase_authentication_success"
const val FIREBASE_AUTHENTICATION_FAILURE = "firebase_authentication_failure"
val naviChatAnalytics: NaviChatAnalytics by lazy { Holder.INSTANCE }
}

View File

@@ -0,0 +1,15 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.common.network.models
import com.google.gson.annotations.SerializedName
data class FirebaseAuthTokenResponse(
@SerializedName("externalCustomerId") val externalCustomerId: String? = null,
@SerializedName("notificationAuthToken") val authToken: String? = null,
)

View File

@@ -1,6 +1,6 @@
/*
*
* * Copyright © 2022-2023 by Navi Technologies Limited
* * Copyright © 2022-2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
@@ -23,9 +23,27 @@ import com.navi.common.csat.models.CSATSubmitResponse
import com.navi.common.csat.models.NetPromoterScoreRequest
import com.navi.common.geocoding.model.network.GeocodingRequest
import com.navi.common.geocoding.model.network.GeocodingResponse
import com.navi.common.model.*
import com.navi.common.model.vkyc.*
import com.navi.common.model.AdditionalAsyncDataResponse
import com.navi.common.model.AsyncRequestData
import com.navi.common.model.CommunicationAppLaunchData
import com.navi.common.model.DeviceDetail
import com.navi.common.model.FeedbackResponse
import com.navi.common.model.FeedbackSubmitData
import com.navi.common.model.KycStatus
import com.navi.common.model.PaymentOrderDetail
import com.navi.common.model.ProfileResponse
import com.navi.common.model.PushNotificationData
import com.navi.common.model.RedirectPageStatus
import com.navi.common.model.SubmitPermissionRequestData
import com.navi.common.model.SubmitPermissionResponse
import com.navi.common.model.UploadDataAsyncResponse
import com.navi.common.model.vkyc.VkycCancelStatus
import com.navi.common.model.vkyc.VkycSettingsWithLocResponse
import com.navi.common.model.vkyc.VkycStatus
import com.navi.common.model.vkyc.WaitingForLongPageDetailsResponse
import com.navi.common.model.vkyc.WaitingPageDetailsResponse
import com.navi.common.network.BaseHttpClient
import com.navi.common.network.models.FirebaseAuthTokenResponse
import com.navi.common.network.models.GenericResponse
import com.navi.common.network.models.SuccessResponse
import com.navi.common.permission.UpdateDevicePermissionsRequest
@@ -37,8 +55,20 @@ import com.navi.vkyc.models.PermissionDetailsResponse
import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.Response
import retrofit2.http.*
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Multipart
import retrofit2.http.PATCH
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Part
import retrofit2.http.PartMap
import retrofit2.http.Path
import retrofit2.http.Query
import retrofit2.http.QueryMap
import retrofit2.http.Tag
import retrofit2.http.Url
interface RetrofitService {
@@ -266,4 +296,7 @@ interface RetrofitService {
@Header(BaseHttpClient.X_TARGET) target: String = CDS,
@Body permissionData: SubmitPermissionRequestData
): Response<GenericResponse<SubmitPermissionResponse>>
@GET("/firebase/v1/token/generate")
suspend fun generateFirebaseAuthToken(): Response<GenericResponse<FirebaseAuthTokenResponse>>
}

View File

@@ -0,0 +1,18 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.common.repo
import com.navi.common.network.retrofit.ResponseCallback
import com.navi.common.utils.retrofitService
import javax.inject.Inject
class FirebaseAuthRepository @Inject constructor() : ResponseCallback() {
suspend fun fetchFirebaseAuthToken() =
apiResponseCallback(retrofitService().generateFirebaseAuthToken())
}

View File

@@ -1,6 +1,6 @@
/*
*
* * Copyright © 2019-2023 by Navi Technologies Limited
* * Copyright © 2019-2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
@@ -941,10 +941,15 @@ abstract class BaseActivity :
protected fun refreshFirebaseToken(authToken: String) {
if (authToken.isEmpty()) {
notificationAnalyticsTracker.trackNotificationAuthTokenEmpty()
notificationAnalyticsTracker.trackNotificationAuthTokenEmpty(
moduleName = getCurrentModuleName()
)
hideLoader()
} else {
notificationAnalyticsTracker.trackNotificationAuthTokenNotEmpty(authToken = authToken)
notificationAnalyticsTracker.trackNotificationAuthTokenNotEmpty(
authToken = authToken,
moduleName = getCurrentModuleName()
)
FirebaseAuth.getInstance()
.signInWithCustomToken(authToken)
.addOnCompleteListener { task ->
@@ -952,12 +957,14 @@ abstract class BaseActivity :
hideLoader()
notificationAnalyticsTracker.trackNotificationAuthTokenSigninFailure(
authToken = authToken,
stacktrace = task.exception?.stackTraceToString()
stacktrace = task.exception?.stackTraceToString(),
moduleName = getCurrentModuleName()
)
return@addOnCompleteListener
}
notificationAnalyticsTracker.trackNotificationAuthTokenSigninSuccess(
authToken = authToken
authToken = authToken,
moduleName = getCurrentModuleName()
)
PreferenceManager.setStringPreference(
CommonPrefConstants.USER_ID,
@@ -968,7 +975,8 @@ abstract class BaseActivity :
.addOnFailureListener {
notificationAnalyticsTracker.trackNotificationAuthTokenSigninFailure(
authToken = authToken,
stacktrace = it.stackTraceToString()
stacktrace = it.stackTraceToString(),
moduleName = getCurrentModuleName()
)
hideLoader()
}

View File

@@ -104,29 +104,42 @@ class CommonNaviAnalytics private constructor() {
}
inner class Notification {
fun trackNotificationAuthTokenEmpty() {
NaviTrackEvent.trackEvent(eventName = "Notification_Auth_Token_Empty")
fun trackNotificationAuthTokenEmpty(moduleName: String) {
NaviTrackEvent.trackEvent(
eventName = "Notification_Auth_Token_Empty",
eventValues = mapOf("module_name" to moduleName)
)
}
fun trackNotificationAuthTokenNotEmpty(authToken: String?) {
fun trackNotificationAuthTokenNotEmpty(authToken: String?, moduleName: String) {
NaviTrackEvent.trackEvent(
eventName = "Notification_Auth_Token_Not_Empty",
eventValues = mapOf("auth_token" to authToken.orEmpty())
eventValues =
mapOf("auth_token" to authToken.orEmpty(), "module_name" to moduleName)
)
}
fun trackNotificationAuthTokenSigninSuccess(authToken: String?) {
fun trackNotificationAuthTokenSigninSuccess(authToken: String?, moduleName: String) {
NaviTrackEvent.trackEvent(
eventName = "Notification_Auth_Token_SignIn_Success",
eventValues = mapOf("auth_token" to authToken.orEmpty())
eventValues =
mapOf("auth_token" to authToken.orEmpty(), "module_name" to moduleName)
)
}
fun trackNotificationAuthTokenSigninFailure(authToken: String?, stacktrace: String?) {
fun trackNotificationAuthTokenSigninFailure(
authToken: String?,
stacktrace: String?,
moduleName: String
) {
NaviTrackEvent.trackEvent(
eventName = "Notification_Auth_Token_SignIn_Failure",
eventValues =
mapOf("auth_token" to authToken.orEmpty(), "stacktrace" to stacktrace.orEmpty())
mapOf(
"auth_token" to authToken.orEmpty(),
"stacktrace" to stacktrace.orEmpty(),
"module_name" to moduleName
)
)
}
}

View File

@@ -0,0 +1,99 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.common.utils
import com.google.firebase.auth.FirebaseAuth
import com.navi.base.sharedpref.CommonPrefConstants
import com.navi.base.sharedpref.PreferenceManager
import com.navi.base.utils.BaseUtils
import com.navi.common.model.ModuleNameV2
import com.navi.common.repo.FirebaseAuthRepository
import javax.inject.Inject
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class FirebaseAuthHelper @Inject constructor(private val repository: FirebaseAuthRepository) {
private val moduleName: String = ModuleNameV2.COMMON.name
private val notificationAnalyticsTracker = CommonNaviAnalytics.naviAnalytics.Notification()
private val coroutineScope =
CoroutineScope(
Dispatchers.IO + CoroutineExceptionHandler { _, throwable -> throwable.log() }
)
fun isLoggedIn(onAuth: (authStatus: FirebaseAuthStatus) -> Unit) {
if (FirebaseAuth.getInstance().currentUser != null) {
onAuth(FirebaseAuthStatus.Success)
} else {
fetchFirebaseAuthToken { authStatus -> onAuth(authStatus) }
}
}
private fun fetchFirebaseAuthToken(onAuth: (authStatus: FirebaseAuthStatus) -> Unit) {
coroutineScope.launch {
val tokenData = repository.fetchFirebaseAuthToken()
if (tokenData.error == null && tokenData.errors.isNullOrEmpty()) {
tokenData.data?.authToken?.let { refreshFirebaseToken(it, onAuth) }
} else {
onAuth(FirebaseAuthStatus.Failed)
}
}
}
private fun refreshFirebaseToken(
authToken: String,
onAuth: (authStatus: FirebaseAuthStatus) -> Unit,
) {
if (authToken.isEmpty()) {
notificationAnalyticsTracker.trackNotificationAuthTokenEmpty(moduleName = moduleName)
} else {
notificationAnalyticsTracker.trackNotificationAuthTokenNotEmpty(
authToken = authToken,
moduleName = moduleName
)
FirebaseAuth.getInstance()
.signInWithCustomToken(authToken)
.addOnCompleteListener { task ->
if (task.isSuccessful.not()) {
notificationAnalyticsTracker.trackNotificationAuthTokenSigninFailure(
authToken = authToken,
stacktrace = task.exception?.stackTraceToString(),
moduleName = moduleName
)
onAuth(FirebaseAuthStatus.Failed)
return@addOnCompleteListener
}
onAuth(FirebaseAuthStatus.Success)
notificationAnalyticsTracker.trackNotificationAuthTokenSigninSuccess(
authToken = authToken,
moduleName = moduleName
)
PreferenceManager.setStringPreference(
CommonPrefConstants.USER_ID,
FirebaseAuth.getInstance().currentUser?.uid.orEmpty()
)
BaseUtils.setFirebaseTokenRefreshed(true)
}
.addOnFailureListener {
notificationAnalyticsTracker.trackNotificationAuthTokenSigninFailure(
authToken = authToken,
stacktrace = it.stackTraceToString(),
moduleName = moduleName
)
onAuth(FirebaseAuthStatus.Failed)
}
}
}
}
sealed class FirebaseAuthStatus {
data object Success : FirebaseAuthStatus()
data object Failed : FirebaseAuthStatus()
}