forked from shihaam/thijooree
rever commit/4b1c2419 but keeping contact picker fix
This commit is contained in:
@@ -19,7 +19,7 @@ class MibContactsClient {
|
|||||||
|
|
||||||
private fun cookieHeader(session: MibSession) =
|
private fun cookieHeader(session: MibSession) =
|
||||||
"mbmodel=IOS-1.0; xxid=${session.xxid}; IBSID=${session.xxid}; " +
|
"mbmodel=IOS-1.0; xxid=${session.xxid}; IBSID=${session.xxid}; " +
|
||||||
"mbnonce=${MibNonce.generate(session.nonceGenerator)}; time-tracker=597"
|
"mbnonce=${session.nonceGenerator}; time-tracker=597"
|
||||||
|
|
||||||
private fun Request.Builder.withSessionHeaders(session: MibSession): Request.Builder = this
|
private fun Request.Builder.withSessionHeaders(session: MibSession): Request.Builder = this
|
||||||
.header("Cookie", cookieHeader(session))
|
.header("Cookie", cookieHeader(session))
|
||||||
|
|||||||
@@ -52,18 +52,6 @@ class MibLoginFlow(private val credentialStore: CredentialStore) {
|
|||||||
|
|
||||||
// ─── Public entry point ───────────────────────────────────────────────────
|
// ─── Public entry point ───────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
|
||||||
* Restores a previously persisted session without any network call. Sets the session and
|
|
||||||
* stored credentials so that doRequest() can do an automatic re-login on 419 if needed.
|
|
||||||
*/
|
|
||||||
fun restoreSession(session: MibSession, username: String, passwordHash: String, otpSeed: String) {
|
|
||||||
loginId = username
|
|
||||||
storedUsername = username
|
|
||||||
storedPasswordHash = passwordHash
|
|
||||||
storedOtpSeed = otpSeed
|
|
||||||
lastSession = session
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Full login flow. Automatically handles first-time device registration
|
* Full login flow. Automatically handles first-time device registration
|
||||||
* vs. subsequent logins using stored key1/key2.
|
* vs. subsequent logins using stored key1/key2.
|
||||||
@@ -257,14 +245,10 @@ class MibLoginFlow(private val credentialStore: CredentialStore) {
|
|||||||
"" // fall through to recovery below
|
"" // fall through to recovery below
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect expired session: HTTP 419 (empty response), reasonCode 505, or digestFail
|
// Detect expired session: HTTP 419 (empty response) or JSON with reasonCode 505
|
||||||
// (digestFail / reasonCode 501 means the nonce is invalid for this session — happens
|
|
||||||
// when a persisted session is loaded after the server has already expired it)
|
|
||||||
val isExpired = response.isEmpty() ||
|
val isExpired = response.isEmpty() ||
|
||||||
(response.trimStart().startsWith("{") && run {
|
(response.trimStart().startsWith("{") &&
|
||||||
val obj = JSONObject(response)
|
JSONObject(response).optString("reasonCode") == "505")
|
||||||
obj.optString("reasonCode") == "505" || obj.optBoolean("digestFail", false)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (isExpired && inRelogin.compareAndSet(false, true)) {
|
if (isExpired && inRelogin.compareAndSet(false, true)) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -734,75 +734,13 @@ fun applyNavLabelVisibility() {
|
|||||||
val mibJobs = mibLoginIds.mapNotNull { loginId ->
|
val mibJobs = mibLoginIds.mapNotNull { loginId ->
|
||||||
val creds = store.loadMibCredentials(loginId) ?: return@mapNotNull null
|
val creds = store.loadMibCredentials(loginId) ?: return@mapNotNull null
|
||||||
loginId to async(Dispatchers.IO) {
|
loginId to async(Dispatchers.IO) {
|
||||||
val app = application as BasedBankApp
|
|
||||||
|
|
||||||
// Prefer in-memory session; fall back to persisted session on restart.
|
|
||||||
var existingSession = app.mibSessions[loginId]
|
|
||||||
var existingFlow = app.mibLoginFlows[loginId]
|
|
||||||
val cachedProfiles = app.mibProfilesMap[loginId]?.takeIf { it.isNotEmpty() }
|
|
||||||
?: store.loadMibProfiles(loginId)
|
|
||||||
|
|
||||||
if (existingSession == null) {
|
|
||||||
val persisted = store.loadMibSession(loginId)
|
|
||||||
if (persisted != null && cachedProfiles.isNotEmpty()) {
|
|
||||||
val flow = MibLoginFlow(CredentialStore(this@HomeActivity))
|
|
||||||
flow.restoreSession(persisted, creds.username, creds.passwordHash, creds.otpSeed)
|
|
||||||
existingSession = persisted
|
|
||||||
existingFlow = flow
|
|
||||||
app.mibSessions[loginId] = persisted
|
|
||||||
app.mibLoginFlows[loginId] = flow
|
|
||||||
app.mibProfilesMap[loginId] = cachedProfiles
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try the existing session first — avoids a full re-login on every refresh.
|
|
||||||
// doRequest() inside fetchAllProfiles() handles 419 (session expired) by
|
|
||||||
// automatically re-logging in and retrying, so this is safe to call directly.
|
|
||||||
if (existingSession != null && existingFlow != null && cachedProfiles.isNotEmpty()) {
|
|
||||||
existingFlow.onSessionRefreshed = { newSession, newProfiles ->
|
|
||||||
app.mibSessions[loginId] = newSession
|
|
||||||
app.mibProfilesMap[loginId] = newProfiles
|
|
||||||
store.saveMibSession(loginId, newSession)
|
|
||||||
store.saveMibProfiles(loginId, newProfiles)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
val visibleProfiles = cachedProfiles.filterVisibleProfiles(loginId)
|
|
||||||
val accounts = existingFlow.fetchAllProfiles(existingSession, visibleProfiles, "mib_${creds.username}")
|
|
||||||
val freshSession = existingFlow.lastSession ?: existingSession
|
|
||||||
app.mibSessions[loginId] = freshSession
|
|
||||||
store.saveMibSession(loginId, freshSession)
|
|
||||||
if (existingFlow.lastProfiles.isNotEmpty()) {
|
|
||||||
app.mibProfilesMap[loginId] = existingFlow.lastProfiles
|
|
||||||
store.saveMibProfiles(loginId, existingFlow.lastProfiles)
|
|
||||||
}
|
|
||||||
AccountCache.save(this@HomeActivity, accounts)
|
|
||||||
return@async accounts
|
|
||||||
} catch (e: java.io.IOException) {
|
|
||||||
refreshErrors.add("NO_INTERNET")
|
|
||||||
return@async AccountCache.load(this@HomeActivity).filter { it.loginTag == "mib_$loginId" }
|
|
||||||
} catch (e: BankServerException) {
|
|
||||||
refreshErrors.add("SERVER:${e.bankName}")
|
|
||||||
return@async AccountCache.load(this@HomeActivity).filter { it.loginTag == "mib_$loginId" }
|
|
||||||
} catch (_: Exception) {
|
|
||||||
// Session completely unrecoverable — fall through to full re-login
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Full re-login: first start with no persisted session, or unrecoverable above
|
|
||||||
try {
|
try {
|
||||||
val flow = MibLoginFlow(CredentialStore(this@HomeActivity))
|
val flow = MibLoginFlow(CredentialStore(this@HomeActivity))
|
||||||
flow.onSessionRefreshed = { newSession, newProfiles ->
|
|
||||||
app.mibSessions[loginId] = newSession
|
|
||||||
app.mibProfilesMap[loginId] = newProfiles
|
|
||||||
store.saveMibSession(loginId, newSession)
|
|
||||||
store.saveMibProfiles(loginId, newProfiles)
|
|
||||||
}
|
|
||||||
val accounts = flow.login(creds.username, creds.passwordHash, creds.otpSeed)
|
val accounts = flow.login(creds.username, creds.passwordHash, creds.otpSeed)
|
||||||
val newSession = flow.lastSession!!
|
val app = application as BasedBankApp
|
||||||
app.mibSessions[loginId] = newSession
|
app.mibSessions[loginId] = flow.lastSession!!
|
||||||
app.mibProfilesMap[loginId] = flow.lastProfiles
|
app.mibProfilesMap[loginId] = flow.lastProfiles
|
||||||
app.mibLoginFlows[loginId] = flow
|
app.mibLoginFlows[loginId] = flow
|
||||||
store.saveMibSession(loginId, newSession)
|
|
||||||
store.saveMibProfiles(loginId, flow.lastProfiles)
|
store.saveMibProfiles(loginId, flow.lastProfiles)
|
||||||
accounts
|
accounts
|
||||||
} catch (e: java.io.IOException) {
|
} catch (e: java.io.IOException) {
|
||||||
|
|||||||
@@ -79,41 +79,9 @@ class CredentialStore(context: Context) {
|
|||||||
.remove("mib_${loginId}_enc_profile")
|
.remove("mib_${loginId}_enc_profile")
|
||||||
.remove("mib_${loginId}_enc_full_name")
|
.remove("mib_${loginId}_enc_full_name")
|
||||||
.remove("mib_${loginId}_hidden_profile_ids")
|
.remove("mib_${loginId}_hidden_profile_ids")
|
||||||
.remove("mib_${loginId}_enc_session")
|
|
||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── MIB active session (xxid + sessionKey + nonceGenerator) ─────────────────
|
|
||||||
|
|
||||||
fun saveMibSession(loginId: String, session: sh.sar.basedbank.api.mib.MibSession) {
|
|
||||||
val json = JSONObject().apply {
|
|
||||||
put("xxid", session.xxid)
|
|
||||||
put("sessionKey", session.sessionKey)
|
|
||||||
put("nonceGenerator", session.nonceGenerator)
|
|
||||||
put("appId", session.appId)
|
|
||||||
}.toString()
|
|
||||||
val key = getOrCreateKey()
|
|
||||||
prefs.edit().putString("mib_${loginId}_enc_session", encrypt(json, key)).apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loadMibSession(loginId: String): sh.sar.basedbank.api.mib.MibSession? {
|
|
||||||
val key = getOrCreateKey()
|
|
||||||
val enc = prefs.getString("mib_${loginId}_enc_session", null) ?: return null
|
|
||||||
return try {
|
|
||||||
val o = JSONObject(decrypt(enc, key))
|
|
||||||
sh.sar.basedbank.api.mib.MibSession(
|
|
||||||
appId = o.getString("appId"),
|
|
||||||
xxid = o.getString("xxid"),
|
|
||||||
nonceGenerator = o.getString("nonceGenerator"),
|
|
||||||
sessionKey = o.getString("sessionKey")
|
|
||||||
)
|
|
||||||
} catch (_: Exception) { null }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clearMibSession(loginId: String) {
|
|
||||||
prefs.edit().remove("mib_${loginId}_enc_session").apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── MIB session keys (key1/key2) and app ID (per loginId) ────────────────
|
// ── MIB session keys (key1/key2) and app ID (per loginId) ────────────────
|
||||||
|
|
||||||
fun saveMibKeys(loginId: String, key1: String, key2: String) {
|
fun saveMibKeys(loginId: String, key1: String, key2: String) {
|
||||||
|
|||||||
Reference in New Issue
Block a user