Sorry new to jetpack compose here,
My interceptor for retrofit, uses old JWT value while making api calls from datastore. On relaunch it fetches latest and correct. But just after login, it uses old value of JWT from datastore. Why?
interface SplitFoolContainer {
val accountServiceRepository: AccountServiceRepository
val googleAuthUiClient: GoogleAuthUiClient
val dataStoreRepository:DataStoreRepository
val fellowRepository:FellowServiceRepo
}
class TokenInterceptor(private val tokenData:String) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request().newBuilder()
.addHeader("Authorization", "Bearer $tokenData")
.build()
return chain.proceed(request)
}
}
class AuthAuthenticator(private val context: Context):Authenticator{
override fun authenticate(route: Route?, response: Response): Request? {
val currentToken = runBlocking {
context.dataStore.data.map { preferences ->
preferences[stringPreferencesKey("is_jwt_token")] ?: ""
}.first()
}
Log.d("AuthAuthenticator", "authenticate: $currentToken")
synchronized(this){
val updatedToken = runBlocking {
context.dataStore.data.map { preferences ->
preferences[stringPreferencesKey("is_jwt_token")] ?: ""
}.first()
}
Log.d("AuthAuthenticator", "updated: $updatedToken")
return if (updatedToken != currentToken) response.request.newBuilder()
.header("Authorization", "Bearer $updatedToken")
.build() else null
}
}
}
private const val JWT_STORED_DATA_STORE = "JWT_STORED_DATA_STORE"
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
name = JWT_STORED_DATA_STORE
)
/**
* [AppContainer] implementation that provides instance of [BusRepository]
*/
class SplitFoolDataContainer(private val context: Context) : SplitFoolContainer {
/**
* Implementation for [BusRepository]
*/
private val baseUrl = "MYURL"
private fun createOkHttpClient(): OkHttpClient {
val map = runBlocking {
context.dataStore.data.map { preferences ->
preferences[stringPreferencesKey("is_jwt_token")] ?: ""
}.first()
}
Log.d("great gaurav", "createOkHttpClient: $map")
val tokenInterceptor = TokenInterceptor(tokenData = map)
// Return OkHttpClient with the token interceptor
return OkHttpClient.Builder()
// .authenticator(AuthAuthenticator(context))
.addInterceptor(tokenInterceptor)
.build()
}
private val retrofit: Retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(baseUrl)
.client(createOkHttpClient())
.build()
private val loginApiService: LoginService by lazy {
retrofit.create(LoginService::class.java)
}
private val fellowApiService: FellowApiService by lazy {
retrofit.create(FellowApiService::class.java)
}
override val googleAuthUiClient by lazy {
Log.d( "SplitFoolDataContainer", "googleAuthUiClient: ${context.dataStore.data}")
GoogleAuthUiClient(
context = context,
oneTapClient = Identity.getSignInClient(context)
)
}
override val dataStoreRepository: DataStoreRepository by lazy {
DataStoreRepository(
dataStore = context.dataStore
)
}
override val fellowRepository: FellowServiceRepo by lazy {
FellowServiceRepo(
fellowApiService = fellowApiService
)
}
}
my SplitContainer is the container of my app. Letme know if you want any inputs from me?
Based on your code snippet i think that problem is that your TokenInterceptor
doesn't know about token change. You are creating one instance of TokenInterceptor
once with token value available on app start (or when SplitFoolDataContainer
is created). So when you login and get new token, you are still using same interceptor instance with old token.
On next launch it works okay because TokenInterceptor is created with actual token which was saved before on the login.
EDIT:
As quick workaround you can collect token in the TokenInterceptor
.
class TokenInterceptor(dataStore: DataStore<Preferences>) : Interceptor {
//Holding actual token data
private var tokenData: String = ""
init {
val tokenFlow = dataStore.data.map {
it[stringPreferencesKey("is_jwt_token")]
}
CoroutineScope(Dispatchers.IO).launch {
//Collecting token value
tokenFlow.collect { newToken ->
//Storing new token value
tokenData = newToken ?: ""
}
}
}
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request().newBuilder()
.addHeader("Authorization", "Bearer $tokenData")
.build()
return chain.proceed(request)
}
}
As i said this is just quick workaround, i am not sure if dataStore is best for storing tokens because these collecting problems, i would much rather go with SharedPreferences.