Skip to content

Commit 223fc43

Browse files
authored
fix: Ensure cookies authentication prior to webview loading (#312)
Ensure cookies' expiry time is verified before loading a webview. Additionally, addressed a race condition within the `clearWebViewCookie` method. This race condition caused the premature reset of `authSessionCookieExpiration` to -1 due to delays in the callback execution. Fixes: LEARNER-9891
1 parent 4d782ee commit 223fc43

File tree

5 files changed

+65
-30
lines changed

5 files changed

+65
-30
lines changed

core/src/main/java/org/openedx/core/extension/ViewExt.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@ import android.graphics.Rect
66
import android.util.DisplayMetrics
77
import android.view.View
88
import android.view.ViewGroup
9+
import android.webkit.WebView
910
import android.widget.Toast
1011
import androidx.fragment.app.DialogFragment
12+
import kotlinx.coroutines.CoroutineScope
13+
import kotlinx.coroutines.launch
14+
import org.openedx.core.system.AppCookieManager
1115

1216
fun Context.dpToPixel(dp: Int): Float {
1317
return dp * (resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
@@ -46,3 +50,14 @@ fun DialogFragment.setWidthPercent(percentage: Int) {
4650
fun Context.toastMessage(message: String) {
4751
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
4852
}
53+
54+
fun WebView.loadUrl(url: String, scope: CoroutineScope, cookieManager: AppCookieManager) {
55+
if (cookieManager.isSessionCookieMissingOrExpired()) {
56+
scope.launch {
57+
cookieManager.tryToRefreshSessionCookie()
58+
loadUrl(url)
59+
}
60+
} else {
61+
loadUrl(url)
62+
}
63+
}

core/src/main/java/org/openedx/core/system/AppCookieManager.kt

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import java.util.concurrent.TimeUnit
1111
class AppCookieManager(private val config: Config, private val api: CookiesApi) {
1212

1313
companion object {
14-
private const val REV_934_COOKIE =
15-
"REV_934=mobile; expires=Tue, 31 Dec 2021 12:00:20 GMT; domain=.edx.org;"
1614
private val FRESHNESS_INTERVAL = TimeUnit.HOURS.toMillis(1)
1715
}
1816

@@ -34,19 +32,11 @@ class AppCookieManager(private val config: Config, private val api: CookiesApi)
3432
}
3533

3634
fun clearWebViewCookie() {
37-
CookieManager.getInstance().removeAllCookies { result ->
38-
if (result) {
39-
authSessionCookieExpiration = -1
40-
}
41-
}
35+
CookieManager.getInstance().removeAllCookies(null)
36+
authSessionCookieExpiration = -1
4237
}
4338

4439
fun isSessionCookieMissingOrExpired(): Boolean {
4540
return authSessionCookieExpiration < System.currentTimeMillis()
4641
}
47-
48-
fun setMobileCookie() {
49-
CookieManager.getInstance().setCookie(config.getApiHostURL(), REV_934_COOKIE)
50-
}
51-
5242
}

course/src/main/java/org/openedx/course/presentation/unit/html/HtmlUnitFragment.kt

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,30 @@ import android.os.Bundle
99
import android.util.Log
1010
import android.view.LayoutInflater
1111
import android.view.ViewGroup
12-
import android.webkit.*
12+
import android.webkit.JavascriptInterface
13+
import android.webkit.WebResourceRequest
14+
import android.webkit.WebResourceResponse
15+
import android.webkit.WebView
16+
import android.webkit.WebViewClient
1317
import androidx.compose.foundation.background
1418
import androidx.compose.foundation.isSystemInDarkTheme
15-
import androidx.compose.foundation.layout.*
19+
import androidx.compose.foundation.layout.Box
20+
import androidx.compose.foundation.layout.fillMaxHeight
21+
import androidx.compose.foundation.layout.fillMaxSize
22+
import androidx.compose.foundation.layout.fillMaxWidth
23+
import androidx.compose.foundation.layout.padding
24+
import androidx.compose.foundation.layout.widthIn
1625
import androidx.compose.foundation.shape.RoundedCornerShape
17-
import androidx.compose.material.*
18-
import androidx.compose.runtime.*
26+
import androidx.compose.material.CircularProgressIndicator
27+
import androidx.compose.material.MaterialTheme
28+
import androidx.compose.material.Surface
29+
import androidx.compose.runtime.Composable
30+
import androidx.compose.runtime.collectAsState
31+
import androidx.compose.runtime.getValue
32+
import androidx.compose.runtime.mutableStateOf
33+
import androidx.compose.runtime.remember
34+
import androidx.compose.runtime.rememberCoroutineScope
35+
import androidx.compose.runtime.setValue
1936
import androidx.compose.ui.Alignment
2037
import androidx.compose.ui.Modifier
2138
import androidx.compose.ui.draw.clip
@@ -33,10 +50,15 @@ import androidx.fragment.app.Fragment
3350
import kotlinx.coroutines.launch
3451
import org.koin.androidx.viewmodel.ext.android.viewModel
3552
import org.openedx.core.extension.isEmailValid
53+
import org.openedx.core.extension.loadUrl
3654
import org.openedx.core.system.AppCookieManager
37-
import org.openedx.core.ui.*
55+
import org.openedx.core.ui.ConnectionErrorView
56+
import org.openedx.core.ui.WindowSize
57+
import org.openedx.core.ui.rememberWindowSize
58+
import org.openedx.core.ui.roundBorderWithoutBottom
3859
import org.openedx.core.ui.theme.OpenEdXTheme
3960
import org.openedx.core.ui.theme.appColors
61+
import org.openedx.core.ui.windowSizeValue
4062
import org.openedx.core.utils.EmailUtil
4163

4264
class HtmlUnitFragment : Fragment() {
@@ -268,13 +290,15 @@ private fun HTMLContentView(
268290
}
269291
isVerticalScrollBarEnabled = false
270292
isHorizontalScrollBarEnabled = false
271-
loadUrl(url)
293+
294+
loadUrl(url, coroutineScope, cookieManager)
272295
}
273296
},
274297
update = { webView ->
275298
if (!isLoading && injectJSList.isNotEmpty()) {
276299
injectJSList.forEach { webView.evaluateJavascript(it, null) }
277300
}
278-
})
301+
}
302+
)
279303
}
280304

