package org.pixeldroid.app.utils.di import org.pixeldroid.app.utils.api.PixelfedAPI import org.pixeldroid.app.utils.api.objects.Token import org.pixeldroid.app.utils.db.AppDatabase import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.runBlocking import okhttp3.* import org.pixeldroid.app.utils.api.PixelfedAPI.Companion.apiForUser import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) class APIModule { @Provides @Singleton fun providesAPIHolder(db: AppDatabase): PixelfedAPIHolder { return PixelfedAPIHolder(db) } } class TokenAuthenticator(val user: UserDatabaseEntity, val db: AppDatabase, val apiHolder: PixelfedAPIHolder) : Authenticator { private val pixelfedAPI = PixelfedAPI.createFromUrl(user.instance_uri) // Returns the number of tries for this response by walking through the priorResponses private fun Response.responseCount(): Int { var result = 1 var response: Response? = priorResponse while (response != null) { result++ response = response.priorResponse } return result } override fun authenticate(route: Route?, response: Response): Request? { if (response.responseCount() > 3) { return null // Give up, we've already failed to authenticate a couple times } // Refresh the access_token using a synchronous api request val newAccessToken: Token = try { runBlocking { pixelfedAPI.obtainToken( scope = "", grant_type = "refresh_token", refresh_token = user.refreshToken, client_id = user.clientId, client_secret = user.clientSecret ) } } catch (e: Exception){ return null } // Save the new access token and refresh token if (newAccessToken.access_token != null && newAccessToken.refresh_token != null) { db.userDao().updateAccessToken( newAccessToken.access_token, newAccessToken.refresh_token, user.user_id, user.instance_uri ) apiHolder.setToCurrentUser() } // Add new header to rejected request and retry it return response.request.newBuilder() .header("Authorization", "Bearer ${newAccessToken.access_token.orEmpty()}") .build() } } class PixelfedAPIHolder(private val db: AppDatabase){ var api: PixelfedAPI? = db.userDao().getActiveUser()?.let { setToCurrentUser(it) } fun setToCurrentUser( user: UserDatabaseEntity = db.userDao().getActiveUser()!! ): PixelfedAPI { val newAPI = apiForUser(user, db, this) api = newAPI return newAPI } }