I'm trying to inject a retrofit service into kodien. The API service uses Firebase Auth token for authenticating. So I've to pass the id token in request header as basic auth by the getting the token from Firebase Auth.getInstance().getIdToken() which gives the token in Task callback. Now the problem I'm facing is I've to pass the token to http client request interceptor. I'm unable to figure out how to do it.
I've already looked at some examples Kotlin Coroutines for solution. But I'm new to Kotlin Coroutines and wasn't able to pickup much.
Here is my kodien object.
override val kodein by Kodein.lazy {
import(androidXModule(this@App))
bind() from singleton { FirebaseDatabase.getInstance() }
bind<ApiService>() from singleton { RetrofitFactory.makeApiService() }
}
My retrofit factory.
object RetrofitFactory {
fun makeApiService(): ApiService {
FirebaseAuth.getInstance().currentUser?.getIdToken(true)?.addOnCompleteListener {
if (it.isSuccessful){
// HERE IS THE TOKEN
val token = it.result?.token
}
}
val client = OkHttpClient.Builder()
.addInterceptor {
val request = it.request().newBuilder()
.addHeader("authorization", "*** PASS TOKEN HERE ***")
.build()
it.proceed(request)
}
.build()
return Retrofit.Builder()
.client(client)
.baseUrl(BuildConfig.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
}
I might be getting this totally wrong or maybe there is a better way.
Any help is appreciated.
Thanks in advance.
I found a solution.
Create a TokenProvider
like below
class TokenProvider {
private var token: String = ""
fun get() = token
fun load() {
getToken {
token = it
}
}
private fun getToken(callback: (String) -> Unit) {
FirebaseAuth.getInstance().currentUser?.getIdToken(true)?.addOnCompleteListener {
if (it.isSuccessful){
callback(it.result?.token!!)
}
}
}
}
Inject the TokenProvider
in kodein
override val kodein by Kodein.lazy {
import(androidXModule(this@App))
bind() from singleton { TokenProvider() }
bind<ApiService>() with singleton { RetrofitFactory.makeApiService(instance()) }
}
Pass the TokenProvider
to makeServiceApi
as parameter and use it.
object RetrofitFactory {
fun makeApiService(tokenProvider: TokenProvider): ApiService {
val token = tokenProvider.get()
val client = OkHttpClient.Builder()
.addInterceptor {
val request = it.request()
.newBuilder()
.addHeader("authorization", "Bearer $token")
.build()
it.proceed(request)
}
.build()
return Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
}
And finally, load the token in App onCreate
override fun onCreate() {
super.onCreate()
val tokenProvider by instance<TokenProvider>()
tokenProvider.load()
}
There is an edge case with this solution. If you call the Api as soon as App loads. By that time, if the firebase token is not loaded. Then the Api call will fail.
This works for me because I'm not calling the Api on App load. Still looking for a solution which can solve for this edge case. One workaround for this is await the token load before calling Api.