What happens when an ID token expires, and when does firebase use the refresh token to get a new id token?
I have a very basic Android app, in which a user can login with email and password. That part works and after they login I can check for the currentUser
and sure enough it has a user object (and it’s isAuthenticated
property is true). At this point that’s all the app does (I’m just starting it). The user can login and see a “Welcome” screen. But then I found something weird. I deleted the user from the Firebase console. When I start the app back up, the currentUser
is still there and still logged in (isAuthenticated = true
). That’s not all that surprising. The ID token doesn’t expire for 1 hour, so it would make sense that the user is still logged in for a period of time. But, I waited a LONG time (over 24 hours) and the user is STILL logged in. That surprised me, because after an hour the id token should’ve expired and a refresh token used to get a new id token (which would fail since the user no longer exists). But that hasn’t happened. So that leads me to the above two questions.
When does firebase use the refresh token to get a new id token? I can kind of understand why my user might not get logged out. I don’t have any functionality in my app (yet) where the user can DO something that would trigger a token refresh. OK, but what kinds of activities will trigger a refresh? Or do I need to make a call to refresh the token? If so, how do I know when to make that call? How do I know when the ID token is expired?
Also, in my scenario above, once the ID token does expire, I was surprised that the user object still shows isAuthenticated = true
. What happens once the ID token expires? Is there some way to know it’s expired?
Below is a simple screen that demonstrates what I've described.
When it loads it checks the User object to see if it exists (and if it's isAnonymous
property is true or false).
It also uses a LaunchedEffect to check again after 3 seconds (in case it takes firebase a little bit to initialize and try to refresh the token).
@Composable
fun HomeScreen(onLoginClick: ()->Unit = { /* navigate to login screen */ }) {
LaunchedEffect(Unit) {
Firebase.auth.addAuthStateListener { auth ->
Log.d("LOGIN", "Current User: ${auth.currentUser}")
}
}
var isLoggedIn by remember {
val loginState = !(Firebase.auth.currentUser?.isAnonymous ?: true)
Log.d("LOGIN", "Is logged in: $loginState")
mutableStateOf(loginState)
}
LaunchedEffect(Unit) {
delay(3_000)
val loginState = !(Firebase.auth.currentUser?.isAnonymous ?: true)
Log.d("LOGIN", "Is logged in after delay: $loginState")
isLoggedIn = loginState
}
Column {
if (isLoggedIn) {
Text("Welcome, Authenticated User!")
} else {
Button(onClick = { onLoginClick() }) {
Text("Log In")
}
}
}
}
When I call the above composable with a logged in user it displays the text "Welcome, Authenticated User!". I also see two log messages for "is logged in: true", and "is logged in after delay: true".
Then I go in to the firebase console and delete the user. I also close the app. Now I wait an entire day (24+ hours). I launch the app again and it displays:
"Welcome Authenticated User" and the same two log messages, ""is logged in: true", and "is logged in after delay: true".
I would expect it to display the Login button and the log messages to contain "false" for the isLoggedIn
property, because either the currentUser
is null, or it's isAnonymous
property is true. But neither is the case. Even though the user hasn't existed for over a day in Firebase, currentUser
is still set and it's isAnonymous
property is false.
The Output log is:
14:16:29.448 LOGIN D Is logged in: true
14:16:29.944 LOGIN D Current User: com.google.firebase.auth.internal.zzx@bd4236a
14:16:32.757 LOGIN D Is logged in after delay: true