make pwa token validation more stable
This commit is contained in:
+1
@@ -14,6 +14,7 @@ object PwaConstants {
|
||||
const val EXTRA_PWA_PROFILE_UUID = "pwa_profile_uuid"
|
||||
const val EXTRA_PWA_CONTEXT_ID = "pwa_context_id"
|
||||
const val EXTRA_PWA_TOKEN = "pwa_token"
|
||||
const val EXTRA_PWA_INSTALL_START_URL = "pwa_install_start_url"
|
||||
|
||||
// Profile and file paths
|
||||
const val CURRENT_PROFILE_FILE = "weblibre_profiles/current_profile"
|
||||
|
||||
+48
-6
@@ -10,6 +10,8 @@ import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ShortcutManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import eu.weblibre.flutter_mozilla_components.Components
|
||||
@@ -147,7 +149,8 @@ class IntentReceiverActivity : Activity() {
|
||||
}
|
||||
|
||||
private fun isTrustedPwaLaunch(intent: Intent, profileUuid: String, token: String?): Boolean {
|
||||
val url = intent.dataString ?: return false
|
||||
val intentUrl = intent.dataString ?: return false
|
||||
val installStartUrl = intent.getStringExtra(PwaConstants.EXTRA_PWA_INSTALL_START_URL)
|
||||
val action = intent.action
|
||||
val hasTrustedAction = action == Intent.ACTION_VIEW || action == "mozilla.components.feature.pwa.VIEW_PWA"
|
||||
if (!hasTrustedAction || token.isNullOrEmpty()) {
|
||||
@@ -158,14 +161,53 @@ class IntentReceiverActivity : Activity() {
|
||||
PwaConstants.PROFILE_MAPPING_PREFS,
|
||||
Context.MODE_PRIVATE,
|
||||
)
|
||||
val tokenKey = "${PwaConstants.PROFILE_MAPPING_TOKEN_PREFIX}${url}::${profileUuid}"
|
||||
val storedToken = prefs.getString(tokenKey, null)
|
||||
if (storedToken == null || storedToken != token) {
|
||||
Log.w(TAG, "PWA token mismatch for $url")
|
||||
val tokenKey = "${PwaConstants.PROFILE_MAPPING_TOKEN_PREFIX}${intentUrl}::${profileUuid}"
|
||||
if (prefs.getString(tokenKey, null) == token) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (!installStartUrl.isNullOrEmpty() && installStartUrl != intentUrl) {
|
||||
val installTokenKey = "${PwaConstants.PROFILE_MAPPING_TOKEN_PREFIX}${installStartUrl}::${profileUuid}"
|
||||
if (prefs.getString(installTokenKey, null) == token) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (isPinnedShortcutTokenMatch(intentUrl, profileUuid, token, installStartUrl)) {
|
||||
return true
|
||||
}
|
||||
|
||||
Log.w(TAG, "PWA token mismatch for $intentUrl")
|
||||
return false
|
||||
}
|
||||
|
||||
private fun isPinnedShortcutTokenMatch(
|
||||
intentUrl: String,
|
||||
profileUuid: String,
|
||||
token: String,
|
||||
installStartUrl: String?,
|
||||
): Boolean {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
val shortcutManager = getSystemService(ShortcutManager::class.java) ?: return false
|
||||
return shortcutManager.pinnedShortcuts.any { shortcut ->
|
||||
val shortcutIntent = shortcut.intent ?: return@any false
|
||||
if (shortcutIntent.getStringExtra(PwaConstants.EXTRA_PWA_PROFILE_UUID) != profileUuid) {
|
||||
return@any false
|
||||
}
|
||||
|
||||
if (shortcutIntent.getStringExtra(PwaConstants.EXTRA_PWA_TOKEN) != token) {
|
||||
return@any false
|
||||
}
|
||||
|
||||
val shortcutUrl = shortcutIntent.dataString
|
||||
val shortcutInstallUrl = shortcutIntent.getStringExtra(PwaConstants.EXTRA_PWA_INSTALL_START_URL)
|
||||
shortcutUrl == intentUrl ||
|
||||
(shortcutInstallUrl != null && shortcutInstallUrl == intentUrl) ||
|
||||
(!installStartUrl.isNullOrEmpty() && shortcutInstallUrl == installStartUrl && (shortcutUrl == intentUrl || shortcutUrl == installStartUrl))
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveSessionIdFromStore(
|
||||
|
||||
+53
-8
@@ -145,7 +145,12 @@ class GeckoPwaApiImpl(
|
||||
val (iconBitmap, isMaskable) = loadPwaIcon(manifest)
|
||||
|
||||
val shortcutId = generateShortcutId(manifest.startUrl)
|
||||
val launchToken = generateAndStoreLaunchToken(manifest.startUrl, profileUuid)
|
||||
val launchToken = resolveLaunchToken(
|
||||
shortcutManager = shortcutManager,
|
||||
shortcutId = shortcutId,
|
||||
startUrl = manifest.startUrl,
|
||||
profileUuid = profileUuid,
|
||||
)
|
||||
|
||||
val appName = manifest.shortName ?: manifest.name ?: "Web App"
|
||||
|
||||
@@ -155,6 +160,7 @@ class GeckoPwaApiImpl(
|
||||
putExtra(PwaConstants.EXTRA_PWA_PROFILE_UUID, profileUuid)
|
||||
putExtra(PwaConstants.EXTRA_PWA_CONTEXT_ID, contextId)
|
||||
putExtra(PwaConstants.EXTRA_PWA_TOKEN, launchToken)
|
||||
putExtra(PwaConstants.EXTRA_PWA_INSTALL_START_URL, manifest.startUrl)
|
||||
}
|
||||
|
||||
val shortcut = ShortcutInfo.Builder(context, shortcutId).apply {
|
||||
@@ -261,16 +267,55 @@ class GeckoPwaApiImpl(
|
||||
.apply()
|
||||
}
|
||||
|
||||
private fun generateAndStoreLaunchToken(startUrl: String, profileUuid: String): String {
|
||||
val token = UUID.randomUUID().toString()
|
||||
val tokenKey = "${PwaConstants.PROFILE_MAPPING_TOKEN_PREFIX}${startUrl}::${profileUuid}"
|
||||
val committed = appPrefs.edit()
|
||||
.putString(tokenKey, token)
|
||||
.commit()
|
||||
private fun resolveLaunchToken(
|
||||
shortcutManager: ShortcutManager,
|
||||
shortcutId: String,
|
||||
startUrl: String,
|
||||
profileUuid: String,
|
||||
): String {
|
||||
val storedToken = getStoredLaunchToken(startUrl, profileUuid)
|
||||
val existingShortcutToken = shortcutManager.pinnedShortcuts
|
||||
.firstOrNull { shortcut -> shortcut.id == shortcutId }
|
||||
?.intent
|
||||
?.takeIf { shortcutIntent ->
|
||||
shortcutIntent.getStringExtra(PwaConstants.EXTRA_PWA_PROFILE_UUID) == profileUuid
|
||||
}
|
||||
?.getStringExtra(PwaConstants.EXTRA_PWA_TOKEN)
|
||||
|
||||
if (!existingShortcutToken.isNullOrEmpty()) {
|
||||
val committed = storeLaunchToken(startUrl, profileUuid, existingShortcutToken)
|
||||
if (!committed) {
|
||||
logger.warn("Failed to persist pinned shortcut PWA token for $startUrl")
|
||||
}
|
||||
return existingShortcutToken
|
||||
}
|
||||
|
||||
if (!storedToken.isNullOrEmpty()) {
|
||||
val committed = storeLaunchToken(startUrl, profileUuid, storedToken)
|
||||
if (!committed) {
|
||||
logger.warn("Failed to refresh stored PWA launch token index for $startUrl")
|
||||
}
|
||||
return storedToken
|
||||
}
|
||||
|
||||
val generatedToken = UUID.randomUUID().toString()
|
||||
val committed = storeLaunchToken(startUrl, profileUuid, generatedToken)
|
||||
if (!committed) {
|
||||
logger.warn("Failed to persist PWA launch token for $startUrl")
|
||||
}
|
||||
return token
|
||||
return generatedToken
|
||||
}
|
||||
|
||||
private fun getStoredLaunchToken(startUrl: String, profileUuid: String): String? {
|
||||
val tokenKey = "${PwaConstants.PROFILE_MAPPING_TOKEN_PREFIX}${startUrl}::${profileUuid}"
|
||||
return appPrefs.getString(tokenKey, null)
|
||||
}
|
||||
|
||||
private fun storeLaunchToken(startUrl: String, profileUuid: String, token: String): Boolean {
|
||||
val tokenKey = "${PwaConstants.PROFILE_MAPPING_TOKEN_PREFIX}${startUrl}::${profileUuid}"
|
||||
return appPrefs.edit()
|
||||
.putString(tokenKey, token)
|
||||
.commit()
|
||||
}
|
||||
|
||||
private fun getProfileMapping(startUrl: String): String? {
|
||||
|
||||
Reference in New Issue
Block a user