discovery/src/main/java/org/openedx/discovery/presentation/program/ProgramFragment.kt

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import androidx.compose.runtime.collectAsState
2323
import androidx.compose.runtime.getValue
2424
import androidx.compose.runtime.mutableStateOf
2525
import androidx.compose.runtime.remember
26+
import androidx.compose.runtime.rememberCoroutineScope
2627
import androidx.compose.runtime.setValue
2728
import androidx.compose.ui.Alignment
2829
import androidx.compose.ui.ExperimentalComposeUiApi
@@ -41,10 +42,14 @@ import androidx.compose.ui.viewinterop.AndroidView
4142
import androidx.compose.ui.zIndex
4243
import androidx.core.os.bundleOf
4344
import androidx.fragment.app.Fragment
45+
import kotlinx.coroutines.launch
46+
import org.koin.androidx.compose.koinViewModel
4447
import org.koin.androidx.viewmodel.ext.android.viewModel
48+
import org.openedx.core.extension.loadUrl
4549
import org.openedx.core.extension.toastMessage
4650
import org.openedx.core.presentation.dialog.alert.ActionDialogFragment
4751
import org.openedx.core.presentation.dialog.alert.InfoDialogFragment
52+
import org.openedx.core.system.AppCookieManager
4853
import org.openedx.core.ui.ConnectionErrorView
4954
import org.openedx.core.ui.HandleUIMessage
5055
import org.openedx.core.ui.Toolbar
@@ -119,6 +124,7 @@ class ProgramFragment(private val myPrograms: Boolean = false) : Fragment() {
119124
windowSize = windowSize,
120125
uiState = uiState,
121126
contentUrl = getInitialUrl(),
127+
cookieManager = viewModel.cookieManager,
122128
canShowBackBtn = arguments?.getString(ARG_PATH_ID, "")
123129
?.isNotEmpty() == true,
124130
uriScheme = viewModel.uriScheme,
@@ -182,15 +188,11 @@ class ProgramFragment(private val myPrograms: Boolean = false) : Fragment() {
182188
onSettingsClick = {
183189
viewModel.navigateToSettings(requireActivity().supportFragmentManager)
184190
},
185-
refreshSessionCookie = {
186-
viewModel.refreshCookie()
187-
},
188191
)
189192
}
190193
}
191194
}
192195

