optimize bml refresh flow

This commit is contained in:
2026-05-22 06:01:13 +05:00
parent 01e5c17284
commit b784085605
7 changed files with 143 additions and 65 deletions
@@ -30,6 +30,14 @@ class BmlAccountClient {
return parseDashboard(json ?: return emptyList(), loginTag, profileName, profileId)
}
/** Lightweight call to verify the session is alive. Throws [AuthExpiredException] on 401/419. */
fun checkProfile(session: BmlSession) {
val resp = client.newCall(bmlApiRequest(session, "$BML_BASE_URL/api/mobile/profile")).execute()
val code = resp.code
resp.close()
if (code == 401 || code == 419) throw AuthExpiredException()
}
fun fetchUserInfo(session: BmlSession): BmlUserInfo? {
val resp = client.newCall(bmlApiRequest(session, "$BML_BASE_URL/api/mobile/userinfo")).execute()
val json = resp.body?.string() ?: return null
@@ -310,13 +310,47 @@ class BmlLoginFlow {
val tokenJson = tokenResp.body?.string() ?: throw Exception("Empty token response")
tokenResp.close()
val accessToken = JSONObject(tokenJson).optString("access_token")
val tokenObj = JSONObject(tokenJson)
val accessToken = tokenObj.optString("access_token")
.takeIf { it.isNotBlank() } ?: throw Exception("Token exchange failed")
val refreshToken = tokenObj.optString("refresh_token", "")
val expiresIn = tokenObj.optLong("expires_in", 0L)
val expiresAt = if (expiresIn > 0) System.currentTimeMillis() + expiresIn * 1000L else 0L
val session = BmlSession(accessToken = accessToken, deviceId = deviceId)
val session = BmlSession(accessToken = accessToken, deviceId = deviceId, refreshToken = refreshToken, expiresAt = expiresAt)
val accounts = BmlAccountClient().fetchAccounts(session, loginTag, profileName, profileId)
return Pair(session, accounts)
}
// ─── Token refresh ───────────────────────────────────────────────────────
/**
* Uses the saved refresh token to obtain a new access token without re-login.
* Returns a new [BmlSession] with updated tokens.
*/
fun refreshSession(session: BmlSession): BmlSession {
val body = FormBody.Builder()
.add("grant_type", "refresh_token")
.add("refresh_token", session.refreshToken)
.add("client_id", CLIENT_ID)
.add("Device-ID", session.deviceId)
.add("User-Agent", APP_USER_AGENT)
.add("x-app-version", APP_VERSION)
.build()
val resp = newBmlApiClient().newCall(
Request.Builder().url("$BASE_URL/oauth/token").post(body)
.header("User-Agent", WEB_USER_AGENT).build()
).execute()
val json = resp.body?.string() ?: throw Exception("Empty refresh response")
resp.close()
val obj = JSONObject(json)
val newAccess = obj.optString("access_token").takeIf { it.isNotBlank() }
?: throw Exception("Token refresh failed")
val newRefresh = obj.optString("refresh_token", "").ifBlank { session.refreshToken }
val expiresIn = obj.optLong("expires_in", 0L)
val expiresAt = if (expiresIn > 0) System.currentTimeMillis() + expiresIn * 1000L else 0L
return BmlSession(accessToken = newAccess, deviceId = session.deviceId, refreshToken = newRefresh, expiresAt = expiresAt)
}
// ─── Parsing ──────────────────────────────────────────────────────────────
/**
@@ -4,8 +4,12 @@ import sh.sar.basedbank.api.models.BankAccount
data class BmlSession(
val accessToken: String,
val deviceId: String
)
val deviceId: String,
val refreshToken: String = "",
val expiresAt: Long = 0L // Unix millis; 0 = unknown
) {
fun isExpired() = expiresAt > 0L && System.currentTimeMillis() >= expiresAt
}
data class BmlProfile(
val profileId: String,