Skip to content

Commit

Permalink
Fix Device Metadata migration used for Device Tracking if an aliased …
Browse files Browse the repository at this point in the history
…userId was used.
  • Loading branch information
tylerjroach committed Dec 11, 2024
1 parent 83605cb commit 293b87b
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,26 @@ internal object CredentialStoreCognitoActions : CredentialStoreActions {
legacyCredentialStore.deleteCredential()
}

// migrate device data
val lastAuthUserId = legacyCredentialStore.retrieveLastAuthUserId()
lastAuthUserId?.let {
val deviceMetaData = legacyCredentialStore.retrieveDeviceMetadata(lastAuthUserId)
/*
Migrate Device Metadata
1. We first need to get the list of userIds that contain device metadata on the device.
2. For each userId, we check to see if the current credential store has device metadata for that user.
3. If the current user does not have device metadata in the current store, migrate from legacy.
This is a possibility because of a bug where we were previously attempting to migrate using an aliased
userId lookup. This situation would happen if a user migrated, signed out, then signed back in. Upon
signing back in, they would be granted new device metadata. Since that new metadata is what is
associated with the refresh token, we do not want to overwrite it.
4. If the current user has device metadata in the current credential store, do not migrate from legacy.
5. Upon migration completion, we delete the legacy device metadata.
*/
legacyCredentialStore.retrieveDeviceMetadataUserIdList().forEach { userId ->
val deviceMetaData = legacyCredentialStore.retrieveDeviceMetadata(userId)
if (deviceMetaData != DeviceMetadata.Empty) {
credentialStore.saveDeviceMetadata(lastAuthUserId, deviceMetaData)
legacyCredentialStore.deleteDeviceKeyCredential(lastAuthUserId)
credentialStore.retrieveDeviceMetadata(userId)
if (credentialStore.retrieveDeviceMetadata(userId) == DeviceMetadata.Empty) {
credentialStore.saveDeviceMetadata(userId, deviceMetaData)
}
legacyCredentialStore.deleteDeviceKeyCredential(userId)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import com.amplifyframework.statemachine.codegen.data.DeviceMetadata
import com.amplifyframework.statemachine.codegen.data.FederatedToken
import com.amplifyframework.statemachine.codegen.data.SignInMethod
import com.amplifyframework.statemachine.codegen.data.SignedInData
import java.io.File
import java.time.Instant
import java.time.temporal.ChronoUnit
import java.util.Date
Expand Down Expand Up @@ -74,6 +75,7 @@ internal class AWSCognitoLegacyCredentialStore(
const val TOKEN_KEY = "token"
}

private val userDeviceDetailsCacheKeyPrefix = "$APP_DEVICE_INFO_CACHE.${authConfiguration.userPool?.poolId}."
private val userDeviceDetailsCacheKey = "$APP_DEVICE_INFO_CACHE.${authConfiguration.userPool?.poolId}.%s"

private val idAndCredentialsKeyValue: KeyValueRepository by lazy {
Expand Down Expand Up @@ -229,6 +231,22 @@ internal class AWSCognitoLegacyCredentialStore(
)
}

/*
During migration away from the legacy credential store, we need to find all shared preference files that store
device metadata. These filenames contain the real userId (not aliased) for the tracked device metadata.
*/
fun retrieveDeviceMetadataUserIdList(): List<String> {
return try {
val sharedPrefsSuffix = ".xml"
File(context.dataDir, "shared_prefs").listFiles { _, filename ->
filename.startsWith(userDeviceDetailsCacheKeyPrefix) && filename.endsWith(sharedPrefsSuffix)
}?.map { it.name.substringAfter(userDeviceDetailsCacheKeyPrefix).substringBefore(sharedPrefsSuffix) }
?.filter { it.isNotBlank() } ?: emptyList()
} catch (e: Exception) {
return emptyList()
}
}

@Synchronized
override fun retrieveDeviceMetadata(username: String): DeviceMetadata {
val deviceDetailsCacheKey = String.format(userDeviceDetailsCacheKey, username)
Expand Down

0 comments on commit 293b87b

Please sign in to comment.