193-
194196
private fun getInitialUrl(): String {
195197
return arguments?.let { args ->
196198
val pathId = args.getString(ARG_PATH_ID) ?: ""
@@ -219,6 +221,7 @@ private fun ProgramInfoScreen(
219221
windowSize: WindowSize,
220222
uiState: ProgramUIState?,
221223
contentUrl: String,
224+
cookieManager: AppCookieManager,
222225
uriScheme: String,
223226
canShowBackBtn: Boolean,
224227
hasInternetConnection: Boolean,
@@ -227,10 +230,10 @@ private fun ProgramInfoScreen(
227230
onSettingsClick: () -> Unit,
228231
onBackClick: () -> Unit,
229232
onUriClick: (String, WebViewLink.Authority) -> Unit,
230-
refreshSessionCookie: () -> Unit = {},
231233
) {
232234
val scaffoldState = rememberScaffoldState()
233235
val configuration = LocalConfiguration.current
236+
val coroutineScope = rememberCoroutineScope()
234237
val isLoading = uiState is ProgramUIState.Loading
235238

236239
when (uiState) {
@@ -290,7 +293,11 @@ private fun ProgramInfoScreen(
290293
uriScheme = uriScheme,
291294
isAllLinksExternal = true,
292295
onWebPageLoaded = onWebPageLoaded,
293-
refreshSessionCookie = refreshSessionCookie,
296+
refreshSessionCookie = {
297+
coroutineScope.launch {
298+
cookieManager.tryToRefreshSessionCookie()
299+
}
300+
},
294301
onUriClick = onUriClick,
295302
)
296303

@@ -301,7 +308,7 @@ private fun ProgramInfoScreen(
301308
webView
302309
},
303310
update = {
304-
webView.loadUrl(contentUrl)
311+
webView.loadUrl(contentUrl, coroutineScope, cookieManager)
305312
}
306313
)
307314
} else {
@@ -339,6 +346,7 @@ fun MyProgramsPreview() {
339346
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
340347
uiState = ProgramUIState.Loading,
341348
contentUrl = "https://www.example.com/",
349+
cookieManager = koinViewModel<ProgramViewModel>().cookieManager,
342350
uriScheme = "",
343351
canShowBackBtn = false,
344352
hasInternetConnection = false,

discovery/src/main/java/org/openedx/discovery/presentation/program/ProgramViewModel.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ class ProgramViewModel(
3434

3535
val programConfig get() = config.getProgramConfig().webViewConfig
3636

37+
val cookieManager get() = edxCookieManager
38+
3739
val hasInternetConnection: Boolean get() = networkConnection.isOnline()
3840

3941
private val _uiState = MutableSharedFlow<ProgramUIState>(
@@ -104,8 +106,4 @@ class ProgramViewModel(
104106
fun navigateToSettings(fragmentManager: FragmentManager) {
105107
router.navigateToSettings(fragmentManager)
106108
}
107-
108-
fun refreshCookie() {
109-
viewModelScope.launch { edxCookieManager.tryToRefreshSessionCookie() }
110-
}
111109
}

0 commit comments

Comments
 (0)