fix single profile multi login
All checks were successful
Auto Tag on Version Change / check-version (push) Successful in 3s
All checks were successful
Auto Tag on Version Change / check-version (push) Successful in 3s
This commit is contained in:
@@ -121,15 +121,27 @@ class BmlLoginFlow {
|
||||
twoFaResp.close()
|
||||
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(
|
||||
Request.Builder().url("$BASE_URL/web/profile")
|
||||
.header("User-Agent", WEB_USER_AGENT).build()
|
||||
).execute()
|
||||
val profileCode = profileResp.code
|
||||
val profileLocation = profileResp.header("Location") ?: ""
|
||||
val profileBody = profileResp.body?.string() ?: ""
|
||||
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
|
||||
}
|
||||
|
||||
@@ -144,6 +156,13 @@ class BmlLoginFlow {
|
||||
* [requestBusinessOtp] + [submitBusinessOtp].
|
||||
*/
|
||||
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 reqBuilder = Request.Builder()
|
||||
.url("$BASE_URL/web/profile/${profile.profileId}")
|
||||
@@ -156,8 +175,9 @@ class BmlLoginFlow {
|
||||
resp.close()
|
||||
|
||||
return when {
|
||||
code == 409 || (code == 302 && "/web/redirect" in location) -> {
|
||||
// Profile activated — blaze_identity cookie set in response headers
|
||||
code == 409 || (code == 302 && "/web/profile/2fa/business" !in location) -> {
|
||||
// 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)
|
||||
BmlActivationResult.Success(session, accounts)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ data class BmlProfile(
|
||||
val profileId: String,
|
||||
val name: String,
|
||||
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(
|
||||
|
||||
@@ -429,17 +429,46 @@ class CredentialsFragment : Fragment() {
|
||||
if (anySession != null) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val info = BmlLoginFlow().fetchUserInfo(anySession)
|
||||
if (info != null) store.saveBmlUserProfile(
|
||||
bmlLoginId,
|
||||
CredentialStore.BmlUserProfile(
|
||||
fullName = info.fullName,
|
||||
email = info.email,
|
||||
mobile = info.mobile,
|
||||
customerId = info.customerId,
|
||||
idCard = info.idCard,
|
||||
birthdate = info.birthdate
|
||||
if (info != null) {
|
||||
store.saveBmlUserProfile(
|
||||
bmlLoginId,
|
||||
CredentialStore.BmlUserProfile(
|
||||
fullName = info.fullName,
|
||||
email = info.email,
|
||||
mobile = info.mobile,
|
||||
customerId = info.customerId,
|
||||
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("type", p.type)
|
||||
put("profileType", p.profileType)
|
||||
put("autoActivated", p.autoActivated)
|
||||
})
|
||||
prefs.edit().putString("bml_${loginId}_profiles", arr.toString()).apply()
|
||||
}
|
||||
@@ -228,10 +229,11 @@ class CredentialStore(context: Context) {
|
||||
(0 until arr.length()).map { i ->
|
||||
val o = arr.getJSONObject(i)
|
||||
BmlProfile(
|
||||
profileId = o.optString("profileId"),
|
||||
name = o.optString("name"),
|
||||
type = o.optString("type"),
|
||||
profileType = o.optString("profileType", "default")
|
||||
profileId = o.optString("profileId"),
|
||||
name = o.optString("name"),
|
||||
type = o.optString("type"),
|
||||
profileType = o.optString("profileType", "default"),
|
||||
autoActivated = o.optBoolean("autoActivated", false)
|
||||
)
|
||||
}
|
||||
} catch (_: Exception) { emptyList() }
|
||||
|
||||
Reference in New Issue
Block a user