Fix Quest Platform SDK auth flow
- Use asyncInitialize with message pump for proper SDK callback delivery - Fall back to oculusId when numeric user ID unavailable (DUC pending) - Gracefully handle missing nonce - Auto-upgrade to full nonce validation when numeric ID becomes available
This commit is contained in:
@@ -14,7 +14,6 @@ import com.meta.horizon.platform.ovr.models.UserProof
|
||||
import com.meta.horizon.platform.ovr.requests.Request
|
||||
import com.meta.horizon.platform.ovr.requests.Users
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
@@ -43,35 +42,43 @@ class LoginViewModel @Inject constructor(
|
||||
_isLoading.value = true
|
||||
_error.value = null
|
||||
try {
|
||||
// Initialize Platform SDK
|
||||
// Initialize Platform SDK (async, wait for completion via message pump)
|
||||
if (!Core.isInitialized()) {
|
||||
Log.d(TAG, "Initializing Platform SDK with appId=$QUEST_APP_ID")
|
||||
val initResult = Core.initialize(QUEST_APP_ID, activity.applicationContext)
|
||||
Log.d(TAG, "Core.initialize returned: $initResult")
|
||||
Log.d(TAG, "Async initializing Platform SDK with appId=$QUEST_APP_ID")
|
||||
val initResult = awaitWithPump { Core.asyncInitialize(QUEST_APP_ID, activity.applicationContext) }
|
||||
Log.d(TAG, "Platform SDK initialized: $initResult")
|
||||
} else {
|
||||
Log.d(TAG, "Platform SDK already initialized")
|
||||
}
|
||||
Log.d(TAG, "Core.isInitialized=${Core.isInitialized()}")
|
||||
|
||||
// Check cached user ID first (synchronous)
|
||||
val cachedUserId = Core.getLoggedInUserID()
|
||||
Log.d(TAG, "Core.getLoggedInUserID() = $cachedUserId")
|
||||
// Get logged-in user (async with message pump)
|
||||
Log.d(TAG, "Requesting logged-in user...")
|
||||
val user = awaitWithPump { Users.getLoggedInUser() }
|
||||
val numericId = user.getID().toString()
|
||||
val oculusId = user.oculusID
|
||||
Log.d(TAG, "User: id=$numericId displayName=${user.displayName} oculusId=$oculusId")
|
||||
|
||||
if (cachedUserId == 0L) {
|
||||
throw Exception("Platform SDK returned user ID 0. " +
|
||||
"Make sure the app is installed from the Horizon store (not sideloaded) " +
|
||||
// Use numeric ID if available (requires Data Use Checkup), fall back to oculusId
|
||||
val userId = if (user.getID() != 0L) numericId else oculusId
|
||||
if (userId.isNullOrEmpty()) {
|
||||
throw Exception("Platform SDK returned no user identifier. " +
|
||||
"Make sure the app is installed from the Horizon store " +
|
||||
"and your account is a test user for this app.")
|
||||
}
|
||||
|
||||
// Get full user via async call with manual message pump
|
||||
val user = awaitWithPump { Users.getLoggedInUser() }
|
||||
val userId = user.getID().toString()
|
||||
Log.d(TAG, "User: id=$userId displayName=${user.displayName}")
|
||||
|
||||
// Get user proof (nonce)
|
||||
val proof = awaitWithPump { Users.getUserProof() }
|
||||
val nonce = proof.value
|
||||
Log.d(TAG, "UserProof nonce=$nonce")
|
||||
// Get user proof (nonce) — may fail without DUC approval
|
||||
var nonce = ""
|
||||
try {
|
||||
Log.d(TAG, "Requesting user proof...")
|
||||
val proof = awaitWithPump { Users.getUserProof() }
|
||||
nonce = proof.value
|
||||
Log.d(TAG, "UserProof nonce length=${nonce.length}")
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "getUserProof failed (DUC pending?), proceeding without nonce", e)
|
||||
}
|
||||
|
||||
// Send to backend for verification
|
||||
Log.d(TAG, "Sending to backend: userId=$userId hasNonce=${nonce.isNotEmpty()}")
|
||||
val response = apiService.metaCallback(
|
||||
MetaCallbackRequest(
|
||||
userId = userId,
|
||||
@@ -82,6 +89,7 @@ class LoginViewModel @Inject constructor(
|
||||
|
||||
// Save session tokens
|
||||
tokenStore.saveSession(response.accessToken, response.refreshToken)
|
||||
Log.d(TAG, "Login successful!")
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Quest login failed", e)
|
||||
_error.value = e.message ?: "Login failed"
|
||||
@@ -97,7 +105,7 @@ class LoginViewModel @Inject constructor(
|
||||
|
||||
/**
|
||||
* Awaits a Platform SDK request by manually pumping the message queue.
|
||||
* The SDK requires Request.runCallbacks() to be called for callbacks to fire.
|
||||
* The SDK delivers responses via Core.popSDKMessage() which must be polled.
|
||||
*/
|
||||
private suspend fun <T> awaitWithPump(
|
||||
block: () -> Request<T>,
|
||||
@@ -105,25 +113,30 @@ class LoginViewModel @Inject constructor(
|
||||
var completed = false
|
||||
block()
|
||||
.onSuccess { result: T ->
|
||||
Log.d(TAG, "SDK request succeeded: ${result?.javaClass?.simpleName}")
|
||||
if (!completed) {
|
||||
completed = true
|
||||
cont.resume(result)
|
||||
}
|
||||
}
|
||||
.onError { error ->
|
||||
Log.e(TAG, "SDK request failed: code=${error.code} http=${error.httpCode} msg=${error.message}")
|
||||
if (!completed) {
|
||||
completed = true
|
||||
cont.resumeWithException(Exception(error.message))
|
||||
cont.resumeWithException(Exception("SDK error ${error.code}: ${error.message}"))
|
||||
}
|
||||
}
|
||||
|
||||
// Pump messages on a background thread
|
||||
Thread {
|
||||
val timeout = System.currentTimeMillis() + 10_000 // 10s timeout
|
||||
val timeout = System.currentTimeMillis() + 15_000 // 15s timeout
|
||||
var msgCount = 0
|
||||
while (!completed && System.currentTimeMillis() < timeout) {
|
||||
try {
|
||||
val msg = Core.popSDKMessage()
|
||||
if (msg != null) {
|
||||
msgCount++
|
||||
Log.d(TAG, "Pumped message #$msgCount type=${msg.type} isError=${msg.isError}")
|
||||
Request.handleMessage(msg)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
@@ -131,9 +144,10 @@ class LoginViewModel @Inject constructor(
|
||||
}
|
||||
Thread.sleep(50)
|
||||
}
|
||||
Log.d(TAG, "Message pump done: completed=$completed msgs=$msgCount")
|
||||
if (!completed) {
|
||||
completed = true
|
||||
cont.resumeWithException(Exception("Platform SDK request timed out"))
|
||||
cont.resumeWithException(Exception("Platform SDK request timed out after 15s (pumped $msgCount messages)"))
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user