Description
When using the User-Agent OAuth flow (response_type=hybrid_token), the identity service endpoint (/id/{orgId}/{userId}) returns 403 Bad_OAuth_Token when called with the hybrid token. This causes fetchUserIdentity() in AuthenticationUtilities.kt to fail, which has two critical downstream effects:
-
Same-org multi-user account collision — When fetchUserIdentity() returns null, username is null, so buildAccountName() produces "null (instanceUrl) (appName)" for every user on the same org. addAccountExplicitly() silently overwrites the first user's credentials with the second user's, breaking multi-user support entirely.
-
Missing identity fields — displayName, username, email, and thumbnailUrl are never populated on the UserAccount, causing blank user profiles and null pointer exceptions in account switcher UIs.
Root Cause
In AuthenticationUtilities.kt, fetchUserIdentity() calls callIdentityService() with the hybrid token. The identity service endpoint rejects hybrid tokens with Bad_OAuth_Token, so the call fails and returns null.
The hybrid token works fine for REST API calls (e.g., querying the User sObject), just not for the identity service endpoint.
Steps to Reproduce
- Configure a Connected App with User-Agent flow (
response_type=hybrid_token)
- Log in as User A on org X
- Log in as User B on the same org X
- User B's credentials overwrite User A's account (same
accountName)
- Switching to User A now uses User B's tokens
Expected Behavior
fetchUserIdentity() should have a fallback when the identity service fails — e.g., querying the User sObject (/services/data/vXX.0/sobjects/User/{userId}?fields=Username,Email,Name,SmallPhotoUrl) which works with hybrid tokens.
At minimum, buildAccountName() should use tokenResponse.userId as a fallback when username is null, to prevent account collisions.
Suggested Fix
Replace or supplement callIdentityService() with a User sObject query fallback:
private suspend fun fetchUserIdentity(
tokenResponse: TokenEndpointResponse
): IdServiceResponse? {
// Try identity service first
val identity = runCatching { callIdentityService(...) }.getOrNull()
if (identity != null) return identity
// Fallback: query User sObject (works with hybrid tokens)
return runCatching { fetchUserIdentityViaUserQuery(tokenResponse) }.getOrNull()
}
And in onAuthFlowComplete():
val usernameForAccount = userIdentity?.username ?: tokenResponse.userId
Affected Versions
- Confirmed on 13.2.0 and 13.2.1
- Affects any app using User-Agent flow with
hybrid_token grant type
Environment
- Android SDK 13.2.0/13.2.1
- OAuth User-Agent flow (
hybrid_token grant type)
- Multi-user enabled
Description
When using the User-Agent OAuth flow (
response_type=hybrid_token), the identity service endpoint (/id/{orgId}/{userId}) returns 403 Bad_OAuth_Token when called with the hybrid token. This causesfetchUserIdentity()inAuthenticationUtilities.ktto fail, which has two critical downstream effects:Same-org multi-user account collision — When
fetchUserIdentity()returns null,usernameis null, sobuildAccountName()produces"null (instanceUrl) (appName)"for every user on the same org.addAccountExplicitly()silently overwrites the first user's credentials with the second user's, breaking multi-user support entirely.Missing identity fields —
displayName,username,email, andthumbnailUrlare never populated on theUserAccount, causing blank user profiles and null pointer exceptions in account switcher UIs.Root Cause
In
AuthenticationUtilities.kt,fetchUserIdentity()callscallIdentityService()with the hybrid token. The identity service endpoint rejects hybrid tokens withBad_OAuth_Token, so the call fails and returns null.The hybrid token works fine for REST API calls (e.g., querying the User sObject), just not for the identity service endpoint.
Steps to Reproduce
response_type=hybrid_token)accountName)Expected Behavior
fetchUserIdentity()should have a fallback when the identity service fails — e.g., querying the User sObject (/services/data/vXX.0/sobjects/User/{userId}?fields=Username,Email,Name,SmallPhotoUrl) which works with hybrid tokens.At minimum,
buildAccountName()should usetokenResponse.userIdas a fallback whenusernameis null, to prevent account collisions.Suggested Fix
Replace or supplement
callIdentityService()with a User sObject query fallback:And in
onAuthFlowComplete():Affected Versions
hybrid_tokengrant typeEnvironment
hybrid_tokengrant type)