Android: FirebasetokenProvider: нет текущего пользователя при запросе токенаAndroid

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Android: FirebasetokenProvider: нет текущего пользователя при запросе токена

Сообщение Anonymous »

После замены Firebase BOM версии 33.1.2 мы начали сталкиваться с проблемами, где Authinstance.currentUser возвращает NULL, хотя пользователь недавно вошел в систему. Чтобы смягчить это, я добавил слушателя: < /p>

Код: Выделить всё

val authStateFlow: Flow = callbackFlow {
val l = FirebaseAuth.AuthStateListener { trySend(it.currentUser) }
authInstance.addAuthStateListener(l)
awaitClose { authInstance.removeAuthStateListener(l) }
}
< /code>
Мы также добавили что -то глупое, например, позволить пользователю перейти в приложение, если есть локальный пользователь, и ждать этого токена Auth в течение минуты, чтобы инициализировать себя, но теперь это вызывает проблемы, когда мы пытаемся получить новый токен.  Пример: < /p>
val localUser = withTimeoutOrNull(250) {
userRepository.user.firstOrNull { it != null }
}
val timeout = localUser?.let {
holdSplash = false
60_000L
} ?: run {
1_000L
}
val firebaseUser = withTimeoutOrNull(timeout) {
userViewModel.initializedAuthState.first()
}
< /code>
Вот как мы используем поставщик токенов, который заботится о освежении токена, когда мы делаем запросы на сервер: < /p>
/**
* Class that centralizes token caching/refresh behind a small TokenProvider that:*
* * stores the last token + its expiration,
* * prevents refresh stampedes with a Mutex,
* * can be invalidated by an IdTokenListener/AuthStateListener.
*/
class FirebaseTokenProvider(
private val coPilotCrashlyticsHelper: CoPilotCrashlyticsHelper,
private val authSource: FirebaseAuthSource,
private val timeoutMs: Long = 60_000
) {
@Volatile private var cachedToken: String? = null
@Volatile private var expiryEpochMs: Long = 0L
private val mutex = Mutex()

init {
// Invalidate cache when Firebase rotates tokens or user changes.
authSource.addIdTokenListener {
synchronized(this) {
cachedToken = null
expiryEpochMs = 0L
}
}
authSource.addAuthStateListener {
synchronized(this) {
if (authSource.currentUser() == null) {
cachedToken = null
expiryEpochMs = 0L
}
}
}
}

suspend fun getValidTokenOrNull(forceRefresh: Boolean = false): String? {
val now = System.currentTimeMillis()
val token = cachedToken
if (!forceRefresh && token != null && now < expiryEpochMs - 5_000) {
return token
}

return mutex.withLock {
val stillNow = System.currentTimeMillis()
if (!forceRefresh && cachedToken != null && stillNow < expiryEpochMs - 5_000) {
return cachedToken!!
}
val user = authSource.currentUser() ?: run {
coPilotCrashlyticsHelper.log("FirebaseTokenProvider: No current user when requesting token.")
return null
}
val result: GetTokenResult = withTimeout(timeoutMs) {
user.getIdToken( forceRefresh /* forceRefresh = */).await()
}
val newToken = result.token ?: run {
coPilotCrashlyticsHelper.log("FirebaseTokenProvider: Got null token for user ${user.uid}.")
return null
}
val expSeconds = result.expirationTimestamp // seconds since epoch.
expiryEpochMs = expSeconds * 1000
cachedToken = newToken
newToken
}
}

fun clear() {
synchronized(this) {
cachedToken = null
expiryEpochMs = 0L
}
}
}
, а затем Authenticator/Interceptor, который мы используем для нашего OKTTP/MOROFIT

Код: Выделить всё

class FirebaseAuthenticator(
private val firebaseCrashlyticsHelper: CoPilotCrashlyticsHelper,
private val tokenProvider: FirebaseTokenProvider) : Authenticator {

override fun authenticate(route: Route?, response: Response): Request? {
// Prevent infinite loops: only retry once per chain.
if (responseCount(response) >= 1) return null

// Only retry on 401; 403 usually means "permission" and a retry rarely helps.
if (response.code != 401 || response.code != 403) return null

firebaseCrashlyticsHelper.log("Refreshing token.  Received response: $response")

val newToken = runCatching {
runBlocking { tokenProvider.getValidTokenOrNull(forceRefresh = true) }
}.getOrNull() ?: return null

// If the header already uses this token, don't loop.
val prior = response.request.header("Authorization")
val bearer = "Bearer $newToken"
if (prior == bearer) return null

return response.request.newBuilder()
.header("Authorization", bearer)
.build()
}

private fun responseCount(response: Response): Int {
var count = 1
var r: Response? = response.priorResponse
while (r != null) { count++; r = r.priorResponse }
return count
}
}

/** Interceptor for to provide Firebase token for HTTP requests to [CoPilotApiService]. */
class FirebaseAuthInterceptor(
private val tokenProvider: FirebaseTokenProvider
) : Interceptor {

override fun intercept(chain: Interceptor.Chain): Response {
val original = chain.request()

// If there is no user, proceed without header.
val token = runCatching { runBlocking { tokenProvider.getValidTokenOrNull(forceRefresh = false) } }
.getOrNull()

val request = if (token != null) {
original.newBuilder()
.header("Authorization", "Bearer $token")
.build()
} else {
original
}

return chain.proceed(request)
}
}

...

okHttpClientBuilder.addInterceptor(FirebaseAuthInterceptor(firebaseTokenProvider))
okHttpClientBuilder.authenticator(firebaseAuthenticator)
Любая идея, что может происходить в новых библиотеках Firebase, которые мы забыли настроить/обновить и почему CurrentUser очищается из магазина ключей?
Спасибо!

Подробнее здесь: https://stackoverflow.com/questions/797 ... ting-token
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»