forked from shihaam/thijooree
fix single profile multi login
This commit is contained in:
@@ -121,15 +121,27 @@ class BmlLoginFlow {
|
|||||||
twoFaResp.close()
|
twoFaResp.close()
|
||||||
if (twoFaResp.code != 302) throw Exception("OTP verification failed — check your OTP seed")
|
if (twoFaResp.code != 302) throw Exception("OTP verification failed — check your OTP seed")
|
||||||
|
|
||||||
// Step 5: GET /web/profile — returns list of profiles for this account
|
// Step 5: GET /web/profile — multi-profile accounts return a 200 with a profile picker;
|
||||||
|
// single-profile accounts skip the picker and redirect straight to /web/redirect with
|
||||||
|
// blaze_identity already set in the response cookies.
|
||||||
val profileResp = client.newCall(
|
val profileResp = client.newCall(
|
||||||
Request.Builder().url("$BASE_URL/web/profile")
|
Request.Builder().url("$BASE_URL/web/profile")
|
||||||
.header("User-Agent", WEB_USER_AGENT).build()
|
.header("User-Agent", WEB_USER_AGENT).build()
|
||||||
).execute()
|
).execute()
|
||||||
|
val profileCode = profileResp.code
|
||||||
|
val profileLocation = profileResp.header("Location") ?: ""
|
||||||
val profileBody = profileResp.body?.string() ?: ""
|
val profileBody = profileResp.body?.string() ?: ""
|
||||||
profileResp.close()
|
profileResp.close()
|
||||||
|
|
||||||
lastProfiles = parseProfiles(profileBody)
|
lastProfiles = if (profileCode == 302) {
|
||||||
|
// Any 302 from GET /web/profile means the server auto-activated the sole profile
|
||||||
|
// and blaze_identity is already set — no profile picker shown.
|
||||||
|
// Use username as a stable temporary profileId (unique per login); it will be
|
||||||
|
// replaced by the real BML customer ID after fetchUserInfo in finishBmlLogin().
|
||||||
|
listOf(BmlProfile(profileId = username, name = "Personal", type = "Profile", profileType = "default", autoActivated = true))
|
||||||
|
} else {
|
||||||
|
parseProfiles(profileBody)
|
||||||
|
}
|
||||||
return lastProfiles
|
return lastProfiles
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,6 +156,13 @@ class BmlLoginFlow {
|
|||||||
* [requestBusinessOtp] + [submitBusinessOtp].
|
* [requestBusinessOtp] + [submitBusinessOtp].
|
||||||
*/
|
*/
|
||||||
fun activateProfile(profile: BmlProfile, loginTag: String): BmlActivationResult {
|
fun activateProfile(profile: BmlProfile, loginTag: String): BmlActivationResult {
|
||||||
|
// Single-profile accounts: server already activated during login() and set blaze_identity.
|
||||||
|
// autoActivated=true is the sentinel for this case — skip the profile GET entirely.
|
||||||
|
if (profile.autoActivated) {
|
||||||
|
val (session, accounts) = doOAuthAndFetchAccounts(loginTag, profile.name, profile.profileId)
|
||||||
|
return BmlActivationResult.Success(session, accounts)
|
||||||
|
}
|
||||||
|
|
||||||
val xsrf = xsrfToken()
|
val xsrf = xsrfToken()
|
||||||
val reqBuilder = Request.Builder()
|
val reqBuilder = Request.Builder()
|
||||||
.url("$BASE_URL/web/profile/${profile.profileId}")
|
.url("$BASE_URL/web/profile/${profile.profileId}")
|
||||||
@@ -156,8 +175,9 @@ class BmlLoginFlow {
|
|||||||
resp.close()
|
resp.close()
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
code == 409 || (code == 302 && "/web/redirect" in location) -> {
|
code == 409 || (code == 302 && "/web/profile/2fa/business" !in location) -> {
|
||||||
// Profile activated — blaze_identity cookie set in response headers
|
// Profile activated — blaze_identity cookie set in response headers.
|
||||||
|
// Any 302 that isn't to the business 2FA page means success.
|
||||||
val (session, accounts) = doOAuthAndFetchAccounts(loginTag, profile.name, profile.profileId)
|
val (session, accounts) = doOAuthAndFetchAccounts(loginTag, profile.name, profile.profileId)
|
||||||
BmlActivationResult.Success(session, accounts)
|
BmlActivationResult.Success(session, accounts)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ data class BmlProfile(
|
|||||||
val profileId: String,
|
val profileId: String,
|
||||||
val name: String,
|
val name: String,
|
||||||
val type: String, // "Profile" (personal) or "Business"
|
val type: String, // "Profile" (personal) or "Business"
|
||||||
val profileType: String // "default" or "business"
|
val profileType: String, // "default" or "business"
|
||||||
|
val autoActivated: Boolean = false // true for single-profile accounts where server skips the picker
|
||||||
)
|
)
|
||||||
|
|
||||||
data class BmlOtpChannel(
|
data class BmlOtpChannel(
|
||||||
|
|||||||
@@ -429,17 +429,46 @@ class CredentialsFragment : Fragment() {
|
|||||||
if (anySession != null) {
|
if (anySession != null) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val info = BmlLoginFlow().fetchUserInfo(anySession)
|
val info = BmlLoginFlow().fetchUserInfo(anySession)
|
||||||
if (info != null) store.saveBmlUserProfile(
|
if (info != null) {
|
||||||
bmlLoginId,
|
store.saveBmlUserProfile(
|
||||||
CredentialStore.BmlUserProfile(
|
bmlLoginId,
|
||||||
fullName = info.fullName,
|
CredentialStore.BmlUserProfile(
|
||||||
email = info.email,
|
fullName = info.fullName,
|
||||||
mobile = info.mobile,
|
email = info.email,
|
||||||
customerId = info.customerId,
|
mobile = info.mobile,
|
||||||
idCard = info.idCard,
|
customerId = info.customerId,
|
||||||
birthdate = info.birthdate
|
idCard = info.idCard,
|
||||||
|
birthdate = info.birthdate
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
// Single-profile accounts used username as a temporary profileId.
|
||||||
|
// Replace it with the real BML customer ID so multi-login doesn't collide.
|
||||||
|
val customerId = info.customerId
|
||||||
|
if (customerId.isNotBlank()) {
|
||||||
|
val profiles = store.loadBmlProfiles(bmlLoginId)
|
||||||
|
val autoProfile = profiles.firstOrNull { it.autoActivated }
|
||||||
|
if (autoProfile != null && autoProfile.profileId != customerId) {
|
||||||
|
val oldId = autoProfile.profileId
|
||||||
|
// Re-key session in memory and storage
|
||||||
|
val session = app.bmlSessions.remove(oldId)
|
||||||
|
if (session != null) {
|
||||||
|
app.bmlSessions[customerId] = session
|
||||||
|
store.clearBmlProfileSession(oldId)
|
||||||
|
store.saveBmlProfileSession(customerId, session.accessToken, session.deviceId)
|
||||||
|
}
|
||||||
|
// Update stored profile list with the real ID
|
||||||
|
val updatedProfiles = profiles.map {
|
||||||
|
if (it.autoActivated) it.copy(profileId = customerId) else it
|
||||||
|
}
|
||||||
|
store.saveBmlProfiles(bmlLoginId, updatedProfiles)
|
||||||
|
app.bmlProfilesMap[bmlLoginId] = updatedProfiles
|
||||||
|
// Update accounts to use real profileId
|
||||||
|
bmlAccumulatedAccounts.replaceAll { acc ->
|
||||||
|
if (acc.profileId == oldId) acc.copy(profileId = customerId) else acc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -217,6 +217,7 @@ class CredentialStore(context: Context) {
|
|||||||
put("name", p.name)
|
put("name", p.name)
|
||||||
put("type", p.type)
|
put("type", p.type)
|
||||||
put("profileType", p.profileType)
|
put("profileType", p.profileType)
|
||||||
|
put("autoActivated", p.autoActivated)
|
||||||
})
|
})
|
||||||
prefs.edit().putString("bml_${loginId}_profiles", arr.toString()).apply()
|
prefs.edit().putString("bml_${loginId}_profiles", arr.toString()).apply()
|
||||||
}
|
}
|
||||||
@@ -228,10 +229,11 @@ class CredentialStore(context: Context) {
|
|||||||
(0 until arr.length()).map { i ->
|
(0 until arr.length()).map { i ->
|
||||||
val o = arr.getJSONObject(i)
|
val o = arr.getJSONObject(i)
|
||||||
BmlProfile(
|
BmlProfile(
|
||||||
profileId = o.optString("profileId"),
|
profileId = o.optString("profileId"),
|
||||||
name = o.optString("name"),
|
name = o.optString("name"),
|
||||||
type = o.optString("type"),
|
type = o.optString("type"),
|
||||||
profileType = o.optString("profileType", "default")
|
profileType = o.optString("profileType", "default"),
|
||||||
|
autoActivated = o.optBoolean("autoActivated", false)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} catch (_: Exception) { emptyList() }
|
} catch (_: Exception) { emptyList() }
|
||||||
|
|||||||
Reference in New Issue
Block a user