feat(android): centralize mobile session refresh and password change
This commit is contained in:
+98
@@ -0,0 +1,98 @@
|
|||||||
|
package com.korexlabs.dhinspeccion.data
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.korexlabs.dhinspeccion.BuildConfig
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
|
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import retrofit2.HttpException
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||||
|
import retrofit2.http.Body
|
||||||
|
import retrofit2.http.Header
|
||||||
|
import retrofit2.http.POST
|
||||||
|
|
||||||
|
data class ChangeMobilePasswordRequest(
|
||||||
|
val currentPassword: String,
|
||||||
|
val newPassword: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class ChangeMobilePasswordResponse(
|
||||||
|
val status: String,
|
||||||
|
val mustChangePassword: Boolean,
|
||||||
|
)
|
||||||
|
|
||||||
|
private interface MobileSessionApi {
|
||||||
|
@POST("auth/mobile/refresh")
|
||||||
|
suspend fun refresh(@Body request: RefreshRequest): MobileSessionResponse
|
||||||
|
|
||||||
|
@POST("auth/change-password")
|
||||||
|
suspend fun changePassword(
|
||||||
|
@Header("Authorization") authorization: String,
|
||||||
|
@Body request: ChangeMobilePasswordRequest,
|
||||||
|
): ChangeMobilePasswordResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Único coordinador de sesión del proceso Android.
|
||||||
|
*
|
||||||
|
* Todos los repositorios comparten el mismo gate de refresh para que un token rotativo no sea
|
||||||
|
* consumido en paralelo por Inventario, Hallazgos y Actas.
|
||||||
|
*/
|
||||||
|
class MobileSessionCoordinator private constructor(context: Context) {
|
||||||
|
private val store = SecureSessionStore(context.applicationContext)
|
||||||
|
private val gate = SessionRefreshGate()
|
||||||
|
private val moshi = Moshi.Builder().addLast(KotlinJsonAdapterFactory()).build()
|
||||||
|
private val api: MobileSessionApi = Retrofit.Builder()
|
||||||
|
.baseUrl(BuildConfig.API_BASE_URL)
|
||||||
|
.client(OkHttpClient.Builder().build())
|
||||||
|
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||||
|
.build()
|
||||||
|
.create(MobileSessionApi::class.java)
|
||||||
|
|
||||||
|
fun currentSession(): StoredSession? = store.load()
|
||||||
|
|
||||||
|
fun save(response: MobileSessionResponse): StoredSession = store.save(response)
|
||||||
|
|
||||||
|
fun clear() = store.clear()
|
||||||
|
|
||||||
|
suspend fun <T> authorized(block: suspend (StoredSession) -> T): T {
|
||||||
|
var session = store.load() ?: throw IllegalStateException("Sesión no iniciada")
|
||||||
|
try {
|
||||||
|
return block(session)
|
||||||
|
} catch (error: HttpException) {
|
||||||
|
if (error.code() != 401) throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
session = gate.refreshIfNeeded(
|
||||||
|
previousRefreshToken = session.refreshToken,
|
||||||
|
load = store::load,
|
||||||
|
save = store::save,
|
||||||
|
clear = store::clear,
|
||||||
|
refresh = { token -> api.refresh(RefreshRequest(token)) },
|
||||||
|
)
|
||||||
|
return block(session)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun changePassword(currentPassword: String, newPassword: String): StoredSession {
|
||||||
|
val sessionUsed = authorized { session ->
|
||||||
|
api.changePassword(
|
||||||
|
authorization = "Bearer ${session.accessToken}",
|
||||||
|
request = ChangeMobilePasswordRequest(currentPassword, newPassword),
|
||||||
|
)
|
||||||
|
session
|
||||||
|
}
|
||||||
|
val latest = store.load() ?: sessionUsed
|
||||||
|
return store.save(latest.copy(mustChangePassword = false))
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@Volatile
|
||||||
|
private var instance: MobileSessionCoordinator? = null
|
||||||
|
|
||||||
|
fun get(context: Context): MobileSessionCoordinator =
|
||||||
|
instance ?: synchronized(this) {
|
||||||
|
instance ?: MobileSessionCoordinator(context.applicationContext).also { instance = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user